From 831f1a603662e864fef5d8b51719b466e0feddd5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 8 Sep 2014 15:14:34 +0200 Subject: [PATCH 001/835] Adding clearer documentation that explains URL encoding for Object Storage --- docs/userguide/ObjectStore/README.md | 4 +++- docs/userguide/ObjectStore/Storage/Container.md | 1 + docs/userguide/ObjectStore/Storage/Object.md | 4 +++- docs/userguide/ObjectStore/USERGUIDE.md | 13 +++++++++---- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index 7da7fd24a..ff395563a 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -46,6 +46,8 @@ In the example above, you are connecting to the ``DFW`` region of the cloud. Any $container = $objectStoreService->createContainer('logos'); ``` +> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in + ### 4. Upload an object to the container. ```php @@ -59,5 +61,5 @@ $container->uploadObject($remoteFileName, $fileData); ## Next steps -There is a lot more you can do with containers and objects. See +There is a lot more you can do with containers and objects. See the [complete user guide to the Object Store service](USERGUIDE.md). diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index d30cd0796..1c01a6156 100755 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -26,6 +26,7 @@ If the response returned is `FALSE`, there was an API error - most likely due to Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. +> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in ## List containers diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index b5f324c9f..f91238c9c 100755 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -22,9 +22,11 @@ setter methods: There are three ways to upload a new file, each of which has different business needs. -__N.B__: Unlike previous versions, you do not need to manually specify your object's content type. The API will do this +> Note: Unlike previous versions, you do not need to manually specify your object's content type. The API will do this for you. +> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in + ### To upload a single/basic file: ```php diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 0cfab2680..593a59ef9 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -28,7 +28,7 @@ To use the object store service, you must first instantiate a `OpenStack` or `Ra 'apiKey' => '' )); ``` - + ### Object Store Service All operations on the object store are done via an object store service object. @@ -46,7 +46,10 @@ For example, you may create a container called `logos` to hold all the image fil A container may contain zero or more objects in it. +> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in + ### Create Container + ```php $container = $objectStoreService->createContainer('logos'); ``` @@ -94,7 +97,7 @@ $containerMetadata = $container->getMetadata(); [ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-metadata.php) ] ### Delete Container -When you no longer have a need for the container, you can remove it. +When you no longer have a need for the container, you can remove it. If the container is empty (that is, it has no objects in it), you can remove it as shown below: @@ -184,6 +187,8 @@ An **object** (sometimes referred to as a file) is the unit of storage in an Obj For example, you may upload an object named `php-elephant.jpg`, a JPEG image file, to the `logos` container. Further, you may assign metadata to this object to indicate that the author of this object was someone named Jane Doe. +> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in + ### Upload Object Once you have created a container, you can upload objects to it. @@ -212,7 +217,7 @@ $metadata = array('author' => 'Jane Doe'); $customHeaders = array(); $metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; +$allHeaders = $customHeaders + $metadataHeaders; $fileData = fopen($localFileName, 'r'); $container->uploadObject($remoteFileName, $fileData, $allHeaders); @@ -337,7 +342,7 @@ You can list all the objects stored in a container. An instance of `OpenCloud\Co ```php $objects = $container->objectList(); foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ } ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects.php) ] From 1e79c32576a3f2c35a03d9f2884710a6680fd452 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 8 Sep 2014 15:16:55 +0200 Subject: [PATCH 002/835] Embolden text --- docs/userguide/ObjectStore/README.md | 2 +- docs/userguide/ObjectStore/Storage/Container.md | 2 +- docs/userguide/ObjectStore/Storage/Object.md | 7 ++++--- docs/userguide/ObjectStore/USERGUIDE.md | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index ff395563a..3a5aebb6a 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -46,7 +46,7 @@ In the example above, you are connecting to the ``DFW`` region of the cloud. Any $container = $objectStoreService->createContainer('logos'); ``` -> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in ### 4. Upload an object to the container. diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index 1c01a6156..fd6cfafd6 100755 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -26,7 +26,7 @@ If the response returned is `FALSE`, there was an API error - most likely due to Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. -> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in ## List containers diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index f91238c9c..867ae3d2b 100755 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -22,10 +22,10 @@ setter methods: There are three ways to upload a new file, each of which has different business needs. -> Note: Unlike previous versions, you do not need to manually specify your object's content type. The API will do this +> **Note:** Unlike previous versions, you do not need to manually specify your object's content type. The API will do this for you. -> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in ### To upload a single/basic file: @@ -104,7 +104,8 @@ To return a list of objects: ```php $files = $container->objectList(); -while ($file = $files->next()) { + +foreach ($files as $file) { // ... do something } ``` diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 593a59ef9..f020b314f 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -46,7 +46,7 @@ For example, you may create a container called `logos` to hold all the image fil A container may contain zero or more objects in it. -> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in ### Create Container @@ -187,7 +187,7 @@ An **object** (sometimes referred to as a file) is the unit of storage in an Obj For example, you may upload an object named `php-elephant.jpg`, a JPEG image file, to the `logos` container. Further, you may assign metadata to this object to indicate that the author of this object was someone named Jane Doe. -> Note: when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in ### Upload Object From 12a817a9d3a0994cba62a850eb760ce95ab1b3ce Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 11 Sep 2014 11:49:59 -0700 Subject: [PATCH 003/835] Adding first couple samples. --- .../create-stack-from-template-file.php | 43 +++++++++++++++++ .../create-stack-from-template-url.php | 48 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 samples/Orchestration/create-stack-from-template-file.php create mode 100644 samples/Orchestration/create-stack-from-template-url.php diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php new file mode 100644 index 000000000..71c222a9c --- /dev/null +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -0,0 +1,43 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Create a stack. +$stack = $orchestrationService->createStack(array( + 'stack_name' => 'My Drupal Web Site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'timeout_mins' => 3 +)); diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php new file mode 100644 index 000000000..b56f2a961 --- /dev/null +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -0,0 +1,48 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Create a stack. +$stack = $orchestrationService->createStack(array( + 'stack_name' => 'My Drupal Web Site', + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupaldbuser' + ), + 'timeout_mins' => 5 +)); From 68a47f3bfc18f898c88a7dbba1d997a455afbb2b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 18:07:16 -0700 Subject: [PATCH 004/835] Adding sample code for listing resource types. --- samples/Orchestration/list-resource-types.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 samples/Orchestration/list-resource-types.php diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php new file mode 100644 index 000000000..740879ca5 --- /dev/null +++ b/samples/Orchestration/list-resource-types.php @@ -0,0 +1,42 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get resource types. +$resourceTypes = $orchestrationService->listResourceTypes(); +foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ +} From adaf7c8fae64c2e45dc475c6a1ae8b03aa80e71a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 18:07:37 -0700 Subject: [PATCH 005/835] Adding sample code for validating templates. --- .../validate-template-from-file.php | 41 +++++++++++++++++++ .../validate-template-from-url.php | 41 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 samples/Orchestration/validate-template-from-file.php create mode 100644 samples/Orchestration/validate-template-from-url.php diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php new file mode 100644 index 000000000..ac561cf6c --- /dev/null +++ b/samples/Orchestration/validate-template-from-file.php @@ -0,0 +1,41 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Validate template from file. +$stack = $orchestrationService->createStack(array( + 'template' => file_get_contents(__DIR__ . '/sample_template.yml') +)); diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php new file mode 100644 index 000000000..afe77d19b --- /dev/null +++ b/samples/Orchestration/validate-template-from-url.php @@ -0,0 +1,41 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Validate template from URL +$orchestrationService->validateTemplate(array( + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' +)); From 15d4c85d59cd79d2c6279346d17f17a8b39b8fcd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 18:12:35 -0700 Subject: [PATCH 006/835] Adding sample code for listing stack events. --- samples/Orchestration/list-stack-events.php | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 samples/Orchestration/list-stack-events.php diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php new file mode 100644 index 000000000..3cf1059cc --- /dev/null +++ b/samples/Orchestration/list-stack-events.php @@ -0,0 +1,46 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get list of events for the stack. +$stackEvents = $stack->listEvents(); +foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\StackEvent **/ +} From fab9d6d90328617f5918dcb39d854dff19d8dca0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 20:52:27 -0500 Subject: [PATCH 007/835] Adding sample code for listing stack resources. --- .../Orchestration/list-stack-resources.php | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 samples/Orchestration/list-stack-resources.php diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php new file mode 100644 index 000000000..aebc978b1 --- /dev/null +++ b/samples/Orchestration/list-stack-resources.php @@ -0,0 +1,49 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get stack resource. +$stackResource = $stack->getResource(getenv('RESOURCE_NAME')); + +// 5. Get list of events for the stack resource. +$stackResourceEvents = $stackResource->listEvents(); +foreach ($stackResourceEvents as $stackResourceEvent) { + /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +} From 9e4e4de47ef03c291e43ff5f5cb742f5ace21352 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 20:52:48 -0500 Subject: [PATCH 008/835] Adding sample code for listing stack resource events. --- .../list-stack-resource-events.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 samples/Orchestration/list-stack-resource-events.php diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php new file mode 100644 index 000000000..0ff5f56ca --- /dev/null +++ b/samples/Orchestration/list-stack-resource-events.php @@ -0,0 +1,32 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); From aa5a38e4110401c9834968a6137b47afdce4c3ec Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 20:55:25 -0500 Subject: [PATCH 009/835] Fixing code sample. --- samples/Orchestration/list-stack-resources.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index aebc978b1..b94f018a5 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -21,7 +21,7 @@ // * RAX_USERNAME: Your Rackspace Cloud Account Username, and // * RAX_API_KEY: Your Rackspace Cloud Account API Key // * STACK_NAME: Name of stack -// * STACK_RESOURCE_NAME: Name of resource in stack +// require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; @@ -39,11 +39,8 @@ // 3. Get stack. $stack = $orchestrationService->getStack(getenv('STACK_NAME')); -// 4. Get stack resource. -$stackResource = $stack->getResource(getenv('RESOURCE_NAME')); - -// 5. Get list of events for the stack resource. -$stackResourceEvents = $stackResource->listEvents(); -foreach ($stackResourceEvents as $stackResourceEvent) { - /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +// 5. Get list of resources in the stack. +$stackResources = $stack->listResources(); +foreach ($stackResources as $stackResource) { + /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ } From 848c5e7a6df8f7ac9a67f63338aa28b1daecfe4c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 20:56:27 -0500 Subject: [PATCH 010/835] Adding sample code for listing stack resource events. --- .../list-stack-resource-events.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index 0ff5f56ca..d0264b1d6 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -20,6 +20,8 @@ // * Prior to running this script, you must setup the following environment variables: // * RAX_USERNAME: Your Rackspace Cloud Account Username, and // * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * STACK_NAME: Name of stack +// * STACK_RESOURCE_NAME: Name of resource in stack // require __DIR__ . '/../../vendor/autoload.php'; @@ -30,3 +32,19 @@ 'username' => getenv('RAX_USERNAME'), 'apiKey' => getenv('RAX_API_KEY') )); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get resource in stack. +$stackResource = $stack->getResource(getenv('RESOURCE_NAME')); + +// 5. Get list of events for the stack resource. +$stackResourceEvents = $stackResource->listEvents(); +foreach ($stackResourceEvents as $stackResourceEvent) { + /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +} From 0ed3306b3c72b5915f78c93eb09a815613a6e2e6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 20:58:28 -0500 Subject: [PATCH 011/835] Fixing step number. --- samples/Orchestration/list-stack-resources.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index b94f018a5..b19952058 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -39,7 +39,7 @@ // 3. Get stack. $stack = $orchestrationService->getStack(getenv('STACK_NAME')); -// 5. Get list of resources in the stack. +// 4. Get list of resources in the stack. $stackResources = $stack->listResources(); foreach ($stackResources as $stackResource) { /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ From 818e2a2165ebaa69ac2525b2758fe0e00c2f5b4e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 20:59:34 -0500 Subject: [PATCH 012/835] Adding sample code for retrieving a stack. --- samples/Orchestration/get-stack.php | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 samples/Orchestration/get-stack.php diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php new file mode 100644 index 000000000..cd8fd45f1 --- /dev/null +++ b/samples/Orchestration/get-stack.php @@ -0,0 +1,41 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From bc864f4313cbb6269c28f5a706fda9bdf2561c30 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 21:06:37 -0500 Subject: [PATCH 013/835] Adding sample code for deleting a stack. --- samples/Orchestration/delete-stack.php | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 samples/Orchestration/delete-stack.php diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php new file mode 100644 index 000000000..975ea80ec --- /dev/null +++ b/samples/Orchestration/delete-stack.php @@ -0,0 +1,43 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Delete stack. +$stack->delete(); From 69815c38bed09020d9557fe9be95c6f9321fb572 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 12 Sep 2014 21:09:45 -0500 Subject: [PATCH 014/835] Adding sample code for listing stacks. --- samples/Orchestration/list-stacks.php | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 samples/Orchestration/list-stacks.php diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php new file mode 100644 index 000000000..b1d532784 --- /dev/null +++ b/samples/Orchestration/list-stacks.php @@ -0,0 +1,42 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stacks. +$stacks = $orchestrationService->listStacks(); +foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +} From 545f978f9333d490b711a5faacd58a8d4597540d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 15 Sep 2014 05:31:30 -0700 Subject: [PATCH 015/835] Use OpenStack environment variables. --- samples/Orchestration/create-stack-from-template-file.php | 8 ++++---- samples/Orchestration/create-stack-from-template-url.php | 8 ++++---- samples/Orchestration/delete-stack.php | 8 ++++---- samples/Orchestration/get-stack.php | 8 ++++---- samples/Orchestration/list-resource-types.php | 8 ++++---- samples/Orchestration/list-stack-events.php | 8 ++++---- samples/Orchestration/list-stack-resource-events.php | 8 ++++---- samples/Orchestration/list-stack-resources.php | 8 ++++---- samples/Orchestration/list-stacks.php | 8 ++++---- samples/Orchestration/validate-template-from-file.php | 8 ++++---- samples/Orchestration/validate-template-from-url.php | 8 ++++---- 11 files changed, 44 insertions(+), 44 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 71c222a9c..7513209d3 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // require __DIR__ . '/../../vendor/autoload.php'; @@ -27,8 +27,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index b56f2a961..838f698bb 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // require __DIR__ . '/../../vendor/autoload.php'; @@ -27,8 +27,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index 975ea80ec..8c5c3fc45 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // * STACK_NAME: Name of stack // @@ -28,8 +28,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index cd8fd45f1..1927c9e0a 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // * STACK_NAME: Name of stack // @@ -28,8 +28,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 740879ca5..6679aaa16 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // require __DIR__ . '/../../vendor/autoload.php'; @@ -27,8 +27,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index 3cf1059cc..ef48aedf0 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // * STACK_NAME: Name of stack // @@ -28,8 +28,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index d0264b1d6..253c3bdfe 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // * STACK_NAME: Name of stack // * STACK_RESOURCE_NAME: Name of resource in stack // @@ -29,8 +29,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index b19952058..d45b6114a 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // * STACK_NAME: Name of stack // @@ -28,8 +28,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index b1d532784..120584f22 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // require __DIR__ . '/../../vendor/autoload.php'; @@ -27,8 +27,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index ac561cf6c..a1df6f29a 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // require __DIR__ . '/../../vendor/autoload.php'; @@ -27,8 +27,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index afe77d19b..68a75ca49 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -18,8 +18,8 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, and +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key // require __DIR__ . '/../../vendor/autoload.php'; @@ -27,8 +27,8 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') + 'username' => getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') )); // 2. Obtain an Orchestration service object from the client. From dcd219c6f9a81532a1943a49ce407bf7bf4e803a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 15 Sep 2014 05:40:24 -0700 Subject: [PATCH 016/835] Using OS_REGION instea of hardcoding DFW region. --- .../Orchestration/create-stack-from-template-file.php | 7 ++++--- samples/Orchestration/create-stack-from-template-url.php | 7 ++++--- samples/Orchestration/delete-stack.php | 7 ++++--- samples/Orchestration/get-stack.php | 7 ++++--- samples/Orchestration/list-resource-types.php | 7 ++++--- samples/Orchestration/list-stack-events.php | 7 ++++--- samples/Orchestration/list-stack-resource-events.php | 9 +++++---- samples/Orchestration/list-stack-resources.php | 7 ++++--- samples/Orchestration/list-stacks.php | 7 ++++--- samples/Orchestration/validate-template-from-file.php | 7 ++++--- samples/Orchestration/validate-template-from-url.php | 7 ++++--- 11 files changed, 45 insertions(+), 34 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 7513209d3..fdc3267ff 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_REGION: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -32,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 838f698bb..5bbf40798 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_REGION: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -32,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index 8c5c3fc45..f9f5c6ee8 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_REGION: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -33,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index 1927c9e0a..997c1f343 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_REGION: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -33,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 6679aaa16..816c2c13f 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_REGION: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -32,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get resource types. diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index ef48aedf0..c8b513e7f 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_REGION: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -33,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index 253c3bdfe..ff557b4ea 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -18,9 +18,10 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key -// * STACK_NAME: Name of stack +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_REGION: The OpenStack Cloud region you want to use, +// * STACK_NAME: Name of stack, and // * STACK_RESOURCE_NAME: Name of resource in stack // @@ -34,7 +35,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index d45b6114a..a59544750 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_REGION: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -33,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 120584f22..38335c051 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_REGION: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -32,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stacks. diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index a1df6f29a..b4e49013c 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_REGION: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -32,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from file. diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index 68a75ca49..7a8b411ac 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -18,8 +18,9 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, and -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key +// * OS_USERNAME: Your OpenStack Cloud Account Username, +// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_REGION: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -32,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = 'DFW'; +$region = getenv('OS_REGION'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from URL From 685a1e6311d9bde8274ed40d77c9785c8196fc5c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 15 Sep 2014 05:45:33 -0700 Subject: [PATCH 017/835] Using OpenStack namespace instead of Rackspace namespace for the service class. --- samples/Orchestration/create-stack-from-template-file.php | 4 ++-- samples/Orchestration/create-stack-from-template-url.php | 4 ++-- samples/Orchestration/delete-stack.php | 4 ++-- samples/Orchestration/get-stack.php | 4 ++-- samples/Orchestration/list-resource-types.php | 4 ++-- samples/Orchestration/list-stack-events.php | 4 ++-- samples/Orchestration/list-stack-resource-events.php | 4 ++-- samples/Orchestration/list-stack-resources.php | 4 ++-- samples/Orchestration/list-stacks.php | 4 ++-- samples/Orchestration/validate-template-from-file.php | 4 ++-- samples/Orchestration/validate-template-from-url.php | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index fdc3267ff..292f43f9b 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -24,10 +24,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 5bbf40798..f79a9fe5f 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -24,10 +24,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index f9f5c6ee8..f6bf4c09f 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -25,10 +25,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index 997c1f343..854de5756 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -25,10 +25,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 816c2c13f..3d8d598b6 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -24,10 +24,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index c8b513e7f..1a00338cf 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -25,10 +25,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index ff557b4ea..b40c8b9f1 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -26,10 +26,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index a59544750..bd86ee87e 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -25,10 +25,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 38335c051..0d68fe902 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -24,10 +24,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index b4e49013c..51d9f24e4 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -24,10 +24,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index 7a8b411ac..048b4364f 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -24,10 +24,10 @@ // require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); From 672a6ba2823d366952e446753b50b578b0781de2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 15 Sep 2014 05:46:56 -0700 Subject: [PATCH 018/835] Fixing OpenStack region name environment variable name. --- samples/Orchestration/create-stack-from-template-file.php | 4 ++-- samples/Orchestration/create-stack-from-template-url.php | 4 ++-- samples/Orchestration/delete-stack.php | 4 ++-- samples/Orchestration/get-stack.php | 4 ++-- samples/Orchestration/list-resource-types.php | 4 ++-- samples/Orchestration/list-stack-events.php | 4 ++-- samples/Orchestration/list-stack-resource-events.php | 4 ++-- samples/Orchestration/list-stack-resources.php | 4 ++-- samples/Orchestration/list-stacks.php | 4 ++-- samples/Orchestration/validate-template-from-file.php | 4 ++-- samples/Orchestration/validate-template-from-url.php | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 292f43f9b..fb19ae29c 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and -// * OS_REGION: The OpenStack Cloud region you want to use +// * OS_REGION_NAME: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -33,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index f79a9fe5f..828e1acaa 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and -// * OS_REGION: The OpenStack Cloud region you want to use +// * OS_REGION_NAME: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -33,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index f6bf4c09f..c04eda88e 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, -// * OS_REGION: The OpenStack Cloud region you want to use, and +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -34,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index 854de5756..73770ee56 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, -// * OS_REGION: The OpenStack Cloud region you want to use, and +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -34,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 3d8d598b6..04631bedd 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and -// * OS_REGION: The OpenStack Cloud region you want to use +// * OS_REGION_NAME: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -33,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get resource types. diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index 1a00338cf..a86d71de9 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, -// * OS_REGION: The OpenStack Cloud region you want to use, and +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -34,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index b40c8b9f1..29630ab67 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, -// * OS_REGION: The OpenStack Cloud region you want to use, +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, // * STACK_NAME: Name of stack, and // * STACK_RESOURCE_NAME: Name of resource in stack // @@ -35,7 +35,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index bd86ee87e..d6b7ee7f0 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, -// * OS_REGION: The OpenStack Cloud region you want to use, and +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -34,7 +34,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stack. diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 0d68fe902..2f69a6ed4 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and -// * OS_REGION: The OpenStack Cloud region you want to use +// * OS_REGION_NAME: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -33,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Get stacks. diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index 51d9f24e4..ce9a01179 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and -// * OS_REGION: The OpenStack Cloud region you want to use +// * OS_REGION_NAME: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -33,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from file. diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index 048b4364f..517d711a1 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and -// * OS_REGION: The OpenStack Cloud region you want to use +// * OS_REGION_NAME: The OpenStack Cloud region you want to use // require __DIR__ . '/../../vendor/autoload.php'; @@ -33,7 +33,7 @@ )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION'); +$region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from URL From 3b01abff4dce665299a98126b1841e00976491a0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 15 Sep 2014 15:13:59 -0700 Subject: [PATCH 019/835] Fixing copy/pasta. --- samples/Orchestration/validate-template-from-file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index ce9a01179..24e92e9d5 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -37,6 +37,6 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from file. -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->validateTemplate(array( 'template' => file_get_contents(__DIR__ . '/sample_template.yml') )); From 6d8d8b4f9fac693d5b59a369fba55290a085cb20 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 09:54:08 -0700 Subject: [PATCH 020/835] Using OS_AUTH_URL instead of Rackspace US auth URL constant. --- samples/Orchestration/create-stack-from-template-file.php | 3 ++- samples/Orchestration/create-stack-from-template-url.php | 3 ++- samples/Orchestration/delete-stack.php | 3 ++- samples/Orchestration/get-stack.php | 3 ++- samples/Orchestration/list-resource-types.php | 3 ++- samples/Orchestration/list-stack-events.php | 3 ++- samples/Orchestration/list-stack-resource-events.php | 3 ++- samples/Orchestration/list-stack-resources.php | 3 ++- samples/Orchestration/list-stacks.php | 3 ++- samples/Orchestration/validate-template-from-file.php | 3 ++- samples/Orchestration/validate-template-from-url.php | 3 ++- 11 files changed, 22 insertions(+), 11 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index fb19ae29c..0a852d478 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use @@ -27,7 +28,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 828e1acaa..40743f805 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use @@ -27,7 +28,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index c04eda88e..5c870b000 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and @@ -28,7 +29,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index 73770ee56..ed603f822 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and @@ -28,7 +29,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 04631bedd..93dc84fe5 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use @@ -27,7 +28,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index a86d71de9..ad1622628 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and @@ -28,7 +29,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index 29630ab67..f0f381371 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, @@ -29,7 +30,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index d6b7ee7f0..48e45a1ca 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and @@ -28,7 +29,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 2f69a6ed4..270206a86 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use @@ -27,7 +28,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index 24e92e9d5..28cb25e02 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use @@ -27,7 +28,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index 517d711a1..8b8fe5d10 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -18,6 +18,7 @@ // // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: +// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use @@ -27,7 +28,7 @@ use OpenCloud\OpenStack; // 1. Instantiate a Rackspace client. -$client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, array( +$client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') )); From c79fd71965aff0089737a002f16e4a41b18279f8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 09:57:20 -0700 Subject: [PATCH 021/835] Updating comment to reflect code. --- samples/Orchestration/create-stack-from-template-file.php | 2 +- samples/Orchestration/create-stack-from-template-url.php | 2 +- samples/Orchestration/delete-stack.php | 2 +- samples/Orchestration/get-stack.php | 2 +- samples/Orchestration/list-resource-types.php | 2 +- samples/Orchestration/list-stack-events.php | 2 +- samples/Orchestration/list-stack-resource-events.php | 2 +- samples/Orchestration/list-stack-resources.php | 2 +- samples/Orchestration/list-stacks.php | 2 +- samples/Orchestration/validate-template-from-file.php | 2 +- samples/Orchestration/validate-template-from-url.php | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 0a852d478..006dc3ca6 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -27,7 +27,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 40743f805..210940171 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -27,7 +27,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index 5c870b000..7bf6ebe39 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -28,7 +28,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index ed603f822..075f6225f 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -28,7 +28,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 93dc84fe5..2744bdbca 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -27,7 +27,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index ad1622628..ec0625be6 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -28,7 +28,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index f0f381371..82c0457c8 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -29,7 +29,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index 48e45a1ca..883eb3895 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -28,7 +28,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 270206a86..607e8cd27 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -27,7 +27,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index 28cb25e02..f21472a49 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -27,7 +27,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index 8b8fe5d10..47a901e9d 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -27,7 +27,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; -// 1. Instantiate a Rackspace client. +// 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), 'apiKey' => getenv('NOVA_API_KEY') From c1103969a4218a4a0b27a5423a44dab91b8c6b65 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 12:22:32 -0700 Subject: [PATCH 022/835] Adding sample code for updating stacks. --- samples/Orchestration/update-stack.php | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 samples/Orchestration/update-stack.php diff --git a/samples/Orchestration/update-stack.php b/samples/Orchestration/update-stack.php new file mode 100644 index 000000000..bb132957c --- /dev/null +++ b/samples/Orchestration/update-stack.php @@ -0,0 +1,53 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Update stack. +$orchestrationService->updateStack(array( + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupalweb' + ), + 'timeout_mins' => 5 +)); From 889f73abb6192b48fa28ead82386262df83cbcca Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 12:26:25 -0700 Subject: [PATCH 023/835] Fixing name of environment variable. --- samples/Orchestration/list-stack-resource-events.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index 82c0457c8..a31e71bbb 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -43,7 +43,7 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get resource in stack. -$stackResource = $stack->getResource(getenv('RESOURCE_NAME')); +$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); // 5. Get list of events for the stack resource. $stackResourceEvents = $stackResource->listEvents(); From 3435e26dcee21beac97127176fbd1f612afc9305 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 12:27:46 -0700 Subject: [PATCH 024/835] Adding sample code for retrieving a stack resource. --- samples/Orchestration/get-stack-resource.php | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 samples/Orchestration/get-stack-resource.php diff --git a/samples/Orchestration/get-stack-resource.php b/samples/Orchestration/get-stack-resource.php new file mode 100644 index 000000000..2148de424 --- /dev/null +++ b/samples/Orchestration/get-stack-resource.php @@ -0,0 +1,47 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get resource in stack. +$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +/** @var $stack OpenCloud\Orchestration\Resource\StackResource **/ From 2d5b303381105fa3aae0d6dda0494de2316ba2b4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 12:47:11 -0700 Subject: [PATCH 025/835] Fixing type of object returned. --- samples/Orchestration/list-stack-events.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index ec0625be6..1208dd449 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -44,5 +44,5 @@ // 4. Get list of events for the stack. $stackEvents = $stack->listEvents(); foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\StackEvent **/ + /** @var $stackEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ } From 90dc06918163c8ff73b5fc823929d9d34e475dc3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 12:50:19 -0700 Subject: [PATCH 026/835] Adding sample code for retrieving a stack resource event. --- .../get-stack-resource-event.php | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 samples/Orchestration/get-stack-resource-event.php diff --git a/samples/Orchestration/get-stack-resource-event.php b/samples/Orchestration/get-stack-resource-event.php new file mode 100644 index 000000000..9f257935b --- /dev/null +++ b/samples/Orchestration/get-stack-resource-event.php @@ -0,0 +1,51 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get resource in stack. +$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); + +// 5. Get stack resource event. +$stackResourceEvent = $stackResource->getEvent(getenv('STACK_RESOURCE_EVENT_ID')); +/** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ From b0a50fd09300342637c10844d82823e41834b501 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 14:31:09 -0700 Subject: [PATCH 027/835] Fixing variable name in docblock. --- samples/Orchestration/get-stack-resource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/get-stack-resource.php b/samples/Orchestration/get-stack-resource.php index 2148de424..fb457fd13 100644 --- a/samples/Orchestration/get-stack-resource.php +++ b/samples/Orchestration/get-stack-resource.php @@ -44,4 +44,4 @@ // 4. Get resource in stack. $stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); -/** @var $stack OpenCloud\Orchestration\Resource\StackResource **/ +/** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ From 55502d59ee006a701ea7976d60aa7cb862f2bd21 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 15:12:23 -0700 Subject: [PATCH 028/835] Adding sample code for retrieving a stack's template. --- samples/Orchestration/get-stack-template.php | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 samples/Orchestration/get-stack-template.php diff --git a/samples/Orchestration/get-stack-template.php b/samples/Orchestration/get-stack-template.php new file mode 100644 index 000000000..2c77baab3 --- /dev/null +++ b/samples/Orchestration/get-stack-template.php @@ -0,0 +1,46 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get stack template. +$stackTemplate = $stack->getTemplate(); +/** @var $stackTemplate string **/ From f5663ebc35d855d7dae3ef104959e25ac1fbbac5 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 15:32:41 -0700 Subject: [PATCH 029/835] Fixing update object and syntax. --- samples/Orchestration/update-stack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/update-stack.php b/samples/Orchestration/update-stack.php index bb132957c..cbc646a9c 100644 --- a/samples/Orchestration/update-stack.php +++ b/samples/Orchestration/update-stack.php @@ -42,7 +42,7 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Update stack. -$orchestrationService->updateStack(array( +$stack->update(array( 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'parameters' => array( 'flavor_id' => 'performance1_1', From 8eb99ebd46b7ecf3b4e2613257f25c667fa3c825 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 15:33:22 -0700 Subject: [PATCH 030/835] Adding sample code for previewing a stack. --- samples/Orchestration/preview-stack.php | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 samples/Orchestration/preview-stack.php diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php new file mode 100644 index 000000000..b9870046a --- /dev/null +++ b/samples/Orchestration/preview-stack.php @@ -0,0 +1,46 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Create a stack. +$stackPreview = $orchestrationService->previewStack(array( + 'stack_name' => 'My Drupal Web Site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\StackPreview **/ From d48a73de421cc78c19ca71769c9ee3ff7e1cb15b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 17 Sep 2014 16:00:37 -0700 Subject: [PATCH 031/835] Adding sample code for retrieving a resource type. --- samples/Orchestration/get-resource-type.php | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 samples/Orchestration/get-resource-type.php diff --git a/samples/Orchestration/get-resource-type.php b/samples/Orchestration/get-resource-type.php new file mode 100644 index 000000000..c6bf71d78 --- /dev/null +++ b/samples/Orchestration/get-resource-type.php @@ -0,0 +1,43 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get resource type. +$stack = $orchestrationService->getResourceType(getenv('RESOURCE_TYPE_NAME')); +/** @var $stack OpenCloud\Orchestration\Resource\ResourceType **/ From 495c522e023c58d616533560314637e4e0302c48 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 08:57:08 -0700 Subject: [PATCH 032/835] Splitting up getting stack. --- .../get-stack-by-name-and-id.php | 44 +++++++++++++++++++ .../{get-stack.php => get-stack-by-name.php} | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 samples/Orchestration/get-stack-by-name-and-id.php rename samples/Orchestration/{get-stack.php => get-stack-by-name.php} (97%) diff --git a/samples/Orchestration/get-stack-by-name-and-id.php b/samples/Orchestration/get-stack-by-name-and-id.php new file mode 100644 index 000000000..421f799a7 --- /dev/null +++ b/samples/Orchestration/get-stack-by-name-and-id.php @@ -0,0 +1,44 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack using the stack name. +$stack = $orchestrationService->getStack(getenv('STACK_NAME'), getenv('STACK_ID')); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack-by-name.php similarity index 97% rename from samples/Orchestration/get-stack.php rename to samples/Orchestration/get-stack-by-name.php index 075f6225f..96751da2b 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack-by-name.php @@ -38,6 +38,6 @@ $region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); -// 3. Get stack. +// 3. Get stack using the stack name. $stack = $orchestrationService->getStack(getenv('STACK_NAME')); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From c463fd7558f5878698053697db3caf56a34d4451 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 09:26:50 -0700 Subject: [PATCH 033/835] Fixing variable name. --- samples/Orchestration/get-resource-type.php | 4 +- .../list-stack-resources-by-stack-name.php | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 samples/Orchestration/list-stack-resources-by-stack-name.php diff --git a/samples/Orchestration/get-resource-type.php b/samples/Orchestration/get-resource-type.php index c6bf71d78..305b7419e 100644 --- a/samples/Orchestration/get-resource-type.php +++ b/samples/Orchestration/get-resource-type.php @@ -39,5 +39,5 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Get resource type. -$stack = $orchestrationService->getResourceType(getenv('RESOURCE_TYPE_NAME')); -/** @var $stack OpenCloud\Orchestration\Resource\ResourceType **/ +$resourceType = $orchestrationService->getResourceType(getenv('RESOURCE_TYPE_NAME')); +/** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ diff --git a/samples/Orchestration/list-stack-resources-by-stack-name.php b/samples/Orchestration/list-stack-resources-by-stack-name.php new file mode 100644 index 000000000..883eb3895 --- /dev/null +++ b/samples/Orchestration/list-stack-resources-by-stack-name.php @@ -0,0 +1,48 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get list of resources in the stack. +$stackResources = $stack->listResources(); +foreach ($stackResources as $stackResource) { + /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +} From 87bf4b2819569d4e208f7570c93d8862ed8d507b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 09:28:06 -0700 Subject: [PATCH 034/835] Removing unnecessary file. --- .../list-stack-resources-by-stack-name.php | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 samples/Orchestration/list-stack-resources-by-stack-name.php diff --git a/samples/Orchestration/list-stack-resources-by-stack-name.php b/samples/Orchestration/list-stack-resources-by-stack-name.php deleted file mode 100644 index 883eb3895..000000000 --- a/samples/Orchestration/list-stack-resources-by-stack-name.php +++ /dev/null @@ -1,48 +0,0 @@ - getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') -)); - -// 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); - -// 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); - -// 4. Get list of resources in the stack. -$stackResources = $stack->listResources(); -foreach ($stackResources as $stackResource) { - /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ -} From 6626f51988c9c602a6e92d55b3f802aae6e819d4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 09:28:26 -0700 Subject: [PATCH 035/835] Using Stack class for preview as well. --- samples/Orchestration/preview-stack.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php index b9870046a..0db208a0a 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack.php @@ -38,9 +38,9 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stackPreview = $orchestrationService->previewStack(array( +$stack = $orchestrationService->previewStack(array( 'stack_name' => 'My Drupal Web Site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'timeout_mins' => 3 )); -/** @var $stack OpenCloud\Orchestration\Resource\StackPreview **/ +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From 9bca7a7c307a734b2efc26de5b278c9c4862f146 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 14:36:45 -0700 Subject: [PATCH 036/835] Noting return type of createStack method. --- samples/Orchestration/create-stack-from-template-file.php | 1 + samples/Orchestration/create-stack-from-template-url.php | 1 + 2 files changed, 2 insertions(+) diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 006dc3ca6..0e598b5bd 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -43,3 +43,4 @@ 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'timeout_mins' => 3 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 210940171..42ffe2f6a 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -48,3 +48,4 @@ ), 'timeout_mins' => 5 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From d39fbd084f9866ae673fb5f831695f15ee69faeb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 14:37:16 -0700 Subject: [PATCH 037/835] Adding sample code for retrieving a resource type template. --- .../get-resource-type-template.php | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 samples/Orchestration/get-resource-type-template.php diff --git a/samples/Orchestration/get-resource-type-template.php b/samples/Orchestration/get-resource-type-template.php new file mode 100644 index 000000000..06d4defb0 --- /dev/null +++ b/samples/Orchestration/get-resource-type-template.php @@ -0,0 +1,46 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get resource type. +$resourceType = $orchestrationService->getResourceType(getenv('RESOURCE_TYPE_NAME')); + +// 4. Get template for resource type. +$resourceTypeTemplate = $resourceType->getTemplate(); +/** @var $resourceTypeTemplate OpenCloud\Orchestration\Resource\ResourceTypeTemplate **/ From 4fd5bb4bf380e01487d55d226d0d25a73527b305 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 14:42:04 -0700 Subject: [PATCH 038/835] Adding sample code for abandoning a stack. --- samples/Orchestration/abandon-stack.php | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 samples/Orchestration/abandon-stack.php diff --git a/samples/Orchestration/abandon-stack.php b/samples/Orchestration/abandon-stack.php new file mode 100644 index 000000000..8412d070f --- /dev/null +++ b/samples/Orchestration/abandon-stack.php @@ -0,0 +1,47 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Abandon stack. +$abandonStackData = $stack->abandon(); +/** @var $abandonStackData string **/ + From 9e9e661f17f1bb460acd261188302e3e985a7e08 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Sep 2014 14:51:39 -0700 Subject: [PATCH 039/835] Adding sample code for retrieving build info. --- samples/Orchestration/get-build-info.php | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 samples/Orchestration/get-build-info.php diff --git a/samples/Orchestration/get-build-info.php b/samples/Orchestration/get-build-info.php new file mode 100644 index 000000000..a5cf7f040 --- /dev/null +++ b/samples/Orchestration/get-build-info.php @@ -0,0 +1,42 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get build info. +$buildInfo = $orchestrationService->getBuildInfo(); +/** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ From 901b8e1e5ea151af9ba55cef7aab783a3b06d04a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 23 Sep 2014 15:48:09 +0200 Subject: [PATCH 040/835] [ci skip] Linkify urlencode --- docs/userguide/ObjectStore/README.md | 2 +- docs/userguide/ObjectStore/Storage/Container.md | 2 +- docs/userguide/ObjectStore/Storage/Object.md | 2 +- docs/userguide/ObjectStore/USERGUIDE.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index 3a5aebb6a..33ad706dd 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -46,7 +46,7 @@ In the example above, you are connecting to the ``DFW`` region of the cloud. Any $container = $objectStoreService->createContainer('logos'); ``` -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in ### 4. Upload an object to the container. diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index fd6cfafd6..5f75de9d1 100755 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -26,7 +26,7 @@ If the response returned is `FALSE`, there was an API error - most likely due to Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in ## List containers diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 867ae3d2b..4127b2610 100755 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -25,7 +25,7 @@ There are three ways to upload a new file, each of which has different business > **Note:** Unlike previous versions, you do not need to manually specify your object's content type. The API will do this for you. -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in ### To upload a single/basic file: diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index f020b314f..5257fbe6f 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -46,7 +46,7 @@ For example, you may create a container called `logos` to hold all the image fil A container may contain zero or more objects in it. -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in ### Create Container @@ -187,7 +187,7 @@ An **object** (sometimes referred to as a file) is the unit of storage in an Obj For example, you may upload an object named `php-elephant.jpg`, a JPEG image file, to the `logos` container. Further, you may assign metadata to this object to indicate that the author of this object was someone named Jane Doe. -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with `urlencode` before passing them in +> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in ### Upload Object From b62f44cfeb249408939a89f2c60cb9bcea50792d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 29 Sep 2014 10:59:56 -0700 Subject: [PATCH 041/835] Adding quick start. --- docs/userguide/Orchestration/README.md | 77 ++++++++++++++++++++++++++ samples/Orchestration/quickstart.php | 49 ++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 docs/userguide/Orchestration/README.md create mode 100644 samples/Orchestration/quickstart.php diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md new file mode 100644 index 000000000..282a6f3b2 --- /dev/null +++ b/docs/userguide/Orchestration/README.md @@ -0,0 +1,77 @@ +# Orchestration + +**Orchestration** is a service that can be used to create and manage cloud +resources. Examples of such resources are databases, load balancers, +servers and software installed on them. + +## Concepts + +To use the Orchestration service effectively, you should understand several +key concepts: + +* **Template**: An Orchestration template is a JSON or YAML document that +describes how a set of resources should be assembled to produce a working +deployment. The template specifies what resources should be used, what +attributes of these resources are parameterized and what information is +output to the user when a template is instantiated. + +* **Resource**: A resource is a template artifact that represents some component of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load balancer, some configuration management system, and so forth). + +* **Stack**: A stack is a running instance of a template. When a stack is created, +the resources specified in the template are created. + +## Getting started + +### 1. Instantiate an OpenStack or Rackspace client. + +Choose one of the following two options: + +* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. + + ```php + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + ``` + +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. + + ```php + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +### 2. Obtain an Orchestration service object from the client. +```php +$region = ''; +$orchestrationService = $client->orchestrationService(null, $region); +``` + +In the example above, you are connecting to the ``DFW`` region of the cloud. Any stacks and resources created with this `$orchestrationService` instance will be stored in that cloud region. + +### 3. Create a stack from a template. +```php +$stack = $orchestrationService->createStack(array( + 'stack_name' => 'Cloud server with attached block storage', + 'template_url' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', + 'parameters' => array( + 'key_name' => 'mine', + 'flavor' => 'performance1_1', + 'image' => '0112b238-4267-4a22-9785-fcf75814bc2f' // Ubuntu 14.04 LTS (Trusty Tahr) + ), + 'timeout_mins' => 5 +)); +``` + +[ [Get the executable PHP script for this example](/samples/Orchestration/quickstart.php) ] + +## Next steps + +Once you have created a stack, there is more you can do with it. See [complete user guide for load balancers](USERGUIDE.md). diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php new file mode 100644 index 000000000..095d11531 --- /dev/null +++ b/samples/Orchestration/quickstart.php @@ -0,0 +1,49 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Create a stack. +$stack = $orchestrationService->createStack(array( + 'stack_name' => 'Cloud server with attached block storage', + 'template_url' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', + 'parameters' => array( + 'key_name' => 'mine', + 'flavor' => 'performance1_1', + 'image' => '0112b238-4267-4a22-9785-fcf75814bc2f' // Ubuntu 14.04 LTS (Trusty Tahr) + ), + 'timeout_mins' => 5 +)); From 387c7c778dc31f21acb055403d74207c95db6f87 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 29 Sep 2014 11:34:09 -0700 Subject: [PATCH 042/835] Adding skeleton user guide. --- docs/userguide/Orchestration/USERGUIDE.md | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 docs/userguide/Orchestration/USERGUIDE.md diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md new file mode 100644 index 000000000..087f653b7 --- /dev/null +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -0,0 +1,86 @@ +# The Complete User Guide to the Orchestration Service + +TODO: Intro + +## Concepts + +TODO + +## Prerequisites + +### Client +To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. + +* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. + + ```php + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. + + ```php + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +### Orchestration Service +All orchestration operations are done via an orchestration service object. + +```php +$region = 'DFW'; +$orchestrationService = $client->orchestrationService(null, $region); +``` + +In the example above, you are connecting to the ``DFW`` region of the cloud. Any resources and stacks created with this `$orchestrationService` instance will be created in that cloud region. + +## Templates +### Validate Template +#### Validate Template from File +#### Validate Template from URL + +## Stacks + +### Preview Stack +### Create Stack +#### Create Stack from Template File +#### Create Stack from Template URL +### List Stacks +### Get Stack +#### Get Stack by Name and ID +#### Get Stack by Name +### Get Stack Template +### Update Stack +### Delete Stack +### Abandon Stack +### Adopt Stack + +## Stack Resources + +### List stack Resources +### Get stack Resource + +## Stack Resource Events + +### List Stack Events +### List Stack Resource Events +### Get Stack Resource Event + +## Resource Types + +### List Resource Types +### Get Resource Type +### Get Resource Type Template + +## Build Info + +### Get Build Info From 7321ebc6eee1a46d5447233945aeefa9b20e8a10 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 29 Sep 2014 11:35:55 -0700 Subject: [PATCH 043/835] Adding sample code for adopting a stack. --- samples/Orchestration/adopt-stack.php | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 samples/Orchestration/adopt-stack.php diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php new file mode 100644 index 000000000..49744b4e0 --- /dev/null +++ b/samples/Orchestration/adopt-stack.php @@ -0,0 +1,47 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Adopt a stack. +$stack = $orchestrationService->adoptStack(array( + 'stack_name' => 'My Drupal Web Site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'adopt_stack_data' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From c621b4bc1143ddd34dd037b845240db860393797 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 16:30:47 -0700 Subject: [PATCH 044/835] Fixing stack names. --- samples/Orchestration/adopt-stack.php | 2 +- samples/Orchestration/create-stack-from-template-file.php | 2 +- samples/Orchestration/create-stack-from-template-url.php | 2 +- samples/Orchestration/preview-stack.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index 49744b4e0..924bedeea 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -39,7 +39,7 @@ // 3. Adopt a stack. $stack = $orchestrationService->adoptStack(array( - 'stack_name' => 'My Drupal Web Site', + 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'adopt_stack_data' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), 'timeout_mins' => 3 diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 0e598b5bd..fe3a5ce55 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -39,7 +39,7 @@ // 3. Create a stack. $stack = $orchestrationService->createStack(array( - 'stack_name' => 'My Drupal Web Site', + 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'timeout_mins' => 3 )); diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 42ffe2f6a..8ee90eba9 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -39,7 +39,7 @@ // 3. Create a stack. $stack = $orchestrationService->createStack(array( - 'stack_name' => 'My Drupal Web Site', + 'stack_name' => 'my-drupal-web-site', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( 'flavor_id' => 'performance1_1', diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php index 0db208a0a..472f63475 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack.php @@ -39,7 +39,7 @@ // 3. Create a stack. $stack = $orchestrationService->previewStack(array( - 'stack_name' => 'My Drupal Web Site', + 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'timeout_mins' => 3 )); From a6330fd5542580a79cfabb1cd1586e18d3d88081 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 16:31:20 -0700 Subject: [PATCH 045/835] Fixing comment. --- samples/Orchestration/preview-stack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php index 472f63475..f35aa5fb2 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack.php @@ -37,7 +37,7 @@ $region = getenv('OS_REGION_NAME'); $orchestrationService = $client->orchestrationService(null, $region); -// 3. Create a stack. +// 3. Preview a stack. $stack = $orchestrationService->previewStack(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), From a2a4f4e3488b4cd61d34b61bc353c6c0cd0ab498 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 16:31:36 -0700 Subject: [PATCH 046/835] Previewing a stack does not require a timeout. --- samples/Orchestration/preview-stack.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php index f35aa5fb2..7968d9942 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack.php @@ -40,7 +40,6 @@ // 3. Preview a stack. $stack = $orchestrationService->previewStack(array( 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'timeout_mins' => 3 + 'template' => file_get_contents(__DIR__ . '/sample_template.yml') )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From f98fad93382ce49c873464e5bb0c758bc9913cac Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 16:31:56 -0700 Subject: [PATCH 047/835] Validating a template does not return a value. --- samples/Orchestration/validate-template-from-file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-file.php index f21472a49..6e4b07711 100644 --- a/samples/Orchestration/validate-template-from-file.php +++ b/samples/Orchestration/validate-template-from-file.php @@ -38,6 +38,6 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from file. -$stack = $orchestrationService->validateTemplate(array( +$orchestrationService->validateTemplate(array( 'template' => file_get_contents(__DIR__ . '/sample_template.yml') )); From 96f924c132b438799d964981f4078304644c160e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 17:03:38 -0700 Subject: [PATCH 048/835] Starting to flesh out user guide. --- docs/userguide/Orchestration/USERGUIDE.md | 139 +++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 087f653b7..4a388b02a 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -14,6 +14,7 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R * If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. ```php + ', array( @@ -25,6 +26,7 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R * If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. ```php + orchestrationService(null, $region); ``` -In the example above, you are connecting to the ``DFW`` region of the cloud. Any resources and stacks created with this `$orchestrationService` instance will be created in that cloud region. +In the example above, you are connecting to the ``DFW`` region of the cloud. Any +resources and stacks created with this `$orchestrationService` instance will be +created in that cloud region. ## Templates +An Orchestration template is a JSON or YAML document that +describes how a set of resources should be assembled to produce a working +deployment (known as a [stack](#stacks)). The template specifies what resources +should be used, what attributes of these resources are parameterized and what +information is output to the user when a template is instantiated. + ### Validate Template +Prior to actually _using_ a template to create a stack, you may want to validate it. + #### Validate Template from File +If your template is stored on your local computer as a JSON or YAML file, you +can validate it as shown below: + +```php +validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/sample_template.yml') +)); +``` + +[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-url.php) ] + +If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseException`](http://api.guzzlephp.org/class-Guzzle.Http.Exception.ClientErrorResponseException.html) will be thrown. + #### Validate Template from URL +If your template is stored in a remote location accessible via HTTP or HTTPS, +as a JSON or YAML file, you can validate it as shown below: + +```php +validateTemplate(array( + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' +)); +``` + +[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-url.php) ] + +If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseException`](http://api.guzzlephp.org/class-Guzzle.Http.Exception.ClientErrorResponseException.html) will be thrown. ## Stacks +A stack is a running instance of a template. When a stack is created, the +resources specified in the template are created. ### Preview Stack +Before _actually_ creating a stack from a template, you may want to simply +get a glimpse of what that stack might look like. This is called previewing the +stack. + +This operation takes the following parameters: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `my-drupal-web-site` | +| `template` | Template contents | String, JSON or YAML | No, if `template_url` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | +| `template_url` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | + +#### Preview Stack from Template File + +If your template is stored on your local computer as a JSON or YAML file, you +can use it to preview a stack as shown below: + +```php +previewStack(array( + 'stack_name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` + +[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-file.php) ] + +#### Preview Stack from Template URL + +If your template is stored in a remote location accessible via HTTP or HTTPS, +as a JSON or YAML file, you can use it to preview a stack as shown below: + +```php +previewStack(array( + 'stack_name' => 'my-drupal-web-site', + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` + +[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-url.php) ] + ### Create Stack +You can create a stack from a template. This operation takes the following parameters: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `my-drupal-web-site` | +| `template` | Template contents | String, JSON or YAML | No, if `template_url` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | +| `template_url` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | +| `timeout_mins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | | 5 | + #### Create Stack from Template File + +If your template is stored on your local computer as a JSON or YAML file, you +can use it to create a stack as shown below: + +```php +createStack(array( + 'stack_name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupaldbuser' + ), + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-file.php) ] + #### Create Stack from Template URL +If your template is stored in a remote location accessible via HTTP or HTTPS, +as a JSON or YAML file, you can use it to create a stack as shown below: + +```php +createStack(array( + 'stack_name' => 'my-drupal-web-site', + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupaldbuser' + ), + 'timeout_mins' => 5 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-url.php) ] + ### List Stacks ### Get Stack #### Get Stack by Name and ID From 75e1e36d182c52da06f1bd92f4180fbe68ab36d7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 17:06:18 -0700 Subject: [PATCH 049/835] Removing unnecessary ', array( @@ -26,7 +25,6 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R * If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. ```php - orchestrationService(null, $region); ``` @@ -63,7 +60,6 @@ If your template is stored on your local computer as a JSON or YAML file, you can validate it as shown below: ```php -validateTemplate(array( 'template' => file_get_contents(__DIR__ . '/sample_template.yml') )); @@ -78,7 +74,6 @@ If your template is stored in a remote location accessible via HTTP or HTTPS, as a JSON or YAML file, you can validate it as shown below: ```php -validateTemplate(array( 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' )); @@ -112,7 +107,6 @@ If your template is stored on your local computer as a JSON or YAML file, you can use it to preview a stack as shown below: ```php -previewStack(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), @@ -129,7 +123,6 @@ If your template is stored in a remote location accessible via HTTP or HTTPS, as a JSON or YAML file, you can use it to preview a stack as shown below: ```php -previewStack(array( 'stack_name' => 'my-drupal-web-site', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' @@ -156,7 +149,6 @@ If your template is stored on your local computer as a JSON or YAML file, you can use it to create a stack as shown below: ```php -createStack(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), @@ -176,7 +168,6 @@ If your template is stored in a remote location accessible via HTTP or HTTPS, as a JSON or YAML file, you can use it to create a stack as shown below: ```php -createStack(array( 'stack_name' => 'my-drupal-web-site', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', From 364680aa5d8ee1dc5ca15ae277f1bf73ce0a15a8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 1 Oct 2014 18:23:49 -0700 Subject: [PATCH 050/835] Fleshing out stack operations, introduction and concepts in user guide. --- docs/userguide/Orchestration/USERGUIDE.md | 154 +++++++++++++++++++++- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 06ee7bcfc..5109f716b 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -1,10 +1,26 @@ # The Complete User Guide to the Orchestration Service -TODO: Intro +Orchestration is a service that can be used to create and manage cloud resources. +Examples of such resources are databases, load balancers, servers and software +installed on them. ## Concepts -TODO +To use the Orchestration service effectively, you should understand several key +concepts: + +* **Template**: An Orchestration template is a JSON or YAML document that +describes how a set of resources should be assembled to produce a working +deployment. The template specifies what resources should be used, what attributes +of these resources are parameterized and what information is output to the user +when a template is instantiated. + +* **Resource**: A resource is a template artifact that represents some component +of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a +load balancer, some configuration management system, and so forth). + +* **Stack**: A stack is a running instance of a template. When a stack is created, +the resources specified in the template are created. ## Prerequisites @@ -92,7 +108,7 @@ Before _actually_ creating a stack from a template, you may want to simply get a glimpse of what that stack might look like. This is called previewing the stack. -This operation takes the following parameters: +This operation takes one parameter, an associative array, with the following keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | @@ -133,7 +149,9 @@ $stack = $orchestrationService->previewStack(array( [ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-url.php) ] ### Create Stack -You can create a stack from a template. This operation takes the following parameters: +You can create a stack from a template. + +This operation takes one parameter, an associative array, with the following keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | @@ -183,15 +201,141 @@ $stack = $orchestrationService->createStack(array( [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-url.php) ] ### List Stacks +You can list all the stacks that you have created as shown below: + +```php +$stacks = $orchestrationService->listStacks(); +foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +} +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/list-stacks.php) ] + ### Get Stack -#### Get Stack by Name and ID +You can retrieve a specific stack either by its name OR by its name and ID. + +This operation takes the following positional parameters: + +| Position | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| 1 | Name of the stack | String | Yes | | `my-drupal-web-site` | +| 2 | ID of the stack | String | No | | `1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619` | + #### Get Stack by Name +```php +$stack = $orchestrationService->getStack('my-drupal-stack'); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name.php) ] + +#### Get Stack by Name and ID +```php +$stack = $orchestrationService->getStack('my-drupal-stack', '1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619'); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name-and-id.php) ] + ### Get Stack Template +You can retrieve the template used to create a stack. Please note that a JSON +string is returned, regardless of whether a JSON or YAML template was used to +create the stack. +```php +$stackTemplate = $stack->getTemplate(); +/** @var $stackTemplate string **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-template.php) ] + ### Update Stack +You can update a running stack. + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `template` | Template contents | String, JSON or YAML | No, if `template_url` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | +| `template_url` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | +| `timeout_mins` | Duration, in minutes, after which stack update should time out | Integer | Yes | | 5 | + +#### Update Stack from Template File + +If your template is stored on your local computer as a JSON or YAML file, you +can use it to update a stack as shown below: + +```php +$stack->update(array( + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupalweb' + ), + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-file.php) ] + +#### Update Stack from Template URL + +If your template is stored in a remote location accessible via HTTP or HTTPS, +as a JSON or YAML file, you can use it to update a stack as shown below: + +```php +$stack->update(array( + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupalweb' + ), + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-url.php) ] + ### Delete Stack + +If you no longer need a stack and all its resources, you can delete it as shown +below: + +```php +$stack->delete(); +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/delete-stack.php) ] + ### Abandon Stack + +If you want to delete the stack but preserve all its resources, you can abandon +it as shown below: + +```php +$abandonStackData = $stack->abandon(); +/** @var $abandonStackData string **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/abandon-stack.php) ] + +Note that this operation returns data about the abandoned stack as a string. This +data may be used to recreate the stack using the [Adopt Stack](#adopt-stack) +operation. + ### Adopt Stack +If you have data from an abandoned stack, you may adopt it and recreate the stack +as shown below: + +```php +$stack = $orchestrationService->adoptStack(array( + 'stack_name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'adopt_stack_data' => $abandonStackData, + 'timeout_mins' => 3 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/adopt-stack.php) ] + ## Stack Resources ### List stack Resources From a940c286d7cbaf62c409b7153b2fb86fb120d2db Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 2 Oct 2014 08:56:34 -0700 Subject: [PATCH 051/835] Link to stack resources section. --- docs/userguide/Orchestration/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 5109f716b..d55f287fe 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -101,7 +101,7 @@ If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseExcepti ## Stacks A stack is a running instance of a template. When a stack is created, the -resources specified in the template are created. +[resources](#stack-resources) specified in the template are created. ### Preview Stack Before _actually_ creating a stack from a template, you may want to simply From f6431aba4b4a526ef240732e46d52d4c372751d3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 3 Oct 2014 17:50:46 -0700 Subject: [PATCH 052/835] Fixing typo. --- docs/userguide/Orchestration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 282a6f3b2..a86d5c8aa 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -74,4 +74,4 @@ $stack = $orchestrationService->createStack(array( ## Next steps -Once you have created a stack, there is more you can do with it. See [complete user guide for load balancers](USERGUIDE.md). +Once you have created a stack, there is more you can do with it. See [complete user guide for orchestration](USERGUIDE.md). From 8d447850811e96f816cfda7108bc445956736ea5 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 3 Oct 2014 17:51:43 -0700 Subject: [PATCH 053/835] Fleshing out user guide on stack resources and stack resource events. --- docs/userguide/Orchestration/USERGUIDE.md | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index d55f287fe..4b7fbc42a 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -338,14 +338,66 @@ $stack = $orchestrationService->adoptStack(array( ## Stack Resources +A stack is made up of zero or more resources. Examples of resources are databases, +load balancers, servers or the software installed on servers. + ### List stack Resources + +You can list all the resources for a stack as shown below: + +```php +$stackResources = $stack->listResources(); +foreach ($stackResources as $stackResource) { + /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +} +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] + ### Get stack Resource +You can retrieve a specific resource in a stack using that resource's name, as shown +below: + +```php +// 4. Get resource in stack. +$stackResource = $stack->getResource('load-balancer'); +/** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] + ## Stack Resource Events +Operations on resources within a stack (such as creation of a resource) produce +events. ### List Stack Events +You can list all events for all resources in a stack as shown below: + +```php +$stackEvents = $stack->listEvents(); +foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +} +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] + ### List Stack Resource Events +You can list all events for a specific resource in a stack as shown below: +```php +$stackResourceEvents = $stackResource->listEvents(); +foreach ($stackResourceEvents as $stackResourceEvent) { + /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +} +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] + ### Get Stack Resource Event +You can retrieve a specific event for a specific resource in a stack, by using +the resource event's ID, as shown below: +```php +$stackResourceEvent = $stackResource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); +/** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] ## Resource Types From 8df4df27376c8fc593e1dafabf3fbf02ebd224c4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 3 Oct 2014 19:21:46 -0700 Subject: [PATCH 054/835] Fixing return type. --- samples/Orchestration/get-resource-type-template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/get-resource-type-template.php b/samples/Orchestration/get-resource-type-template.php index 06d4defb0..6190d2d8f 100644 --- a/samples/Orchestration/get-resource-type-template.php +++ b/samples/Orchestration/get-resource-type-template.php @@ -43,4 +43,4 @@ // 4. Get template for resource type. $resourceTypeTemplate = $resourceType->getTemplate(); -/** @var $resourceTypeTemplate OpenCloud\Orchestration\Resource\ResourceTypeTemplate **/ +/** @var $resourceTypeTemplate string **/ From 5bf073acd0eba7f86d150cd8fbc9bcdcd61072be Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 3 Oct 2014 19:22:00 -0700 Subject: [PATCH 055/835] Adding userguide sections on resource types and build info. --- docs/userguide/Orchestration/USERGUIDE.md | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 4b7fbc42a..5e86d59c0 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -400,11 +400,46 @@ $stackResourceEvent = $stackResource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7 [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] ## Resource Types +When defining a template, resource types supported by your Cloud must be used. ### List Resource Types +You can list all supported resource types as shown below: + +```php +$resourceTypes = $orchestrationService->listResourceTypes(); +foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ +} +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/list-resource-types.php) ] + ### Get Resource Type +You can retrieve a specific resource type's schema as shown below: + +```php +$resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); +/** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type.php) ] + ### Get Resource Type Template +You can retrieve a specific resource type's representation as it would appear +in a template, as shown below: + +```php +$resourceTypeTemplate = $resourceType->getTemplate(); +/** @var $resourceTypeTemplate string **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type-template.php) ] ## Build Info ### Get Build Info +You can retrieve information about the current Orchestration service build as +shown below: + +```php +$buildInfo = $orchestrationService->getBuildInfo(); +/** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-build-info.php) ] From 3d3148eb16b9ee8c9d6f8e335a026733bc2811af Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 3 Oct 2014 19:22:17 -0700 Subject: [PATCH 056/835] Consistent formatting. --- docs/userguide/Orchestration/USERGUIDE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 5e86d59c0..be8441d5c 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -222,6 +222,7 @@ This operation takes the following positional parameters: | 2 | ID of the stack | String | No | | `1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619` | #### Get Stack by Name + ```php $stack = $orchestrationService->getStack('my-drupal-stack'); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ @@ -229,6 +230,7 @@ $stack = $orchestrationService->getStack('my-drupal-stack'); [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name.php) ] #### Get Stack by Name and ID + ```php $stack = $orchestrationService->getStack('my-drupal-stack', '1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619'); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ @@ -239,6 +241,7 @@ $stack = $orchestrationService->getStack('my-drupal-stack', '1ded0b2a-36b6-4f7b- You can retrieve the template used to create a stack. Please note that a JSON string is returned, regardless of whether a JSON or YAML template was used to create the stack. + ```php $stackTemplate = $stack->getTemplate(); /** @var $stackTemplate string **/ @@ -382,6 +385,7 @@ foreach ($stackEvents as $stackEvent) { ### List Stack Resource Events You can list all events for a specific resource in a stack as shown below: + ```php $stackResourceEvents = $stackResource->listEvents(); foreach ($stackResourceEvents as $stackResourceEvent) { @@ -393,6 +397,7 @@ foreach ($stackResourceEvents as $stackResourceEvent) { ### Get Stack Resource Event You can retrieve a specific event for a specific resource in a stack, by using the resource event's ID, as shown below: + ```php $stackResourceEvent = $stackResource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ From 7351686e45910475d9c3bf5a319aaafdf31a93cc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 10:04:38 -0700 Subject: [PATCH 057/835] Changing stack creation syntax. --- docs/userguide/Orchestration/README.md | 3 ++- docs/userguide/Orchestration/USERGUIDE.md | 8 ++++---- samples/Orchestration/create-stack-from-template-file.php | 4 ++-- samples/Orchestration/create-stack-from-template-url.php | 4 ++-- samples/Orchestration/quickstart.php | 3 ++- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index a86d5c8aa..0c5674526 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -58,7 +58,8 @@ In the example above, you are connecting to the ``DFW`` region of the cloud. Any ### 3. Create a stack from a template. ```php -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'Cloud server with attached block storage', 'template_url' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', 'parameters' => array( diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index be8441d5c..50abb65d4 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -167,7 +167,8 @@ If your template is stored on your local computer as a JSON or YAML file, you can use it to create a stack as shown below: ```php -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'parameters' => array( @@ -177,7 +178,6 @@ $stack = $orchestrationService->createStack(array( ), 'timeout_mins' => 3 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-file.php) ] @@ -186,7 +186,8 @@ If your template is stored in a remote location accessible via HTTP or HTTPS, as a JSON or YAML file, you can use it to create a stack as shown below: ```php -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'my-drupal-web-site', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( @@ -196,7 +197,6 @@ $stack = $orchestrationService->createStack(array( ), 'timeout_mins' => 5 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-url.php) ] diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index fe3a5ce55..816d846a5 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -38,9 +38,9 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'timeout_mins' => 3 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 8ee90eba9..34c805502 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -38,7 +38,8 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'my-drupal-web-site', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( @@ -48,4 +49,3 @@ ), 'timeout_mins' => 5 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index 095d11531..4e14d079a 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -37,7 +37,8 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stack = $orchestrationService->createStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'Cloud server with attached block storage', 'template_url' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', 'parameters' => array( From a0c3609f3ef954c47005267fa663500a6f75d870 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 10:07:31 -0700 Subject: [PATCH 058/835] Changing preview stack syntax. --- docs/userguide/Orchestration/USERGUIDE.md | 4 ++-- samples/Orchestration/preview-stack.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 50abb65d4..677b07559 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -123,12 +123,12 @@ If your template is stored on your local computer as a JSON or YAML file, you can use it to preview a stack as shown below: ```php -$stack = $orchestrationService->previewStack(array( +$stack = $orchestrationService->stack(); +$stack->preview(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'timeout_mins' => 3 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-file.php) ] diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php index 7968d9942..8680bbf9d 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack.php @@ -38,8 +38,8 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Preview a stack. -$stack = $orchestrationService->previewStack(array( +$stack = $orchestrationService->stack(); +$stack->preview(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml') )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From 24e3469217e2073ac4706b11394495b7d0441d17 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 10:43:39 -0700 Subject: [PATCH 059/835] Changing adopt stack syntax. --- docs/userguide/Orchestration/USERGUIDE.md | 3 ++- samples/Orchestration/adopt-stack.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 677b07559..03b986043 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -329,7 +329,8 @@ If you have data from an abandoned stack, you may adopt it and recreate the stac as shown below: ```php -$stack = $orchestrationService->adoptStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'adopt_stack_data' => $abandonStackData, diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index 924bedeea..d90f59de5 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -38,7 +38,8 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Adopt a stack. -$stack = $orchestrationService->adoptStack(array( +$stack = $orchestrationService->stack(); +$stack->create(array( 'stack_name' => 'my-drupal-web-site', 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), 'adopt_stack_data' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), From 0baa704392a968c435713dc4978cde352b852e65 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 12:29:48 -0700 Subject: [PATCH 060/835] Refactoring method by pulling it up to make it more widely available. --- .../Common/Resource/PersistentResource.php | 16 ++++++++++++++++ .../LoadBalancer/Resource/AbstractResource.php | 15 --------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index cb0ed8dc2..328db372a 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -132,6 +132,22 @@ public function refresh($id = null, $url = null) return $response; } + + /** + * Causes resource to refresh based on parent's URL + */ + protected function refreshFromParent() + { + $url = clone $this->getParent()->getUrl(); + $url->addPath($this->resourceName()); + + $response = $this->getClient()->get($url)->send(); + + if (null !== ($decoded = $this->parseResponse($response))) { + $this->populate($decoded); + } + } + /** * Given a `location` URL, refresh this resource * diff --git a/lib/OpenCloud/LoadBalancer/Resource/AbstractResource.php b/lib/OpenCloud/LoadBalancer/Resource/AbstractResource.php index feb1c446f..42521326f 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/AbstractResource.php +++ b/lib/OpenCloud/LoadBalancer/Resource/AbstractResource.php @@ -40,21 +40,6 @@ public function refresh($id = null, $url = null) } } - /** - * Causes resource to refresh based on parent's URL - */ - protected function refreshFromParent() - { - $url = clone $this->getParent()->getUrl(); - $url->addPath($this->resourceName()); - - $response = $this->getClient()->get($url)->send(); - - if (null !== ($decoded = $this->parseResponse($response))) { - $this->populate($decoded); - } - } - protected function createJson() { $object = new \stdClass; From a45cd6fb2a90e0258921edb6f9da959e4a9049b1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 12:32:03 -0700 Subject: [PATCH 061/835] Fleshing out Orchestration service class. --- .../Orchestration/Resource/BuildInfo.php | 36 ++ .../Orchestration/Resource/ResourceType.php | 52 +++ .../Orchestration/Resource/Stack.php | 31 ++ .../Orchestration/Resource/StackResource.php | 27 ++ .../Resource/StackResourceEvent.php | 27 ++ lib/OpenCloud/Orchestration/Service.php | 67 ++- .../Orchestration/OrchestrationTestCase.php | 34 ++ .../Tests/Orchestration/ServiceTest.php | 110 +++++ tests/OpenCloud/Tests/_response/Auth.resp | 385 +++++++++++++++++- 9 files changed, 766 insertions(+), 3 deletions(-) create mode 100644 lib/OpenCloud/Orchestration/Resource/BuildInfo.php create mode 100644 lib/OpenCloud/Orchestration/Resource/ResourceType.php create mode 100644 lib/OpenCloud/Orchestration/Resource/Stack.php create mode 100644 lib/OpenCloud/Orchestration/Resource/StackResource.php create mode 100644 lib/OpenCloud/Orchestration/Resource/StackResourceEvent.php create mode 100644 tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php create mode 100644 tests/OpenCloud/Tests/Orchestration/ServiceTest.php diff --git a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php new file mode 100644 index 000000000..fc501d826 --- /dev/null +++ b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php @@ -0,0 +1,36 @@ +refreshFromParent(); + } +} diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php new file mode 100644 index 000000000..68fc6be13 --- /dev/null +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -0,0 +1,52 @@ +properties. + */ + protected function setProperties($properties) + { + $this->_properties = $properties; + } + + public function getProperties() + { + return $this->_properties; + } + + public function getResourceType() + { + return $this->resource_type; + } +} diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php new file mode 100644 index 000000000..02ede4049 --- /dev/null +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -0,0 +1,31 @@ +resource('Stack', $id); + } + + /** + * Returns a Stack object associated with this Orchestration service + * + * @param string $id - the stack with the ID is retrieved + * @returns Stack object + */ + public function getStack($id) + { + return $this->stack($id); + } + + /** + * Returns a list of stacks you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listStacks(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Stack::resourceName())->setQuery($params); + + return $this->resourceList('Stack', $url); + } + + /** + * Returns a list of resource types available + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listResourceTypes(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(ResourceType::resourceName())->setQuery($params); + + return $this->resourceList('ResourceType', $url); + } + + /** + * Returns a ResourceType object associated with this Orchestration service + * + * @param string $id - the resource type with the ID is retrieved + * @returns ResourceType object + */ + public function getResourceType($id) + { + return $this->resource('ResourceType', $id); + } + + /** + * Returns a BuildInfo object associated with this Orchestration service + * + * @returns BuildInfo object + */ + public function getBuildInfo() + { + $buildInfo = $this->resource('BuildInfo'); + $buildInfo->refresh(); + return $buildInfo; } /** diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php new file mode 100644 index 000000000..87730ad53 --- /dev/null +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -0,0 +1,34 @@ +service = $this->getClient()->orchestrationService(); + } +} diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php new file mode 100644 index 000000000..3343bd280 --- /dev/null +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -0,0 +1,110 @@ + + */ +namespace OpenCloud\Tests\Orchestration; + +use OpenCloud\Orchestration\Service; + +class ServiceTest extends OrchestrationTestCase +{ + + public function test__construct() + { + $service = $this->getClient()->orchestrationService(null, 'DFW'); + $this->assertInstanceOf('OpenCloud\Orchestration\Service', $service); + } + + public function testCreateStack() + { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->stack()); + } + + public function testListStacks() + { + $this->addMockSubscriber($this->makeResponse('{"stacks":[{"description":"HEAT template for deploying a multi-node wordpress deployment on Rackspace Cloud\nusing Cloud Servers, Cloud Load Balancers and Cloud Databases. This version uses\na user-defined template resource to specify the implementation of the web-heads\n","links":[{"href":"http:\/\/xxxxx\/v1\/xxxx\/stacks\/xxxx\/11cd9b5e-c7ff-43b5-bff8-b0e7429cd87e","rel":"self"}],"stack_status_reason":"Resource suspend failed: Error: State invalid for suspend","stack_name":"timswp6","creation_time":"2014-01-30T20:47:57Z","updated_time":"2014-02-03T18:04:39Z","stack_status":"SUSPEND_FAILED","id":"11cd9b5e-c7ff-43b5-bff8-b0e7429cd87e"},{"description":"HEAT template for deploying a multi-node wordpress deployment on Rackspace Cloud\nusing Cloud Servers, Cloud Load Balancers and Cloud Databases. This version uses\na user-defined template resource to specify the implementation of the web-heads\n","links":[{"href":"http:\/\/xxxx\/v1\/xxxx\/stacks\/xxxx\/1b2ed5de-9b8c-43fa-9392-1da17b5dee7c","rel":"self"}],"stack_status_reason":"Stack create completed successfully","stack_name":"timswp5","creation_time":"2014-01-30T18:18:12Z","updated_time":"2014-01-30T18:42:07Z","stack_status":"CREATE_COMPLETE","id":"1b2ed5de-9b8c-43fa-9392-1da17b5dee7c"}]}')); + + $stacks = $this->service->listStacks(); + $this->assertInstanceOf(self::COLLECTION_CLASS, $stacks); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stacks->getElement(0)); + } + + public function testGetStackByName() + { + $this->addMockSubscriber($this->makeResponse('{"stack":{"disable_rollback":true,"description":"MYSQL server cloud database instance running on Rackspace cloud","parameters":{"instance_name":"testdb","OS::stack_id":"87xxx21-9db9-49be-bc4f-a7f2exxxx68","OS::stack_name":"trove2"},"stack_status_reason":"Stack create completed successfully","stack_name":"trove2","outputs":[],"creation_time":"2014-02-05T19:18:56Z","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxx21-9db9-49be-bc4f-a7f2exxxx68","rel":"self"}],"capabilities":[],"notification_topics":[],"timeout_mins":60,"stack_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","id":"879xxx21-9db9-49be-bc4f-a7f2exxxx9068","template_description":"MYSQL server cloud database instance running on Rackspace cloud"}}')); + + $stack = $this->service->getStack('foobar'); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stack); + $this->assertEquals('MYSQL server cloud database instance running on Rackspace cloud', $stack->getDescription()); + } + + public function testGetStackByNameAndId() + { + $this->addMockSubscriber($this->makeResponse('{"stack":{"disable_rollback":true,"description":"MYSQL server cloud database instance running on Rackspace cloud","parameters":{"instance_name":"testdb","OS::stack_id":"87xxx21-9db9-49be-bc4f-a7f2exxxx68","OS::stack_name":"trove2"},"stack_status_reason":"Stack create completed successfully","stack_name":"trove2","outputs":[],"creation_time":"2014-02-05T19:18:56Z","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxx21-9db9-49be-bc4f-a7f2exxxx68","rel":"self"}],"capabilities":[],"notification_topics":[],"timeout_mins":60,"stack_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","id":"879xxx21-9db9-49be-bc4f-a7f2exxxx9068","template_description":"MYSQL server cloud database instance running on Rackspace cloud"}}')); + + $stack = $this->service->getStack('foobar', 1234); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stack); + $this->assertEquals('MYSQL server cloud database instance running on Rackspace cloud', $stack->getDescription()); + } + + public function testListResourceTypes() + { + $this->addMockSubscriber($this->makeResponse('{"resource_types":["OS::Nova::Server","OS::Heat::RandomString","OS::Swift::Container","Rackspace::Cloud::Server","OS::Heat::ChefSolo","Rackspace::AutoScale::WebHook","Rackspace::AutoScale::Group","Rackspace::Cloud::Network","OS::Cinder::Volume","Rackspace::Cloud::WinServer","Rackspace::Cloud::LoadBalancer","OS::Heat::ResourceGroup","Rackspace::AutoScale::ScalingPolicy","Rackspace::Cloud::DNS","OS::Trove::Instance","OS::Nova::FloatingIPAssociation","OS::Cinder::VolumeAttachment","OS::Nova::FloatingIP","OS::Nova::KeyPair"]}')); + + $resourceTypes = $this->service->listResourceTypes(); + $this->assertInstanceOf(self::COLLECTION_CLASS, $resourceTypes); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $resourceTypes->getElement(0)); + } + + public function testGetResourceType() + { + $this->addMockSubscriber($this->makeResponse('{"attributes":{"an_attribute":{"description":"An attribute description ."}},"properties":{"a_property":{"update_allowed":false,"required":true,"type":"string","description":"A resource description."}},"resource_type":"OS::Nova::Server"}')); + + $resourceType = $this->service->getResourceType('OS::Nova::Server'); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $resourceType); + $this->assertEquals('OS::Nova::Server', $resourceType->getResourceType()); + } + + public function testGetBuildInfo() + { + $this->addMockSubscriber($this->makeResponse('{"engine":{"revision":"2014.j3-20141003-1139"},"fusion-api":{"revision":"j1-20140915-10d9ee4-98"},"api":{"revision":"2014.j3-20141003-1139"}}')); + + $buildInfo = $this->service->getBuildInfo(); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\BuildInfo', $buildInfo); + $this->assertEquals('2014.j3-20141003-1139', $buildInfo->getApi()->revision); + } + + +/** + public function testValidateValidTemplate() + { + $this->assertTrue($this->service->validateTemplate(array('template' => 'heat_template_version: 2013-05-23'))); + } + + public function testValidateInvalidTemplate() + { + $this->assertFalse($this->service->validateTemplate(array('template' => 'foobar: baz'))); + } + +**/ +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/_response/Auth.resp b/tests/OpenCloud/Tests/_response/Auth.resp index d0f978eba..a6ff3fb5e 100644 --- a/tests/OpenCloud/Tests/_response/Auth.resp +++ b/tests/OpenCloud/Tests/_response/Auth.resp @@ -8,4 +8,387 @@ vary: Accept, Accept-Encoding, X-Auth-Token VIA: 1.0 Repose (Repose/2.3.5) Front-End-Https: on -{"access":{"token":{"id":"cc81a27bf50249e5b5878d8b51f2eea6","expires":"2013-11-13T10:49:29.409Z","tenant":{"id":"123456","name":"123456"},"RAX-AUTH:authenticatedBy":["APIKEY"]},"serviceCatalog":[{"name":"cloudFilesCDN","endpoints":[{"region":"ORD","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/cdn2.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"DFW","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/cdn1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"SYD","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/cdn4.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"IAD","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/cdn5.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"HKG","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/cdn6.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"}],"type":"rax:object-cdn"},{"name":"cloudFiles","endpoints":[{"region":"ORD","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/storage101.ord1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","internalURL":"https:\/\/snet-storage101.ord1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"SYD","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/storage101.syd2.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","internalURL":"https:\/\/snet-storage101.syd2.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"DFW","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/storage101.dfw1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","internalURL":"https:\/\/snet-storage101.dfw1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"IAD","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/storage101.iad3.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","internalURL":"https:\/\/snet-storage101.iad3.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"},{"region":"HKG","tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","publicURL":"https:\/\/storage101.hkg1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","internalURL":"https:\/\/snet-storage101.hkg1.clouddrive.com\/v1\/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa"}],"type":"object-store"},{"name":"cloudLoadBalancers","endpoints":[{"region":"DFW","tenantId":"123456","publicURL":"https:\/\/dfw.loadbalancers.api.rackspacecloud.com\/v1.0\/123456"},{"region":"ORD","tenantId":"123456","publicURL":"https:\/\/ord.loadbalancers.api.rackspacecloud.com\/v1.0\/123456"},{"region":"SYD","tenantId":"123456","publicURL":"https:\/\/syd.loadbalancers.api.rackspacecloud.com\/v1.0\/123456"},{"region":"IAD","tenantId":"123456","publicURL":"https:\/\/iad.loadbalancers.api.rackspacecloud.com\/v1.0\/123456"},{"region":"HKG","tenantId":"123456","publicURL":"https:\/\/hkg.loadbalancers.api.rackspacecloud.com\/v1.0\/123456"}],"type":"rax:load-balancer"},{"name": "cloudImages","endpoints": [{"region": "IAD","tenantId": "123456","publicURL": "https://iad.images.api.rackspacecloud.com/v2/123456"}],"type": "image"},{"name":"autoscale","endpoints":[{"region":"ORD","tenantId":"123456","publicURL":"https:\/\/ord.autoscale.api.rackspacecloud.com\/v1.0\/123456"},{"region":"DFW","tenantId":"123456","publicURL":"https:\/\/dfw.autoscale.api.rackspacecloud.com\/v1.0\/123456"}],"type":"rax:autoscale"},{"name":"cloudBlockStorage","endpoints":[{"region":"DFW","tenantId":"123456","publicURL":"https:\/\/dfw.blockstorage.api.rackspacecloud.com\/v1\/123456"},{"region":"ORD","tenantId":"123456","publicURL":"https:\/\/ord.blockstorage.api.rackspacecloud.com\/v1\/123456"},{"region":"IAD","tenantId":"123456","publicURL":"https:\/\/iad.blockstorage.api.rackspacecloud.com\/v1\/123456"},{"region":"SYD","tenantId":"123456","publicURL":"https:\/\/syd.blockstorage.api.rackspacecloud.com\/v1\/123456"},{"region":"HKG","tenantId":"123456","publicURL":"https:\/\/hkg.blockstorage.api.rackspacecloud.com\/v1\/123456"}],"type":"volume"},{"name":"cloudMonitoring","endpoints":[{"tenantId":"123456","publicURL":"https:\/\/monitoring.api.rackspacecloud.com\/v1.0\/123456"}],"type":"rax:monitor"},{"name":"cloudServersOpenStack","endpoints":[{"region":"ORD","tenantId":"123456","publicURL":"https:\/\/ord.servers.api.rackspacecloud.com\/v2\/123456","versionInfo":"https:\/\/ord.servers.api.rackspacecloud.com\/v2","versionList":"https:\/\/ord.servers.api.rackspacecloud.com\/","versionId":"2"},{"region":"DFW","tenantId":"123456","publicURL":"https:\/\/dfw.servers.api.rackspacecloud.com\/v2\/123456","versionInfo":"https:\/\/dfw.servers.api.rackspacecloud.com\/v2","versionList":"https:\/\/dfw.servers.api.rackspacecloud.com\/","versionId":"2"},{"region":"SYD","tenantId":"123456","publicURL":"https:\/\/syd.servers.api.rackspacecloud.com\/v2\/123456","versionInfo":"https:\/\/syd.servers.api.rackspacecloud.com\/v2","versionList":"https:\/\/syd.servers.api.rackspacecloud.com\/","versionId":"2"},{"region":"IAD","tenantId":"123456","publicURL":"https:\/\/iad.servers.api.rackspacecloud.com\/v2\/123456","versionInfo":"https:\/\/iad.servers.api.rackspacecloud.com\/v2","versionList":"https:\/\/iad.servers.api.rackspacecloud.com\/","versionId":"2"},{"region":"HKG","tenantId":"123456","publicURL":"https:\/\/hkg.servers.api.rackspacecloud.com\/v2\/123456","versionInfo":"https:\/\/hkg.servers.api.rackspacecloud.com\/v2","versionList":"https:\/\/hkg.servers.api.rackspacecloud.com\/","versionId":"2"}],"type":"compute"},{"name":"cloudQueues","endpoints":[{"region":"DFW","tenantId":"123456","publicURL":"https:\/\/dfw.queues.api.rackspacecloud.com\/v1\/123456","internalURL":"https:\/\/snet-dfw.queues.api.rackspacecloud.com\/v1\/123456"},{"region":"ORD","tenantId":"123456","publicURL":"https:\/\/ord.queues.api.rackspacecloud.com\/v1\/123456","internalURL":"https:\/\/snet-ord.queues.api.rackspacecloud.com\/v1\/123456"},{"region":"SYD","tenantId":"123456","publicURL":"https:\/\/syd.queues.api.rackspacecloud.com\/v1\/123456","internalURL":"https:\/\/snet-syd.queues.api.rackspacecloud.com\/v1\/123456"},{"region":"IAD","tenantId":"123456","publicURL":"https:\/\/iad.queues.api.rackspacecloud.com\/v1\/123456","internalURL":"https:\/\/snet-iad.queues.api.rackspacecloud.com\/v1\/123456"},{"region":"HKG","tenantId":"123456","publicURL":"https:\/\/hkg.queues.api.rackspacecloud.com\/v1\/123456","internalURL":"https:\/\/snet-hkg.queues.api.rackspacecloud.com\/v1\/123456"}],"type":"rax:queues"},{"name":"cloudDatabases","endpoints":[{"region":"SYD","tenantId":"123456","publicURL":"https:\/\/syd.databases.api.rackspacecloud.com\/v1.0\/123456"},{"region":"ORD","tenantId":"123456","publicURL":"https:\/\/ord.databases.api.rackspacecloud.com\/v1.0\/123456"},{"region":"DFW","tenantId":"123456","publicURL":"https:\/\/dfw.databases.api.rackspacecloud.com\/v1.0\/123456"},{"region":"IAD","tenantId":"123456","publicURL":"https:\/\/iad.databases.api.rackspacecloud.com\/v1.0\/123456"},{"region":"HKG","tenantId":"123456","publicURL":"https:\/\/hkg.databases.api.rackspacecloud.com\/v1.0\/123456"}],"type":"rax:database"},{"name":"cloudBackup","endpoints":[{"tenantId":"123456","publicURL":"https:\/\/backup.api.rackspacecloud.com\/v1.0\/123456"}],"type":"rax:backup"},{"name":"cloudServers","endpoints":[{"tenantId":"123456","publicURL":"https:\/\/servers.api.rackspacecloud.com\/v1.0\/123456","versionInfo":"https:\/\/servers.api.rackspacecloud.com\/v1.0","versionList":"https:\/\/servers.api.rackspacecloud.com\/","versionId":"1.0"}],"type":"compute"},{"name":"cloudDNS","endpoints":[{"tenantId":"123456","publicURL":"https:\/\/dns.api.rackspacecloud.com\/v1.0\/123456"}],"type":"rax:dns"}],"user":{"id":"10048286","roles":[{"id":"10000150","description":"Checkmate Access role","name":"checkmate"},{"id":"3","description":"User Admin Role.","name":"identity:user-admin"},{"tenantId":"MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa","id":"5","description":"A Role that allows a user access to keystone Service methods","name":"object-store:default"},{"tenantId":"123456","id":"6","description":"A Role that allows a user access to keystone Service methods","name":"compute:default"}],"name":"jamiehannaford1","RAX-AUTH:defaultRegion":"DFW"}}} \ No newline at end of file +{ + "access": { + "serviceCatalog": [ + { + "endpoints": [ + { + "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "ORD", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "publicURL": "https://cdn1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "DFW", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "publicURL": "https://cdn4.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "SYD", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "publicURL": "https://cdn5.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "IAD", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "publicURL": "https://cdn6.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "HKG", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + } + ], + "name": "cloudFilesCDN", + "type": "rax:object-cdn" + }, + { + "endpoints": [ + { + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "ORD", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "internalURL": "https://snet-storage101.syd2.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://storage101.syd2.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "SYD", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "internalURL": "https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "DFW", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "internalURL": "https://snet-storage101.iad3.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://storage101.iad3.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "IAD", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "internalURL": "https://snet-storage101.hkg1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://storage101.hkg1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "region": "HKG", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + } + ], + "name": "cloudFiles", + "type": "object-store" + }, + { + "endpoints": [ + { + "publicURL": "https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456", + "region": "DFW", + "tenantId": "123456" + }, + { + "publicURL": "https://ord.loadbalancers.api.rackspacecloud.com/v1.0/123456", + "region": "ORD", + "tenantId": "123456" + }, + { + "publicURL": "https://syd.loadbalancers.api.rackspacecloud.com/v1.0/123456", + "region": "SYD", + "tenantId": "123456" + }, + { + "publicURL": "https://iad.loadbalancers.api.rackspacecloud.com/v1.0/123456", + "region": "IAD", + "tenantId": "123456" + }, + { + "publicURL": "https://hkg.loadbalancers.api.rackspacecloud.com/v1.0/123456", + "region": "HKG", + "tenantId": "123456" + } + ], + "name": "cloudLoadBalancers", + "type": "rax:load-balancer" + }, + { + "endpoints": [ + { + "publicURL": "https://iad.images.api.rackspacecloud.com/v2/123456", + "region": "IAD", + "tenantId": "123456" + } + ], + "name": "cloudImages", + "type": "image" + }, + { + "endpoints": [ + { + "publicURL": "https://ord.autoscale.api.rackspacecloud.com/v1.0/123456", + "region": "ORD", + "tenantId": "123456" + }, + { + "publicURL": "https://dfw.autoscale.api.rackspacecloud.com/v1.0/123456", + "region": "DFW", + "tenantId": "123456" + } + ], + "name": "autoscale", + "type": "rax:autoscale" + }, + { + "endpoints": [ + { + "publicURL": "https://dfw.blockstorage.api.rackspacecloud.com/v1/123456", + "region": "DFW", + "tenantId": "123456" + }, + { + "publicURL": "https://ord.blockstorage.api.rackspacecloud.com/v1/123456", + "region": "ORD", + "tenantId": "123456" + }, + { + "publicURL": "https://iad.blockstorage.api.rackspacecloud.com/v1/123456", + "region": "IAD", + "tenantId": "123456" + }, + { + "publicURL": "https://syd.blockstorage.api.rackspacecloud.com/v1/123456", + "region": "SYD", + "tenantId": "123456" + }, + { + "publicURL": "https://hkg.blockstorage.api.rackspacecloud.com/v1/123456", + "region": "HKG", + "tenantId": "123456" + } + ], + "name": "cloudBlockStorage", + "type": "volume" + }, + { + "endpoints": [ + { + "publicURL": "https://monitoring.api.rackspacecloud.com/v1.0/123456", + "tenantId": "123456" + } + ], + "name": "cloudMonitoring", + "type": "rax:monitor" + }, + { + "endpoints": [ + { + "publicURL": "https://ord.servers.api.rackspacecloud.com/v2/123456", + "region": "ORD", + "tenantId": "123456", + "versionId": "2", + "versionInfo": "https://ord.servers.api.rackspacecloud.com/v2", + "versionList": "https://ord.servers.api.rackspacecloud.com/" + }, + { + "publicURL": "https://dfw.servers.api.rackspacecloud.com/v2/123456", + "region": "DFW", + "tenantId": "123456", + "versionId": "2", + "versionInfo": "https://dfw.servers.api.rackspacecloud.com/v2", + "versionList": "https://dfw.servers.api.rackspacecloud.com/" + }, + { + "publicURL": "https://syd.servers.api.rackspacecloud.com/v2/123456", + "region": "SYD", + "tenantId": "123456", + "versionId": "2", + "versionInfo": "https://syd.servers.api.rackspacecloud.com/v2", + "versionList": "https://syd.servers.api.rackspacecloud.com/" + }, + { + "publicURL": "https://iad.servers.api.rackspacecloud.com/v2/123456", + "region": "IAD", + "tenantId": "123456", + "versionId": "2", + "versionInfo": "https://iad.servers.api.rackspacecloud.com/v2", + "versionList": "https://iad.servers.api.rackspacecloud.com/" + }, + { + "publicURL": "https://hkg.servers.api.rackspacecloud.com/v2/123456", + "region": "HKG", + "tenantId": "123456", + "versionId": "2", + "versionInfo": "https://hkg.servers.api.rackspacecloud.com/v2", + "versionList": "https://hkg.servers.api.rackspacecloud.com/" + } + ], + "name": "cloudServersOpenStack", + "type": "compute" + }, + { + "endpoints": [ + { + "internalURL": "https://snet-dfw.queues.api.rackspacecloud.com/v1/123456", + "publicURL": "https://dfw.queues.api.rackspacecloud.com/v1/123456", + "region": "DFW", + "tenantId": "123456" + }, + { + "internalURL": "https://snet-ord.queues.api.rackspacecloud.com/v1/123456", + "publicURL": "https://ord.queues.api.rackspacecloud.com/v1/123456", + "region": "ORD", + "tenantId": "123456" + }, + { + "internalURL": "https://snet-syd.queues.api.rackspacecloud.com/v1/123456", + "publicURL": "https://syd.queues.api.rackspacecloud.com/v1/123456", + "region": "SYD", + "tenantId": "123456" + }, + { + "internalURL": "https://snet-iad.queues.api.rackspacecloud.com/v1/123456", + "publicURL": "https://iad.queues.api.rackspacecloud.com/v1/123456", + "region": "IAD", + "tenantId": "123456" + }, + { + "internalURL": "https://snet-hkg.queues.api.rackspacecloud.com/v1/123456", + "publicURL": "https://hkg.queues.api.rackspacecloud.com/v1/123456", + "region": "HKG", + "tenantId": "123456" + } + ], + "name": "cloudQueues", + "type": "rax:queues" + }, + { + "endpoints": [ + { + "publicURL": "https://syd.databases.api.rackspacecloud.com/v1.0/123456", + "region": "SYD", + "tenantId": "123456" + }, + { + "publicURL": "https://ord.databases.api.rackspacecloud.com/v1.0/123456", + "region": "ORD", + "tenantId": "123456" + }, + { + "publicURL": "https://dfw.databases.api.rackspacecloud.com/v1.0/123456", + "region": "DFW", + "tenantId": "123456" + }, + { + "publicURL": "https://iad.databases.api.rackspacecloud.com/v1.0/123456", + "region": "IAD", + "tenantId": "123456" + }, + { + "publicURL": "https://hkg.databases.api.rackspacecloud.com/v1.0/123456", + "region": "HKG", + "tenantId": "123456" + } + ], + "name": "cloudDatabases", + "type": "rax:database" + }, + { + "endpoints": [ + { + "publicURL": "https://backup.api.rackspacecloud.com/v1.0/123456", + "tenantId": "123456" + } + ], + "name": "cloudBackup", + "type": "rax:backup" + }, + { + "endpoints": [ + { + "publicURL": "https://servers.api.rackspacecloud.com/v1.0/123456", + "tenantId": "123456", + "versionId": "1.0", + "versionInfo": "https://servers.api.rackspacecloud.com/v1.0", + "versionList": "https://servers.api.rackspacecloud.com/" + } + ], + "name": "cloudServers", + "type": "compute" + }, + { + "endpoints": [ + { + "publicURL": "https://dns.api.rackspacecloud.com/v1.0/123456", + "tenantId": "123456" + } + ], + "name": "cloudDNS", + "type": "rax:dns" + }, + { + "endpoints": [ + { + "publicUrl": "https://syd.orchestration.api.rackspacecloud.com/v1/123456", + "region": "SYD", + "tenantId": "123456" + }, + { + "publicURL": "https://iad.orchestration.api.rackspacecloud.com/v1/123456", + "region": "IAD", + "tenantId": "123456" + }, + { + "publicURL": "https://ord.orchestration.api.rackspacecloud.com/v1/123456", + "region": "ORD", + "tenantId": "123456" + }, + { + "publicURL": "https://dfw.orchestration.api.rackspacecloud.com/v1/123456", + "region": "DFW", + "tenantId": "123456" + } + ], + "name": "cloudOrchestration", + "type": "orchestration" + } + ], + "token": { + "RAX-AUTH:authenticatedBy": [ + "APIKEY" + ], + "expires": "2013-11-13T10:49:29.409Z", + "id": "cc81a27bf50249e5b5878d8b51f2eea6", + "tenant": { + "id": "123456", + "name": "123456" + } + }, + "user": { + "RAX-AUTH:defaultRegion": "DFW", + "id": "10048286", + "name": "jamiehannaford1", + "roles": [ + { + "description": "Checkmate Access role", + "id": "10000150", + "name": "checkmate" + }, + { + "description": "User Admin Role.", + "id": "3", + "name": "identity:user-admin" + }, + { + "description": "A Role that allows a user access to keystone Service methods", + "id": "5", + "name": "object-store:default", + "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + }, + { + "description": "A Role that allows a user access to keystone Service methods", + "id": "6", + "name": "compute:default", + "tenantId": "123456" + } + ] + } + } +} From 23fc494362e0e02473ca63b19f2b576b058a46f4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 12:32:24 -0700 Subject: [PATCH 062/835] Cleaning up old files. --- lib/OpenCloud/Orchestration/Resource.php | 87 ---------- lib/OpenCloud/Orchestration/Stack.php | 205 ----------------------- 2 files changed, 292 deletions(-) delete mode 100644 lib/OpenCloud/Orchestration/Resource.php delete mode 100644 lib/OpenCloud/Orchestration/Stack.php diff --git a/lib/OpenCloud/Orchestration/Resource.php b/lib/OpenCloud/Orchestration/Resource.php deleted file mode 100644 index 053b8e498..000000000 --- a/lib/OpenCloud/Orchestration/Resource.php +++ /dev/null @@ -1,87 +0,0 @@ -noCreate(); - } - - public function id() - { - return $this->physical_resource_id; - } - - protected function primaryKeyField() - { - return 'physical_resource_id'; - } - - public function name() - { - return $this->logical_resource_id; - } - - public function type() - { - return $this->resource_type; - } - - public function status() - { - return $this->resource_status; - } - - public function get() - { - $service = $this->getParent()->getService(); - - switch ($this->resource_type) { - case 'AWS::EC2::Instance': - $objSvc = 'Compute'; - $method = 'Server'; - $name = 'nova'; - break; - default: - throw new Exception(sprintf( - 'Unknown resource type: %s', - $this->resource_type - )); - } - - return $service->connection()->$objSvc($name, $service->region())->$method($this->id()); - } -} diff --git a/lib/OpenCloud/Orchestration/Stack.php b/lib/OpenCloud/Orchestration/Stack.php deleted file mode 100644 index 5452cd8fc..000000000 --- a/lib/OpenCloud/Orchestration/Stack.php +++ /dev/null @@ -1,205 +0,0 @@ -primaryKeyField(); - - if (!empty($this->{$pk})) { - throw new CreateError(sprintf( - 'Stack is already created and has ID of %s', - $this->$pk - )); - } - - $object = (object) array('disable_rollback' => false, 'timeout_mins' => 60); - - foreach ($this->createKeys as $property) { - if (empty($this->$property)) { - throw new CreateError(sprintf( - 'Cannot create Stack with null %s', - $property - )); - } else { - $object->$property = $this->$property; - } - } - - if (null !== $this->parameters) { - $object->parameters = $this->parameters; - } - - return $object; - } - - public function name() - { - return $this->stack_name; - } - - public function status() - { - return $this->stack_status; - } - - public function resource($id = null) - { - $resource = new Resource($this->getService()); - $resource->setParent($this); - $resource->populate($id); - - return $resource; - } - - public function resources() - { - return $this->getService()->collection( - 'OpenCloud\Orchestration\Resource', - $this->url('resources'), - $this - ); - } -} From a8a6ba6a4acb86c6d81259306ae95c173b39725f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 14:53:25 -0700 Subject: [PATCH 063/835] Adding validate template method to Service class. --- lib/OpenCloud/Orchestration/Service.php | 21 +++++++++++++++++++ .../Tests/Orchestration/ServiceTest.php | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Service.php b/lib/OpenCloud/Orchestration/Service.php index 6fa2bfdb5..c24d0e771 100644 --- a/lib/OpenCloud/Orchestration/Service.php +++ b/lib/OpenCloud/Orchestration/Service.php @@ -20,6 +20,7 @@ use OpenCloud\Common\Service\CatalogService; use OpenCloud\Orchestration\Resource\Stack; use OpenCloud\Orchestration\Resource\ResourceType; +use Guzzle\Http\Exception\ClientErrorResponseException; /** * The Orchestration class represents the OpenStack Heat service. @@ -108,6 +109,26 @@ public function getBuildInfo() return $buildInfo; } + /** + * Validates the given template + * + * @return Boolean True, if template is valid; False, otherwise + */ + public function validateTemplate(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath('validate'); + + $json = json_encode($params); + + try { + $this->getClient()->post($url, self::getJsonHeader(), $json)->send(); + return true; + } catch (ClientErrorResponseException $e) { + return false; + } + } + /** * Return namespaces. * diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index 3343bd280..48703d846 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -95,16 +95,16 @@ public function testGetBuildInfo() } -/** public function testValidateValidTemplate() { + $this->addMockSubscriber($this->makeResponse()); $this->assertTrue($this->service->validateTemplate(array('template' => 'heat_template_version: 2013-05-23'))); } public function testValidateInvalidTemplate() { + $this->addMockSubscriber($this->makeResponse(null, 400)); $this->assertFalse($this->service->validateTemplate(array('template' => 'foobar: baz'))); } -**/ } \ No newline at end of file From 3c808acaf0d295a8a341f633a01bd6c31985d64a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 14:53:43 -0700 Subject: [PATCH 064/835] Fixing annotation name. --- lib/OpenCloud/Orchestration/Service.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Service.php b/lib/OpenCloud/Orchestration/Service.php index c24d0e771..d5b17dab4 100644 --- a/lib/OpenCloud/Orchestration/Service.php +++ b/lib/OpenCloud/Orchestration/Service.php @@ -40,7 +40,7 @@ class Service extends CatalogService * Returns a Stack object associated with this Orchestration service * * @param string $id - the stack with the ID is retrieved - * @returns Stack object + * @return Stack object */ public function stack($id = null) { @@ -51,7 +51,7 @@ public function stack($id = null) * Returns a Stack object associated with this Orchestration service * * @param string $id - the stack with the ID is retrieved - * @returns Stack object + * @return Stack object */ public function getStack($id) { @@ -90,7 +90,7 @@ public function listResourceTypes(array $params = array()) * Returns a ResourceType object associated with this Orchestration service * * @param string $id - the resource type with the ID is retrieved - * @returns ResourceType object + * @return ResourceType object */ public function getResourceType($id) { @@ -100,7 +100,7 @@ public function getResourceType($id) /** * Returns a BuildInfo object associated with this Orchestration service * - * @returns BuildInfo object + * @return BuildInfo object */ public function getBuildInfo() { From 29117f671faf6ed997a653415dd31f05abab6007 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 15:12:22 -0700 Subject: [PATCH 065/835] Fleshing out the BuildInfo resource. --- .../Orchestration/Resource/BuildInfo.php | 16 +++++++ .../Orchestration/OrchestrationTestCase.php | 5 +- .../Orchestration/Resource/BuildInfoTest.php | 48 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php diff --git a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php index fc501d826..6a8e6ee6a 100644 --- a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php +++ b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php @@ -28,9 +28,25 @@ class BuildInfo extends PersistentResource protected static $json_name = ''; protected $api; + protected $engine; public function refresh($id = null, $url = null) { return $this->refreshFromParent(); } + + public function create($params = array()) + { + return $this->noCreate(); + } + + public function update($params = array()) + { + return $this->noUpdate(); + } + + public function delete($params = array()) + { + return $this->noDelete(); + } } diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php index 87730ad53..e9ab2a2d0 100644 --- a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -25,10 +25,11 @@ class OrchestrationTestCase extends OpenCloudTestCase protected $service; protected $container; -// protected $mockPath = 'Orchestration'; - public function setupObjects() { $this->service = $this->getClient()->orchestrationService(); + + $this->addMockSubscriber($this->makeResponse('{"engine":{"revision":"2014.j3-20141003-1139"},"fusion-api":{"revision":"j1-20140915-10d9ee4-98"},"api":{"revision":"2014.j3-20141003-1139"}}')); + $this->buildInfo = $this->service->getBuildInfo(); } } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php new file mode 100644 index 000000000..908e0053d --- /dev/null +++ b/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php @@ -0,0 +1,48 @@ +buildInfo->create(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\UpdateError + */ + public function testUpdate() + { + $this->buildInfo->update(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\DeleteError + */ + public function testDelete() + { + $this->buildInfo->delete(); + } +} From 1a5f56b32c161469fedc0e87939c5d609f49f4ae Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 15:29:17 -0700 Subject: [PATCH 066/835] Creating ReadOnlyResource base class + refactoring to use it. --- .../Common/Resource/ReadOnlyResource.php | 42 +++++++++++++++++++ .../Orchestration/Resource/BuildInfo.php | 19 +-------- 2 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 lib/OpenCloud/Common/Resource/ReadOnlyResource.php diff --git a/lib/OpenCloud/Common/Resource/ReadOnlyResource.php b/lib/OpenCloud/Common/Resource/ReadOnlyResource.php new file mode 100644 index 000000000..132136a2e --- /dev/null +++ b/lib/OpenCloud/Common/Resource/ReadOnlyResource.php @@ -0,0 +1,42 @@ +noCreate(); + } + + public function update($params = array()) + { + return $this->noUpdate(); + } + + public function delete() + { + return $this->noDelete(); + } +} diff --git a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php index 6a8e6ee6a..2b50eb5b8 100644 --- a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php +++ b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php @@ -17,12 +17,12 @@ namespace OpenCloud\Orchestration\Resource; -use OpenCloud\Common\Resource\PersistentResource; +use OpenCloud\Common\Resource\ReadOnlyResource; /** * */ -class BuildInfo extends PersistentResource +class BuildInfo extends ReadOnlyResource { protected static $url_resource = 'build_info'; protected static $json_name = ''; @@ -34,19 +34,4 @@ public function refresh($id = null, $url = null) { return $this->refreshFromParent(); } - - public function create($params = array()) - { - return $this->noCreate(); - } - - public function update($params = array()) - { - return $this->noUpdate(); - } - - public function delete($params = array()) - { - return $this->noDelete(); - } } From 4897004d0772a11e27994a300f45fa44c55fbd48 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 15:29:49 -0700 Subject: [PATCH 067/835] Fleshing out ResourceType resource. --- .../Orchestration/Resource/ResourceType.php | 18 +++++- .../Orchestration/OrchestrationTestCase.php | 3 + .../Resource/ResourceTypeTest.php | 56 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php index 68fc6be13..d71d14258 100644 --- a/lib/OpenCloud/Orchestration/Resource/ResourceType.php +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -17,12 +17,12 @@ namespace OpenCloud\Orchestration\Resource; -use OpenCloud\Common\Resource\PersistentResource; +use OpenCloud\Common\Resource\ReadOnlyResource; /** * */ -class ResourceType extends PersistentResource +class ResourceType extends ReadOnlyResource { protected static $url_resource = 'resource_types'; protected static $json_name = ''; @@ -49,4 +49,18 @@ public function getResourceType() { return $this->resource_type; } + + /** + * Returns the template representation for this resource type. + * + * @return String template representation + */ + public function getTemplate() + { + $url = clone $this->getUrl(); + $url->addPath('template'); + + $response = $this->getClient()->get($url)->send(); + return $response->getBody(); + } } diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php index e9ab2a2d0..ed02d3758 100644 --- a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -31,5 +31,8 @@ public function setupObjects() $this->addMockSubscriber($this->makeResponse('{"engine":{"revision":"2014.j3-20141003-1139"},"fusion-api":{"revision":"j1-20140915-10d9ee4-98"},"api":{"revision":"2014.j3-20141003-1139"}}')); $this->buildInfo = $this->service->getBuildInfo(); + + $this->addMockSubscriber($this->makeResponse('{"attributes":{"an_attribute":{"description":"An attribute description ."}},"properties":{"a_property":{"update_allowed":false,"required":true,"type":"string","description":"A resource description."}},"resource_type":"OS::Nova::Server"}')); + $this->resourceType = $this->service->getResourceType("OS::Nova::Server"); } } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php new file mode 100644 index 000000000..68a9dc87e --- /dev/null +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php @@ -0,0 +1,56 @@ +resourceType->create(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\UpdateError + */ + public function testUpdate() + { + $this->resourceType->update(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\DeleteError + */ + public function testDelete() + { + $this->resourceType->delete(); + } + + public function testGetTemplate() + { + $template = '{"HeatTemplateFormatVersion":"2012-12-12","Outputs":{"private_key":{"Description":"The private key if it has been saved.","Value":"{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}"},"public_key":{"Description":"The public key.","Value":"{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}"}},"Parameters":{"name":{"Description":"The name of the key pair.","Type":"String"},"public_key":{"Description":"The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.","Type":"String"},"save_private_key":{"AllowedValues":["True","true","False","false"],"Default":false,"Description":"True if the system should remember a generated private key; False otherwise.","Type":"String"}},"Resources":{"KeyPair":{"Properties":{"name":{"Ref":"name"},"public_key":{"Ref":"public_key"},"save_private_key":{"Ref":"save_private_key"}},"Type":"OS::Nova::KeyPair"}}}'; + $this->addMockSubscriber($this->makeResponse($template)); + + $this->assertEquals($template, $this->resourceType->getTemplate()); + } +} From d667aa3aec7ed36fba90c9331523d322920fdc68 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 16:32:39 -0700 Subject: [PATCH 068/835] Camelcasing parameters. --- docs/userguide/Orchestration/README.md | 6 +- docs/userguide/Orchestration/USERGUIDE.md | 64 +++++++++---------- samples/Orchestration/adopt-stack.php | 8 +-- .../create-stack-from-template-file.php | 6 +- .../create-stack-from-template-url.php | 6 +- samples/Orchestration/preview-stack.php | 4 +- samples/Orchestration/quickstart.php | 6 +- samples/Orchestration/update-stack.php | 2 +- .../validate-template-from-url.php | 2 +- 9 files changed, 52 insertions(+), 52 deletions(-) diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 0c5674526..5f12bf918 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -60,14 +60,14 @@ In the example above, you are connecting to the ``DFW`` region of the cloud. Any ```php $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'Cloud server with attached block storage', - 'template_url' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', + 'name' => 'Cloud server with attached block storage', + 'templateUrl' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', 'parameters' => array( 'key_name' => 'mine', 'flavor' => 'performance1_1', 'image' => '0112b238-4267-4a22-9785-fcf75814bc2f' // Ubuntu 14.04 LTS (Trusty Tahr) ), - 'timeout_mins' => 5 + 'timeoutMins' => 5 )); ``` diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 03b986043..5a604f857 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -91,7 +91,7 @@ as a JSON or YAML file, you can validate it as shown below: ```php $orchestrationService->validateTemplate(array( - 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml' )); ``` @@ -113,8 +113,8 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | | `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `my-drupal-web-site` | -| `template` | Template contents | String, JSON or YAML | No, if `template_url` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | -| `template_url` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | | `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | #### Preview Stack from Template File @@ -125,9 +125,9 @@ can use it to preview a stack as shown below: ```php $stack = $orchestrationService->stack(); $stack->preview(array( - 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'timeout_mins' => 3 + 'name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'timeoutMins' => 3 )); ``` @@ -140,8 +140,8 @@ as a JSON or YAML file, you can use it to preview a stack as shown below: ```php $stack = $orchestrationService->previewStack(array( - 'stack_name' => 'my-drupal-web-site', - 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' + 'name' => 'my-drupal-web-site', + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml' )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` @@ -156,10 +156,10 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | | `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `my-drupal-web-site` | -| `template` | Template contents | String, JSON or YAML | No, if `template_url` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | -| `template_url` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | | `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | -| `timeout_mins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | | 5 | +| `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | | 5 | #### Create Stack from Template File @@ -169,14 +169,14 @@ can use it to create a stack as shown below: ```php $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'parameters' => array( + 'name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' ), - 'timeout_mins' => 3 + 'timeoutMins' => 3 )); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-file.php) ] @@ -188,14 +188,14 @@ as a JSON or YAML file, you can use it to create a stack as shown below: ```php $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'my-drupal-web-site', - 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', - 'parameters' => array( + 'name' => 'my-drupal-web-site', + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' ), - 'timeout_mins' => 5 + 'timeoutMins' => 5 )); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-url.php) ] @@ -255,10 +255,10 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `template` | Template contents | String, JSON or YAML | No, if `template_url` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | -| `template_url` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | | `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | -| `timeout_mins` | Duration, in minutes, after which stack update should time out | Integer | Yes | | 5 | +| `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | | 5 | #### Update Stack from Template File @@ -267,13 +267,13 @@ can use it to update a stack as shown below: ```php $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'parameters' => array( + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupalweb' ), - 'timeout_mins' => 3 + 'timeoutMins' => 3 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` @@ -286,13 +286,13 @@ as a JSON or YAML file, you can use it to update a stack as shown below: ```php $stack->update(array( - 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', - 'parameters' => array( + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupalweb' ), - 'timeout_mins' => 3 + 'timeoutMins' => 3 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` @@ -331,10 +331,10 @@ as shown below: ```php $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'adopt_stack_data' => $abandonStackData, - 'timeout_mins' => 3 + 'name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 3 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index d90f59de5..61f367ad1 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -40,9 +40,9 @@ // 3. Adopt a stack. $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'adopt_stack_data' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), - 'timeout_mins' => 3 + 'name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'adoptStackData' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), + 'timeoutMins' => 3 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 816d846a5..1893e3c5e 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -40,7 +40,7 @@ // 3. Create a stack. $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'timeout_mins' => 3 + 'name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'timeoutMins' => 3 )); diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 34c805502..cd6915890 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -40,12 +40,12 @@ // 3. Create a stack. $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'my-drupal-web-site', - 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'name' => 'my-drupal-web-site', + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' ), - 'timeout_mins' => 5 + 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack.php index 8680bbf9d..3e3df88cf 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack.php @@ -40,6 +40,6 @@ // 3. Preview a stack. $stack = $orchestrationService->stack(); $stack->preview(array( - 'stack_name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml') + 'name' => 'my-drupal-web-site', + 'template' => file_get_contents(__DIR__ . '/sample_template.yml') )); diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index 4e14d079a..4fbe6284f 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -39,12 +39,12 @@ // 3. Create a stack. $stack = $orchestrationService->stack(); $stack->create(array( - 'stack_name' => 'Cloud server with attached block storage', - 'template_url' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', + 'name' => 'Cloud server with attached block storage', + 'templateUrl' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', 'parameters' => array( 'key_name' => 'mine', 'flavor' => 'performance1_1', 'image' => '0112b238-4267-4a22-9785-fcf75814bc2f' // Ubuntu 14.04 LTS (Trusty Tahr) ), - 'timeout_mins' => 5 + 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/update-stack.php b/samples/Orchestration/update-stack.php index cbc646a9c..9a0984398 100644 --- a/samples/Orchestration/update-stack.php +++ b/samples/Orchestration/update-stack.php @@ -49,5 +49,5 @@ 'db_name' => 'drupaldb', 'db_user' => 'drupalweb' ), - 'timeout_mins' => 5 + 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-url.php index 47a901e9d..b5c8146a1 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-url.php @@ -39,5 +39,5 @@ // 3. Validate template from URL $orchestrationService->validateTemplate(array( - 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml' + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml' )); From 46bb64ceed423f6ab19977e0c71dabffc3dcd366 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 6 Oct 2014 16:33:09 -0700 Subject: [PATCH 069/835] Using aliases. --- lib/OpenCloud/Orchestration/Resource/ResourceType.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php index d71d14258..a122771d0 100644 --- a/lib/OpenCloud/Orchestration/Resource/ResourceType.php +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -28,10 +28,14 @@ class ResourceType extends ReadOnlyResource protected static $json_name = ''; protected static $json_collection_name = 'resource_types'; - protected $resource_type; + protected $resourceType; protected $attributes; protected $_properties; // Named so because the Base class has a $properties member. + protected $aliases = array( + 'resource_type' => 'resourceType' + ); + /** * Required to prevent the Base class from attempting to populate $this->properties. */ @@ -45,11 +49,6 @@ public function getProperties() return $this->_properties; } - public function getResourceType() - { - return $this->resource_type; - } - /** * Returns the template representation for this resource type. * From 54e1ea6a89e7610bebb8c405f23be02d646c5e3e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 7 Oct 2014 10:59:18 -0700 Subject: [PATCH 070/835] Fixing capitalization. --- docs/userguide/Orchestration/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 5a604f857..e6e13d6d3 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -357,7 +357,7 @@ foreach ($stackResources as $stackResource) { ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] -### Get stack Resource +### Get Stack Resource You can retrieve a specific resource in a stack using that resource's name, as shown below: From 28e293d5012099c695b331e49c3dbb606f92d642 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 7 Oct 2014 10:59:41 -0700 Subject: [PATCH 071/835] Fixing copy/paste typo. --- docs/userguide/Orchestration/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index e6e13d6d3..d3b8f39a1 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -363,7 +363,7 @@ You can retrieve a specific resource in a stack using that resource's name, as s below: ```php -// 4. Get resource in stack. +// Get resource in stack. $stackResource = $stack->getResource('load-balancer'); /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ ``` From 8d936cd93b1477a70a44a4c3db5e32aa3b3f272f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 7 Oct 2014 11:01:13 -0700 Subject: [PATCH 072/835] Adding sample code and documentation for getting stack resource metadata. --- docs/userguide/Orchestration/USERGUIDE.md | 11 ++++ .../get-stack-resource-metadata.php | 50 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 samples/Orchestration/get-stack-resource-metadata.php diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index d3b8f39a1..bdbaa0d41 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -369,6 +369,17 @@ $stackResource = $stack->getResource('load-balancer'); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] +### Get Stack Resource Metadata + +You can retrieve the metadata for a specific resource in a stack as shown below: + +```php +// Get stack resource metadata. +$stackResourceMetadata = $stackResource->getMetadata(); +/** @var $stackResourceMetadata OpenCloud\Orchestration\Resource\StackResourceMetadata **/ +``` +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] + ## Stack Resource Events Operations on resources within a stack (such as creation of a resource) produce events. diff --git a/samples/Orchestration/get-stack-resource-metadata.php b/samples/Orchestration/get-stack-resource-metadata.php new file mode 100644 index 000000000..ec1b2f6f7 --- /dev/null +++ b/samples/Orchestration/get-stack-resource-metadata.php @@ -0,0 +1,50 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Get resource in stack. +$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); + +// 5. Get stack resource metadata. +$stackResourceMetadata = $stackResource->getMetadata(); +/** @var $stackResourceMetadata OpenCloud\Orchestration\Resource\StackResourceMetadata **/ From 707855a8e8afe24fae96ff38baa08551bfae4331 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 8 Oct 2014 01:18:34 -0700 Subject: [PATCH 073/835] Making user guide and samples consistent. --- docs/userguide/Orchestration/USERGUIDE.md | 122 ++++++------ samples/Orchestration/adopt-stack.php | 9 +- .../create-stack-from-template-file.php | 14 +- .../create-stack-from-template-url.php | 13 +- .../get-stack-resource-metadata.php | 2 +- samples/Orchestration/lamp.yaml | 184 ++++++++++++++++++ ...p => preview-stack-from-template-file.php} | 13 +- .../preview-stack-from-template-url.php | 50 +++++ ...hp => update-stack-from-template-file.php} | 11 +- .../update-stack-from-template-url.php | 53 +++++ ... validate-template-from-template-file.php} | 0 ...> validate-template-from-template-url.php} | 2 +- 12 files changed, 385 insertions(+), 88 deletions(-) create mode 100644 samples/Orchestration/lamp.yaml rename samples/Orchestration/{preview-stack.php => preview-stack-from-template-file.php} (79%) create mode 100644 samples/Orchestration/preview-stack-from-template-url.php rename samples/Orchestration/{update-stack.php => update-stack-from-template-file.php} (86%) create mode 100644 samples/Orchestration/update-stack-from-template-url.php rename samples/Orchestration/{validate-template-from-file.php => validate-template-from-template-file.php} (100%) rename samples/Orchestration/{validate-template-from-url.php => validate-template-from-template-url.php} (92%) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index bdbaa0d41..b7b9d1177 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -77,11 +77,11 @@ can validate it as shown below: ```php $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/sample_template.yml') + 'template' => file_get_contents(__DIR__ . '/lamp.yml') )); ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-url.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseException`](http://api.guzzlephp.org/class-Guzzle.Http.Exception.ClientErrorResponseException.html) will be thrown. @@ -91,11 +91,11 @@ as a JSON or YAML file, you can validate it as shown below: ```php $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml' + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' )); ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-url.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseException`](http://api.guzzlephp.org/class-Guzzle.Http.Exception.ClientErrorResponseException.html) will be thrown. @@ -112,9 +112,9 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `my-drupal-web-site` | -| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `simple-lamp-setup` | +| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | | `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | #### Preview Stack from Template File @@ -123,15 +123,19 @@ If your template is stored on your local computer as a JSON or YAML file, you can use it to preview a stack as shown below: ```php -$stack = $orchestrationService->stack(); -$stack->preview(array( - 'name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'timeoutMins' => 3 +$stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-file.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-file.php) ] #### Preview Stack from Template URL @@ -140,13 +144,18 @@ as a JSON or YAML file, you can use it to preview a stack as shown below: ```php $stack = $orchestrationService->previewStack(array( - 'name' => 'my-drupal-web-site', - 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml' + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-url.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-url.php) ] ### Create Stack You can create a stack from a template. @@ -155,10 +164,10 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `my-drupal-web-site` | -| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | +| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `simple-lamp-setup` | +| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('server_hostname' => 'web01')` | | `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | | 5 | #### Create Stack from Template File @@ -167,19 +176,18 @@ If your template is stored on your local computer as a JSON or YAML file, you can use it to create a stack as shown below: ```php -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'parameters' => array( - 'flavor_id' => 'performance1_1', - 'db_name' => 'drupaldb', - 'db_user' => 'drupaldbuser' +$stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), - 'timeoutMins' => 3 + 'timeoutMins' => 5 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-file.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-file.php) ] #### Create Stack from Template URL If your template is stored in a remote location accessible via HTTP or HTTPS, @@ -188,17 +196,16 @@ as a JSON or YAML file, you can use it to create a stack as shown below: ```php $stack = $orchestrationService->stack(); $stack->create(array( - 'name' => 'my-drupal-web-site', - 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'flavor_id' => 'performance1_1', - 'db_name' => 'drupaldb', - 'db_user' => 'drupaldbuser' + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-url.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-url.php) ] ### List Stacks You can list all the stacks that you have created as shown below: @@ -218,13 +225,13 @@ This operation takes the following positional parameters: | Position | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| 1 | Name of the stack | String | Yes | | `my-drupal-web-site` | +| 1 | Name of the stack | String | Yes | | `simple-lamp-setup` | | 2 | ID of the stack | String | No | | `1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619` | #### Get Stack by Name ```php -$stack = $orchestrationService->getStack('my-drupal-stack'); +$stack = $orchestrationService->getStack('simple-lamp-setup'); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name.php) ] @@ -232,7 +239,7 @@ $stack = $orchestrationService->getStack('my-drupal-stack'); #### Get Stack by Name and ID ```php -$stack = $orchestrationService->getStack('my-drupal-stack', '1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619'); +$stack = $orchestrationService->getStack('simple-lamp-setup', '1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619'); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name-and-id.php) ] @@ -255,8 +262,8 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: My Drual Web Site\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://github.com/ycombinator/drupal-multi/template.yml` | +| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | | `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | | `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | | 5 | @@ -267,17 +274,16 @@ can use it to update a stack as shown below: ```php $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), 'parameters' => array( - 'flavor_id' => 'performance1_1', - 'db_name' => 'drupaldb', - 'db_user' => 'drupalweb' + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), - 'timeoutMins' => 3 + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-file.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-file.php) ] #### Update Stack from Template URL @@ -286,17 +292,16 @@ as a JSON or YAML file, you can use it to update a stack as shown below: ```php $stack->update(array( - 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'flavor_id' => 'performance1_1', - 'db_name' => 'drupaldb', - 'db_user' => 'drupalweb' + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), - 'timeoutMins' => 3 + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-url.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-url.php) ] ### Delete Stack @@ -329,12 +334,11 @@ If you have data from an abandoned stack, you may adopt it and recreate the stac as shown below: ```php -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), +$stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 3 + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` @@ -376,7 +380,7 @@ You can retrieve the metadata for a specific resource in a stack as shown below: ```php // Get stack resource metadata. $stackResourceMetadata = $stackResource->getMetadata(); -/** @var $stackResourceMetadata OpenCloud\Orchestration\Resource\StackResourceMetadata **/ +/** @var $stackResourceMetadata \stdClass **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index 61f367ad1..eef11034c 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -38,11 +38,10 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Adopt a stack. -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), +$stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), 'adoptStackData' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), - 'timeoutMins' => 3 + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 1893e3c5e..e5dac4566 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -38,9 +38,13 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'timeoutMins' => 3 +$stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index cd6915890..93e551004 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -38,14 +38,13 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'my-drupal-web-site', - 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', +$stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'flavor_id' => 'performance1_1', - 'db_name' => 'drupaldb', - 'db_user' => 'drupaldbuser' + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/get-stack-resource-metadata.php b/samples/Orchestration/get-stack-resource-metadata.php index ec1b2f6f7..12f16b446 100644 --- a/samples/Orchestration/get-stack-resource-metadata.php +++ b/samples/Orchestration/get-stack-resource-metadata.php @@ -47,4 +47,4 @@ // 5. Get stack resource metadata. $stackResourceMetadata = $stackResource->getMetadata(); -/** @var $stackResourceMetadata OpenCloud\Orchestration\Resource\StackResourceMetadata **/ +/** @var $stackResourceMetadata \stdClass **/ diff --git a/samples/Orchestration/lamp.yaml b/samples/Orchestration/lamp.yaml new file mode 100644 index 000000000..d70b2cc4c --- /dev/null +++ b/samples/Orchestration/lamp.yaml @@ -0,0 +1,184 @@ +heat_template_version: 2013-05-23 + +description: | + This is a Heat template to deploy a server with LAMP + +parameter_groups: +- label: Server Settings + parameters: + - server_hostname + - image + - flavor + +- label: phpMyAdmin Settings + parameters: + - phpmyadmin_user + +- label: rax-dev-params + parameters: + - kitchen + - chef_version + +parameters: + server_hostname: + label: Server Name + description: Hostname to use for setting the server name. + type: string + default: web + constraints: + - length: + min: 1 + max: 64 + - allowed_pattern: "^[a-zA-Z0-9]([a-zA-Z0-9.-])*$" + description: | + Must begin with a letter or number and be alphanumeric or '-' and '.' + + image: + label: Operating System + description: | + Required: Server image used for all servers that are created as a part of + this deployment. + type: string + default: CentOS 6.5 (PVHVM) + constraints: + - allowed_values: + - CentOS 6.5 (PVHVM) + - Red Hat Enterprise Linux 6.5 (PVHVM) + - Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM) + - Ubuntu 12.04 LTS (Precise Pangolin) + - Debian 7 (Wheezy) (PVHVM) + description: Must be a supported operating system. + + flavor: + label: Server Size + description: | + Required: Rackspace Cloud Server flavor to use. The size is based on the + amount of RAM for the provisioned server. + type: string + default: 1 GB Performance + constraints: + - allowed_values: + - 1 GB Performance + - 2 GB Performance + - 4 GB Performance + - 8 GB Performance + - 15 GB Performance + - 30 GB Performance + description: | + Must be a valid Rackspace Cloud Server flavor for the region you have + selected to deploy into. + + phpmyadmin_user: + label: Username + description: "Username for phpMyAdmin logins." + type: string + default: serverinfo + constraints: + - allowed_pattern: "^(.){1,16}$" + description: | + Must be shorter than 16 characters, this is due to MySQL's maximum + username length. + + kitchen: + description: URL for the kitchen to use + type: string + default: https://github.com/rackspace-orchestration-templates/lamp + + chef_version: + description: Version of chef client to use + type: string + default: 11.12.8 + +resources: + + ssh_key: + type: "OS::Nova::KeyPair" + properties: + name: { get_param: "OS::stack_id" } + save_private_key: true + + mysql_root_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_repl_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_debian_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + phpmyadmin_pass: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + linux_server: + type: "Rackspace::Cloud::Server" + properties: + name: { get_param: server_hostname } + flavor: { get_param: flavor } + image: { get_param: image } + key_name: { get_resource: ssh_key } + config_drive: "true" + metadata: + rax-heat: { get_param: "OS::stack_id" } + metadata: + foo: bar + baz: brrr + + linux_setup: + type: "OS::Heat::ChefSolo" + depends_on: linux_server + properties: + username: root + private_key: { get_attr: [ssh_key, private_key] } + host: { get_attr: [linux_server, accessIPv4] } + kitchen: { get_param: kitchen } + chef_version: { get_param: chef_version } + node: + mysql: + server_root_password: { get_attr: [mysql_root_password, value] } + server_repl_password: { get_attr: [mysql_repl_password, value] } + server_debian_password: { get_attr: [mysql_debian_password, value] } + phpmyadmin: + pass: { get_attr: [phpmyadmin_pass, value] } + user: { get_param: phpmyadmin_user } + run_list: ["recipe[LAMP]"] + +outputs: + private_key: + description: SSH Private Key + value: { get_attr: [ssh_key, private_key] } + + server_ip: + description: Server IP + value: { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_url: + description: phpMyAdmin URL + value: + str_replace: + template: "http://%server_ip%/phpmyadmin" + params: + "%server_ip%": { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_user: + description: phpMyAdmin User + value: { get_param: phpmyadmin_user } + + phpmyadmin_password: + description: phpMyAdmin Password + value: { get_attr: [phpmyadmin_pass, value] } + + mysql_root_password: + description: MySQL Root Password + value: { get_attr: [mysql_root_password, value] } diff --git a/samples/Orchestration/preview-stack.php b/samples/Orchestration/preview-stack-from-template-file.php similarity index 79% rename from samples/Orchestration/preview-stack.php rename to samples/Orchestration/preview-stack-from-template-file.php index 3e3df88cf..57da568f4 100644 --- a/samples/Orchestration/preview-stack.php +++ b/samples/Orchestration/preview-stack-from-template-file.php @@ -38,8 +38,13 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Preview a stack. -$stack = $orchestrationService->stack(); -$stack->preview(array( - 'name' => 'my-drupal-web-site', - 'template' => file_get_contents(__DIR__ . '/sample_template.yml') +$stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 )); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/preview-stack-from-template-url.php b/samples/Orchestration/preview-stack-from-template-url.php new file mode 100644 index 000000000..f5c8c2a23 --- /dev/null +++ b/samples/Orchestration/preview-stack-from-template-url.php @@ -0,0 +1,50 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Preview a stack. +$stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/update-stack.php b/samples/Orchestration/update-stack-from-template-file.php similarity index 86% rename from samples/Orchestration/update-stack.php rename to samples/Orchestration/update-stack-from-template-file.php index 9a0984398..bf19fe67b 100644 --- a/samples/Orchestration/update-stack.php +++ b/samples/Orchestration/update-stack-from-template-file.php @@ -43,11 +43,10 @@ // 4. Update stack. $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/sample_template.yml'), - 'parameters' => array( - 'flavor_id' => 'performance1_1', - 'db_name' => 'drupaldb', - 'db_user' => 'drupalweb' + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), - 'timeoutMins' => 5 + 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/update-stack-from-template-url.php b/samples/Orchestration/update-stack-from-template-url.php new file mode 100644 index 000000000..133608ff7 --- /dev/null +++ b/samples/Orchestration/update-stack-from-template-url.php @@ -0,0 +1,53 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('NOVA_API_KEY') +)); + +// 2. Obtain an Orchestration service object from the client. +$region = getenv('OS_REGION_NAME'); +$orchestrationService = $client->orchestrationService(null, $region); + +// 3. Get stack. +$stack = $orchestrationService->getStack(getenv('STACK_NAME')); + +// 4. Update stack. +$stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 +)); +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/validate-template-from-file.php b/samples/Orchestration/validate-template-from-template-file.php similarity index 100% rename from samples/Orchestration/validate-template-from-file.php rename to samples/Orchestration/validate-template-from-template-file.php diff --git a/samples/Orchestration/validate-template-from-url.php b/samples/Orchestration/validate-template-from-template-url.php similarity index 92% rename from samples/Orchestration/validate-template-from-url.php rename to samples/Orchestration/validate-template-from-template-url.php index b5c8146a1..0b9b1f0dc 100644 --- a/samples/Orchestration/validate-template-from-url.php +++ b/samples/Orchestration/validate-template-from-template-url.php @@ -39,5 +39,5 @@ // 3. Validate template from URL $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml' + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' )); From 95b907970f9a911af3f451cbb3629ac6f5f4432a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 8 Oct 2014 02:42:15 -0700 Subject: [PATCH 074/835] Adding default implementation for updateJson. --- .../Common/Resource/PersistentResource.php | 17 ++++++++++++++++- .../Tests/Common/PersistentObjectTest.php | 8 -------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 328db372a..7b449abdc 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -250,7 +250,22 @@ protected function getAlias($key) */ protected function updateJson($params = array()) { - throw new UpdateError('updateJson() must be overriden in order for an update to happen'); + if (!isset($this->updateKeys)) { + throw new \RuntimeException(sprintf( + 'This resource object [%s] must have a visible updateKeys array', + get_class($this) + )); + } + + $element = (object) array(); + + foreach ($this->updateKeys as $key) { + if (null !== ($property = $this->getProperty($key))) { + $element->{$this->getAlias($key)} = $property; + } + } + + return (object) array($this->jsonName() => (object) $element); } /** diff --git a/tests/OpenCloud/Tests/Common/PersistentObjectTest.php b/tests/OpenCloud/Tests/Common/PersistentObjectTest.php index 25440c057..0f41b7d3c 100644 --- a/tests/OpenCloud/Tests/Common/PersistentObjectTest.php +++ b/tests/OpenCloud/Tests/Common/PersistentObjectTest.php @@ -178,14 +178,6 @@ public function testCreate() $this->instance->create(); } - /** - * @expectedException OpenCloud\Common\Exceptions\UpdateError - */ - public function testUpdate() - { - $this->instance->update(); - } - public function testName() { $this->instance->name = ''; From df1888e30f205b08c925e84cf782f6f01b62d448 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 8 Oct 2014 02:45:00 -0700 Subject: [PATCH 075/835] Fleshing out stack resource. --- docs/userguide/Orchestration/USERGUIDE.md | 26 +-- .../Orchestration/Resource/Resource.php | 67 +++++++ .../Orchestration/Resource/Stack.php | 164 ++++++++++++++++++ .../Orchestration/Resource/StackResource.php | 27 --- lib/OpenCloud/Orchestration/Service.php | 39 +++++ .../get-stack-resource-event.php | 6 +- .../get-stack-resource-metadata.php | 6 +- samples/Orchestration/get-stack-resource.php | 4 +- samples/Orchestration/list-stack-events.php | 2 +- .../list-stack-resource-events.php | 8 +- .../Orchestration/list-stack-resources.php | 6 +- .../Orchestration/OrchestrationTestCase.php | 6 + .../Orchestration/Resource/ResourceTest.php | 55 ++++++ .../Orchestration/Resource/StackTest.php | 130 ++++++++++++++ .../Tests/Orchestration/ServiceTest.php | 20 ++- 15 files changed, 509 insertions(+), 57 deletions(-) create mode 100644 lib/OpenCloud/Orchestration/Resource/Resource.php delete mode 100644 lib/OpenCloud/Orchestration/Resource/StackResource.php create mode 100644 tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php create mode 100644 tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index b7b9d1177..fefe53a34 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -354,9 +354,9 @@ load balancers, servers or the software installed on servers. You can list all the resources for a stack as shown below: ```php -$stackResources = $stack->listResources(); -foreach ($stackResources as $stackResource) { - /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +$resources = $stack->listResources(); +foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] @@ -368,8 +368,8 @@ below: ```php // Get resource in stack. -$stackResource = $stack->getResource('load-balancer'); -/** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +$resource = $stack->getResource('load-balancer'); +/** @var $resource OpenCloud\Orchestration\Resource\Resource **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] @@ -379,8 +379,8 @@ You can retrieve the metadata for a specific resource in a stack as shown below: ```php // Get stack resource metadata. -$stackResourceMetadata = $stackResource->getMetadata(); -/** @var $stackResourceMetadata \stdClass **/ +$resourceMetadata = $resource->getMetadata(); +/** @var $resourceMetadata \stdClass **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] @@ -394,7 +394,7 @@ You can list all events for all resources in a stack as shown below: ```php $stackEvents = $stack->listEvents(); foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ + /** @var $stackEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ } ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] @@ -403,9 +403,9 @@ foreach ($stackEvents as $stackEvent) { You can list all events for a specific resource in a stack as shown below: ```php -$stackResourceEvents = $stackResource->listEvents(); -foreach ($stackResourceEvents as $stackResourceEvent) { - /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +$resourceEvents = $resource->listEvents(); +foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ } ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] @@ -415,8 +415,8 @@ You can retrieve a specific event for a specific resource in a stack, by using the resource event's ID, as shown below: ```php -$stackResourceEvent = $stackResource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -/** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +$resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); +/** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php new file mode 100644 index 000000000..bfe825e9f --- /dev/null +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -0,0 +1,67 @@ + 'name', + 'resource_status' => 'status', + 'resource_status_reason' => 'statusReason', + 'logical_resource_id' => 'logicalId', + 'physical_resource_id' => 'physicalId', + 'required_by' => 'requiredBy', + 'updated_time' => 'updatedTime', + 'resource_type' => 'type' + ); + + public function primaryKeyField() + { + return 'name'; + } + + public function getMetadata() + { + $metadataUrl = $this->metadataUrl(); + $response = $this->getClient()->get($metadataUrl)->send(); + return json_decode($response->getBody(true)); + } + + protected function metadataUrl() + { + return $this->getParent()->url('metadata'); + } +} diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index 02ede4049..e688a06db 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -27,5 +27,169 @@ class Stack extends PersistentResource protected static $url_resource = 'stacks'; protected static $json_name = 'stack'; + protected $id; + protected $disableRollback; protected $description; + protected $parameters; + protected $environment; + protected $files; + protected $name; + protected $status; + protected $statusReason; + protected $outputs; + protected $creationTime; + protected $updatedTime; + protected $timeoutMins; + protected $templateUrl; + protected $template; + protected $adoptStackData; + + protected $aliases = array( + 'disable_rollback' => 'disableRollback', + 'stack_name' => 'name', + 'stack_status' => 'status', + 'stack_status_reason' => 'statusReason', + 'creation_time' => 'creationTime', + 'updated_time' => 'updatedTime', + 'timeout_mins' => 'timeoutMins', + 'template_url' => 'templateUrl', + 'adopt_stack_data' => 'adoptStackData' + ); + + protected $createKeys = array( + 'name', + 'templateUrl', + 'template', + 'environment', + 'files', + 'parameters', + 'timeoutMins', + 'adoptStackData' + ); + + protected $updateKeys = array( + 'templateUrl', + 'template', + 'environment', + 'files', + 'parameters', + 'timeoutMins' + ); + + protected function createJson() + { + $createJson = parent::createJson(); + return $createJson->{self::$json_name}; + } + + protected function updateJson($params = array()) + { + $updateJson = parent::updateJson($params); + return $updateJson->{self::$json_name}; + } + + /** + * Creates a new stack by adopting resources from an abandoned stack + * + * @param array $params Adopt stack parameters + * @return Guzzle\Http\Message\Response + */ + public function adopt($params) + { + // Validate that required parameters are provided + $requiredParameterName = 'adoptStackData'; + if (!array_key_exists($requiredParameterName, $params)) { + throw new \InvalidArgumentException($requiredParameterName . ' is a required option'); + } + + return $this->create($params); + } + + /** + * Previews the stack without actually creating it + * + * @param array $params Preview stack parameters + * @return Guzzle\Http\Message\Response + */ + public function preview($params = array()) + { + // set parameters + if (!empty($params)) { + $this->populate($params, false); + } + + // construct the JSON + $json = json_encode($this->createJson()); + $this->checkJsonError(); + + $previewUrl = $this->previewUrl(); + $response = $this->getClient()->post($previewUrl, self::getJsonHeader(), $json)->send(); + + $decoded = $this->parseResponse($response); + $this->populate($decoded); + + return $response; + } + + /** + * Abandons the stack and returns abandoned stack data. + * + * @return string Abandoned stack data (which could be passed to the adopt stack operation as adoptStackData). + */ + public function abandon() + { + $abandonUrl = $this->abandonUrl(); + $response = $this->getClient()->delete($abandonUrl)->send(); + return $response->getBody(true); + } + + /** + * Returns a Resource object associated with this Stack + * + * @param string $name Stack resource name + * @return Resource object + */ + public function getResource($name) + { + return $this->getService()->resource('Resource', $name, $this); + } + + /** + * Returns a list of Resources associated with this Stack + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listResources(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Resource::resourceName())->setQuery($params); + + return $this->getService()->resourceList('Resource', $url, $this); + } + + protected function previewUrl() + { + return $this->getParent()->url('preview'); + } + + protected function abandonUrl() + { + return $this->getParent()->url('abandon'); + } + + protected function primaryKeyField() + { + return 'name'; + } + + public function getUrl($path = NULL, array $query = array()) + { + $url = parent::getUrl($path, $query); + if ($this->id) { + $url->addPath($this->id); + } + return $url; + } + } diff --git a/lib/OpenCloud/Orchestration/Resource/StackResource.php b/lib/OpenCloud/Orchestration/Resource/StackResource.php deleted file mode 100644 index 46f50aa1a..000000000 --- a/lib/OpenCloud/Orchestration/Resource/StackResource.php +++ /dev/null @@ -1,27 +0,0 @@ -resource('Stack', $id); } + /** + * Previews a Stack from a template and returns it. + * + * @param array $params Stack preview parameters + * @return Stack Object representing previewed stack + */ + public function previewStack($params = array()) + { + $stack = $this->stack(); + $stack->preview($params); + return $stack; + } + + /** + * Creates a new Stack and returns it. + * + * @param array $params Stack creation parameters + * @return Stack Object representing created stack + */ + public function createStack($params = array()) + { + $stack = $this->stack(); + $stack->create($params); + return $stack; + } + + /** + * Adopts a Stack and returns it. + * + * @param array $params Stack adoption parameters + * @return Stack Object representing adopted stack + */ + public function adoptStack($params = array()) + { + $stack = $this->stack(); + $stack->adopt($params); + return $stack; + } + /** * Returns a Stack object associated with this Orchestration service * diff --git a/samples/Orchestration/get-stack-resource-event.php b/samples/Orchestration/get-stack-resource-event.php index 9f257935b..a6870698e 100644 --- a/samples/Orchestration/get-stack-resource-event.php +++ b/samples/Orchestration/get-stack-resource-event.php @@ -44,8 +44,8 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get resource in stack. -$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); // 5. Get stack resource event. -$stackResourceEvent = $stackResource->getEvent(getenv('STACK_RESOURCE_EVENT_ID')); -/** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +$resourceEvent = $resource->getEvent(getenv('STACK_RESOURCE_EVENT_ID')); +/** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ diff --git a/samples/Orchestration/get-stack-resource-metadata.php b/samples/Orchestration/get-stack-resource-metadata.php index 12f16b446..74d1feab0 100644 --- a/samples/Orchestration/get-stack-resource-metadata.php +++ b/samples/Orchestration/get-stack-resource-metadata.php @@ -43,8 +43,8 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get resource in stack. -$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); // 5. Get stack resource metadata. -$stackResourceMetadata = $stackResource->getMetadata(); -/** @var $stackResourceMetadata \stdClass **/ +$resourceMetadata = $resource->getMetadata(); +/** @var $resourceMetadata \stdClass **/ diff --git a/samples/Orchestration/get-stack-resource.php b/samples/Orchestration/get-stack-resource.php index fb457fd13..4c7f206d2 100644 --- a/samples/Orchestration/get-stack-resource.php +++ b/samples/Orchestration/get-stack-resource.php @@ -43,5 +43,5 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get resource in stack. -$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); -/** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +/** @var $resource OpenCloud\Orchestration\Resource\Resource **/ diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index 1208dd449..2559dbe56 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -44,5 +44,5 @@ // 4. Get list of events for the stack. $stackEvents = $stack->listEvents(); foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ + /** @var $stackEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ } diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index a31e71bbb..c4d5f5488 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -43,10 +43,10 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get resource in stack. -$stackResource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); // 5. Get list of events for the stack resource. -$stackResourceEvents = $stackResource->listEvents(); -foreach ($stackResourceEvents as $stackResourceEvent) { - /** @var $stackResourceEvent OpenCloud\Orchestration\Resource\StackResourceEvent **/ +$resourceEvents = $resource->listEvents(); +foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ } diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index 883eb3895..99a2241bb 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -42,7 +42,7 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get list of resources in the stack. -$stackResources = $stack->listResources(); -foreach ($stackResources as $stackResource) { - /** @var $stackResource OpenCloud\Orchestration\Resource\StackResource **/ +$resources = $stack->listResources(); +foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php index ed02d3758..cf22a3a8b 100644 --- a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -34,5 +34,11 @@ public function setupObjects() $this->addMockSubscriber($this->makeResponse('{"attributes":{"an_attribute":{"description":"An attribute description ."}},"properties":{"a_property":{"update_allowed":false,"required":true,"type":"string","description":"A resource description."}},"resource_type":"OS::Nova::Server"}')); $this->resourceType = $this->service->getResourceType("OS::Nova::Server"); + + $this->addMockSubscriber($this->makeResponse('{"stack":{"capabilities":[],"creation_time":"2014-06-03T20:59:46Z","description":"sample stack","disable_rollback":"True","id":"3095aefc-09fb-4bc7-b1f0-f21a304e864c","links":[{"href":"http://192.168.123.200:8004/v1/eb1c63a4f77141548385f113a28f0f52/stacks/simple_stack/3095aefc-09fb-4bc7-b1f0-f21a304e864c","rel":"self"}],"notification_topics":[],"outputs":[],"parameters":{"OS::stack_id":"3095aefc-09fb-4bc7-b1f0-f21a304e864c","OS::stack_name":"simple_stack"},"stack_name":"simple_stack","stack_status":"CREATE_COMPLETE","stack_status_reason":"Stack CREATE completed successfully","template_description":"sample stack","timeout_mins":"","updated_time":""}}')); + $this->stack = $this->service->getStack('simple_stack'); + + $this->addMockSubscriber($this->makeResponse('{"resource":{"resource_name":"MySqlCloudDatabaseServer","description":"","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx21-9xx9-4xxxe-bxxf-a7fxxxxx68/resources/MySqlCloudDatabaseServer","rel":"self"},{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7fxxxxxx68","rel":"stack"}],"logical_resource_id":"MySqlCloudDatabaseServer","resource_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","required_by":[],"resource_status_reason":"state changed","physical_resource_id":"98xxx0-cxx8-4xxe-bxx5-3fxxxx11","resource_type":"OS::Trove::Instance"}}')); + $this->resource = $this->stack->getResource('MySqlCloudDatabaseServer'); } } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php new file mode 100644 index 000000000..0178ad64d --- /dev/null +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php @@ -0,0 +1,55 @@ +resource->create(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\UpdateError + */ + public function testUpdate() + { + $this->resource->update(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\DeleteError + */ + public function testDelete() + { + $this->resource->delete(); + } + + public function testGetMetadata() + { + $this->addMockSubscriber($this->makeResponse('{"foo":"bar","baz":17}')); + $metadata = $this->resource->getMetadata(); + $this->assertEquals("bar", $metadata->foo); + } +} diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php new file mode 100644 index 000000000..67b5544d9 --- /dev/null +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -0,0 +1,130 @@ + 'foobar', + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupaldbuser' + ), + 'timeoutMins' => 5 + ); + + $stack = new PublicStack($this->service); + $stack->create($createParams); + $createJson = $stack->createJson(); + + $expectedObj = (object) array( + 'stack_name' => 'foobar', + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupaldbuser' + ), + 'timeout_mins' => 5 + ); + + $this->assertEquals($expectedObj, $createJson); + } + + public function testUpdateJson() + { + + $updateParams = array( + 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupalwebuser' + ), + 'timeoutMins' => 10 + ); + + $stack = new PublicStack($this->service); + $stack->update($updateParams); + $updateJson = $stack->updateJson(); + + $expectedObj = (object) array( + 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', + 'parameters' => array( + 'flavor_id' => 'performance1_1', + 'db_name' => 'drupaldb', + 'db_user' => 'drupalwebuser' + ), + 'timeout_mins' => 10 + ); + + $this->assertEquals($expectedObj, $updateJson); + } + + public function testAbandonStack() + { + + $expectedAbandonStackData = '{"status":"COMPLETE","name":"g","dry_run":true,"template":{"outputs":{"instance_ip":{"value":{"str_replace":{"params":{"username":"ec2-user","hostname":{"get_attr":["server","first_address"]}},"template":"ssh username@hostname"}}}},"heat_template_version":"2013-05-23","resources":{"server":{"type":"OS::Nova::Server","properties":{"key_name":{"get_param":"key_name"},"image":{"get_param":"image"},"flavor":{"get_param":"flavor"}}}},"parameters":{"key_name":{"default":"heat_key","type":"string"},"image":{"default":"Ubuntu 12.04 LTS (Precise Pangolin)","type":"string"},"flavor":{"default":"1 GB Performance","type":"string"}}},"action":"CREATE","id":"16934ca3-40e0-4fb2-a289-a700662ec05a","resources":{"server":{"status":"COMPLETE","name":"server","resource_data":{},"resource_id":"39d5dad7-7d7a-4cc8-bd84-851e9e2ff4ea","action":"CREATE","type":"OS::Nova::Server","metadata":{}}}}'; + $this->addMockSubscriber($this->makeResponse($expectedAbandonStackData)); + + $actualAbandonStackData = $this->stack->abandon(); + $this->assertEquals($expectedAbandonStackData, $actualAbandonStackData); + } + + public function testListResources() + { + $this->addMockSubscriber($this->makeResponse('{"resources":[{"resource_name":"MySqlCloudDatabaseServer","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7xxxxxd99068/resources/MySqlCloudDatabaseServer","rel":"self"},{"href":"http:s//dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7xxxxx068","rel":"stack"}],"logical_resource_id":"MySqlCloudDatabaseServer","resource_status_reason":"state changed","updated_time":"2014-02-05T19:20:31Z","required_by":[],"resource_status":"CREATE_COMPLETE","physical_resource_id":"984xxxxxe0-c7x8-4x6e-be15-3f0xxxxx711","resource_type":"OS::Trove::Instance"}]}')); + + $resources = $this->stack->listResources(); + $this->assertInstanceOf(self::COLLECTION_CLASS, $resources); + + $firstResource = $resources->getElement(0); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $firstResource); + $this->assertEquals('MySqlCloudDatabaseServer', $firstResource->getName()); + } + + public function testGetResource() + { + $this->addMockSubscriber($this->makeResponse('{"resource":{"resource_name":"MySqlCloudDatabaseServer","description":"A MySQL DB instance","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx21-9xx9-4xxxe-bxxf-a7fxxxxx68/resources/MySqlCloudDatabaseServer","rel":"self"},{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7fxxxxxx68","rel":"stack"}],"logical_resource_id":"MySqlCloudDatabaseServer","resource_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","required_by":[],"resource_status_reason":"state changed","physical_resource_id":"98xxx0-cxx8-4xxe-bxx5-3fxxxx11","resource_type":"OS::Trove::Instance"}}')); + + $resource = $this->stack->getResource('MySqlCloudDatabaseServer'); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $resource); + $this->assertEquals('A MySQL DB instance', $resource->getDescription()); + } +} diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index 48703d846..8d9b8a0c5 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -35,9 +35,27 @@ public function test__construct() $this->assertInstanceOf('OpenCloud\Orchestration\Service', $service); } + public function testPreviewStack() + { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->previewStack()); + } + public function testCreateStack() { - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->stack()); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->createStack()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testAdoptStackWithoutAdoptStackData() + { + $this->service->adoptStack(); + } + + public function testAdoptStackWithAdoptStackData() + { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->adoptStack(array('adoptStackData' => 'foobar'))); } public function testListStacks() From 05da03a20f4fc86a7fbe0ad2546952f74b35f7ba Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 8 Oct 2014 16:24:30 -0700 Subject: [PATCH 076/835] Fleshing out events. --- .../Orchestration/Resource/Event.php | 48 +++++++++++++++++++ .../Orchestration/Resource/Resource.php | 20 ++++++++ .../Orchestration/Resource/Stack.php | 22 +++++++++ .../Resource/StackResourceEvent.php | 27 ----------- .../Orchestration/Resource/ResourceTest.php | 20 ++++++++ .../Orchestration/Resource/StackTest.php | 12 +++++ 6 files changed, 122 insertions(+), 27 deletions(-) create mode 100644 lib/OpenCloud/Orchestration/Resource/Event.php delete mode 100644 lib/OpenCloud/Orchestration/Resource/StackResourceEvent.php diff --git a/lib/OpenCloud/Orchestration/Resource/Event.php b/lib/OpenCloud/Orchestration/Resource/Event.php new file mode 100644 index 000000000..068244fed --- /dev/null +++ b/lib/OpenCloud/Orchestration/Resource/Event.php @@ -0,0 +1,48 @@ + 'time', + 'resource_name' => 'resourceName', + 'logical_resource_id' => 'resourceLogicalId', + 'physical_resource_id' => 'resourcePhysicalId', + 'resource_status' => 'resourceStatus', + 'resource_status_reason' => 'resourceStatusReason', + 'resource_type' => 'resourceType' + ); +} diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index bfe825e9f..23d0bbfba 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -64,4 +64,24 @@ protected function metadataUrl() { return $this->getParent()->url('metadata'); } + + + /** + * Returns a list of Events associated with this Resource + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listEvents(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Event::resourceName())->setQuery($params); + + return $this->getService()->resourceList('Event', $url, $this); + } + + public function getEvent($id) + { + return $this->getService()->resource('Event', $id, $this); + } } diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index e688a06db..36bd221a4 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -168,6 +168,28 @@ public function listResources(array $params = array()) return $this->getService()->resourceList('Resource', $url, $this); } + /** + * Returns a list of Events associated with this Stack + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listEvents(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Event::resourceName())->setQuery($params); + + return $this->getService()->resourceList('Event', $url, $this); + } + + /** + * Iterator use only + */ + public function event($id) + { + return $this->getService()->resource('Event', $id, $this); + } + protected function previewUrl() { return $this->getParent()->url('preview'); diff --git a/lib/OpenCloud/Orchestration/Resource/StackResourceEvent.php b/lib/OpenCloud/Orchestration/Resource/StackResourceEvent.php deleted file mode 100644 index d892d8f8c..000000000 --- a/lib/OpenCloud/Orchestration/Resource/StackResourceEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -resource->getMetadata(); $this->assertEquals("bar", $metadata->foo); } + + public function testListEvents() + { + $this->addMockSubscriber($this->makeResponse('{"events":[{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/474bfdf0-a450-46ec-a78a-0c7faa404073","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_FAILED","resource_status_reason":"NotFound: Subnet f8a699d0-3537-429e-87a5-6b5a8d0c2bf0 could not be found","physical_resource_id":null,"id":"474bfdf0-a450-46ec-a78a-0c7faa404073"},{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/66fa95b6-e6f8-4f05-b1af-e828f5aba04c","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_IN_PROGRESS","resource_status_reason":"state changed","physical_resource_id":null,"id":"66fa95b6-e6f8-4f05-b1af-e828f5aba04c"}]}')); + + $events = $this->resource->listEvents(); + $this->assertInstanceOf(self::COLLECTION_CLASS, $events); + + $firstEvent = $events->getElement(0); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); + $this->assertEquals('474bfdf0-a450-46ec-a78a-0c7faa404073', $firstEvent->getId()); + } + + public function testGetEvent() + { + $this->addMockSubscriber($this->makeResponse('{"event":{"event_time":"2014-01-31T20:08:15Z","id":"f4874455-6505-42b4-af84-114bba2483a5","links":[{"href":"http://hostname/v1/1234/stacks/mystack/56789/resources/lb/events/f4874455-6505-42b4-af84-114bba2483a5","rel":"self"},{"href":"http://hostname/v1/1234/stacks/mystack/56789/resources/lb","rel":"resource"},{"href":"http://hostname/v1/1234/stacks/mystack/56789","rel":"stack"}],"logical_resource_id":"lb","physical_resource_id":"098765","resource_name":"lb","resource_properties":{"accessList":null,"algorithm":"LEAST_CONNECTIONS","connectionLogging":null,"connectionThrottle":{"maxConnectionRate":50,"maxConnections":50,"minConnections":50,"rateInterval":50},"contentCaching":"ENABLED","errorPage":null,"halfClosed":false,"healthMonitor":{"attemptsBeforeDeactivation":3,"bodyRegex":".","delay":10,"hostHeader":null,"path":"/","statusRegex":".","timeout":10,"type":"HTTP"},"metadata":null,"name":"lb-Wordpress Webserver","nodes":[{"addresses":["1.2.3.4","4.5.6.7"],"condition":"ENABLED","port":80,"type":null,"weight":null}],"port":80,"protocol":"HTTP","sessionPersistence":"HTTP_COOKIE","sslTermination":null,"timeout":120,"virtualIps":[{"ipVersion":"IPV4","type":"PUBLIC"}]},"resource_status":"CREATE_COMPLETE","resource_status_reason":"state changed","resource_type":"Rackspace::Cloud::LoadBalancer"}}')); + + $event = $this->resource->getEvent('f4874455-6505-42b4-af84-114bba2483a5'); + $this->assertEquals('Rackspace::Cloud::LoadBalancer', $event->getResourceType()); + } } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index 67b5544d9..01b496f05 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -127,4 +127,16 @@ public function testGetResource() $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $resource); $this->assertEquals('A MySQL DB instance', $resource->getDescription()); } + + public function testListEvents() + { + $this->addMockSubscriber($this->makeResponse('{"events":[{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/474bfdf0-a450-46ec-a78a-0c7faa404073","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_FAILED","resource_status_reason":"NotFound: Subnet f8a699d0-3537-429e-87a5-6b5a8d0c2bf0 could not be found","physical_resource_id":null,"id":"474bfdf0-a450-46ec-a78a-0c7faa404073"},{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/66fa95b6-e6f8-4f05-b1af-e828f5aba04c","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_IN_PROGRESS","resource_status_reason":"state changed","physical_resource_id":null,"id":"66fa95b6-e6f8-4f05-b1af-e828f5aba04c"}]}')); + + $events = $this->stack->listEvents(); + $this->assertInstanceOf(self::COLLECTION_CLASS, $events); + + $firstEvent = $events->getElement(0); + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); + $this->assertEquals('474bfdf0-a450-46ec-a78a-0c7faa404073', $firstEvent->getId()); + } } From 17ef3d0fdb46a861e4db3259b7aca52007a446cf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 17:28:07 -0700 Subject: [PATCH 077/835] Fixing OpenStack client auth parameters. --- docs/userguide/Orchestration/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index fefe53a34..56f76a2a8 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -33,8 +33,8 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R use OpenCloud\OpenStack; $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' + 'username' => '', + 'password' => '' )); ``` From 40da39cfed5001f1a2680511820eabd5ddf27d0d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 17:28:22 -0700 Subject: [PATCH 078/835] Making README consistent with USERGUIDE. --- docs/userguide/Orchestration/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 5f12bf918..a963a84c7 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -31,10 +31,10 @@ Choose one of the following two options: ```php use OpenCloud\OpenStack; - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); ``` * If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. From cce775033b37fd5634cfcf162d7c3cea6bcc121c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 17:29:12 -0700 Subject: [PATCH 079/835] Using OS_PASSWORD instead of NOVA_API_KEY. --- samples/Orchestration/abandon-stack.php | 4 ++-- samples/Orchestration/adopt-stack.php | 4 ++-- samples/Orchestration/create-stack-from-template-file.php | 4 ++-- samples/Orchestration/create-stack-from-template-url.php | 4 ++-- samples/Orchestration/delete-stack.php | 4 ++-- samples/Orchestration/get-build-info.php | 4 ++-- samples/Orchestration/get-resource-type-template.php | 4 ++-- samples/Orchestration/get-resource-type.php | 4 ++-- samples/Orchestration/get-stack-by-name-and-id.php | 4 ++-- samples/Orchestration/get-stack-by-name.php | 4 ++-- samples/Orchestration/get-stack-resource-event.php | 4 ++-- samples/Orchestration/get-stack-resource-metadata.php | 4 ++-- samples/Orchestration/get-stack-resource.php | 4 ++-- samples/Orchestration/get-stack-template.php | 4 ++-- samples/Orchestration/list-resource-types.php | 4 ++-- samples/Orchestration/list-stack-events.php | 4 ++-- samples/Orchestration/list-stack-resource-events.php | 4 ++-- samples/Orchestration/list-stack-resources.php | 4 ++-- samples/Orchestration/list-stacks.php | 4 ++-- samples/Orchestration/preview-stack-from-template-file.php | 4 ++-- samples/Orchestration/preview-stack-from-template-url.php | 4 ++-- samples/Orchestration/quickstart.php | 4 ++-- samples/Orchestration/update-stack-from-template-file.php | 4 ++-- samples/Orchestration/update-stack-from-template-url.php | 4 ++-- .../Orchestration/validate-template-from-template-file.php | 4 ++-- samples/Orchestration/validate-template-from-template-url.php | 4 ++-- 26 files changed, 52 insertions(+), 52 deletions(-) diff --git a/samples/Orchestration/abandon-stack.php b/samples/Orchestration/abandon-stack.php index 8412d070f..97efd9c10 100644 --- a/samples/Orchestration/abandon-stack.php +++ b/samples/Orchestration/abandon-stack.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index eef11034c..487edd22d 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index e5dac4566..4ed9c73b9 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 93e551004..ebe6195b1 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index 7bf6ebe39..811e91036 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-build-info.php b/samples/Orchestration/get-build-info.php index a5cf7f040..7303435af 100644 --- a/samples/Orchestration/get-build-info.php +++ b/samples/Orchestration/get-build-info.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-resource-type-template.php b/samples/Orchestration/get-resource-type-template.php index 6190d2d8f..0c79dc661 100644 --- a/samples/Orchestration/get-resource-type-template.php +++ b/samples/Orchestration/get-resource-type-template.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * RESOURCE_TYPE_NAME: Name of resource type // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-resource-type.php b/samples/Orchestration/get-resource-type.php index 305b7419e..b85f9e956 100644 --- a/samples/Orchestration/get-resource-type.php +++ b/samples/Orchestration/get-resource-type.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * RESOURCE_TYPE_NAME: Name of resource type // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-by-name-and-id.php b/samples/Orchestration/get-stack-by-name-and-id.php index 421f799a7..fe10ee75e 100644 --- a/samples/Orchestration/get-stack-by-name-and-id.php +++ b/samples/Orchestration/get-stack-by-name-and-id.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // * STACK_ID: ID of stack @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-by-name.php b/samples/Orchestration/get-stack-by-name.php index 96751da2b..5abd220eb 100644 --- a/samples/Orchestration/get-stack-by-name.php +++ b/samples/Orchestration/get-stack-by-name.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-resource-event.php b/samples/Orchestration/get-stack-resource-event.php index a6870698e..898c4c37c 100644 --- a/samples/Orchestration/get-stack-resource-event.php +++ b/samples/Orchestration/get-stack-resource-event.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, // * STACK_NAME: Name of stack, // * STACK_RESOURCE_NAME: Name of resource in stack, and @@ -33,7 +33,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-resource-metadata.php b/samples/Orchestration/get-stack-resource-metadata.php index 74d1feab0..c342390c6 100644 --- a/samples/Orchestration/get-stack-resource-metadata.php +++ b/samples/Orchestration/get-stack-resource-metadata.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, // * STACK_NAME: Name of stack, // * STACK_RESOURCE_NAME: Name of resource in stack, and @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-resource.php b/samples/Orchestration/get-stack-resource.php index 4c7f206d2..f585a9d34 100644 --- a/samples/Orchestration/get-stack-resource.php +++ b/samples/Orchestration/get-stack-resource.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // * STACK_RESOURCE_NAME: Name of resource in stack @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-template.php b/samples/Orchestration/get-stack-template.php index 2c77baab3..81a863b73 100644 --- a/samples/Orchestration/get-stack-template.php +++ b/samples/Orchestration/get-stack-template.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 2744bdbca..58edb5c56 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index 2559dbe56..bca8901d7 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index c4d5f5488..cce05ed86 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, // * STACK_NAME: Name of stack, and // * STACK_RESOURCE_NAME: Name of resource in stack @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index 99a2241bb..cb33a88dd 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 607e8cd27..b9197c7e0 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/preview-stack-from-template-file.php b/samples/Orchestration/preview-stack-from-template-file.php index 57da568f4..7b9974cc8 100644 --- a/samples/Orchestration/preview-stack-from-template-file.php +++ b/samples/Orchestration/preview-stack-from-template-file.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/preview-stack-from-template-url.php b/samples/Orchestration/preview-stack-from-template-url.php index f5c8c2a23..cfbb07a68 100644 --- a/samples/Orchestration/preview-stack-from-template-url.php +++ b/samples/Orchestration/preview-stack-from-template-url.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index 4fbe6284f..d5ac6f8da 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -19,7 +19,7 @@ // Pre-requisites: // * Prior to running this script, you must setup the following environment variables: // * OS_USERNAME: Your Rackspace Cloud Account Username, -// * NOVA_API_KEY: Your Rackspace Cloud Account API Key, and +// * OS_PASSWORD: Your Rackspace Cloud Account Password, and // * OS_REGION_NAME: The Rackspace Cloud region you want to use // @@ -29,7 +29,7 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/update-stack-from-template-file.php b/samples/Orchestration/update-stack-from-template-file.php index bf19fe67b..77555ebfb 100644 --- a/samples/Orchestration/update-stack-from-template-file.php +++ b/samples/Orchestration/update-stack-from-template-file.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/update-stack-from-template-url.php b/samples/Orchestration/update-stack-from-template-url.php index 133608ff7..5d39b6916 100644 --- a/samples/Orchestration/update-stack-from-template-url.php +++ b/samples/Orchestration/update-stack-from-template-url.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, +// * OS_PASSWORD: Your OpenStack Cloud Account Password, // * OS_REGION_NAME: The OpenStack Cloud region you want to use, and // * STACK_NAME: Name of stack // @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/validate-template-from-template-file.php b/samples/Orchestration/validate-template-from-template-file.php index 6e4b07711..92ab34a39 100644 --- a/samples/Orchestration/validate-template-from-template-file.php +++ b/samples/Orchestration/validate-template-from-template-file.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/validate-template-from-template-url.php b/samples/Orchestration/validate-template-from-template-url.php index 0b9b1f0dc..d6bef70ca 100644 --- a/samples/Orchestration/validate-template-from-template-url.php +++ b/samples/Orchestration/validate-template-from-template-url.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, -// * NOVA_API_KEY: Your OpenStack Cloud Account API Key, and +// * OS_PASSWORD: Your OpenStack Cloud Account Password, and // * OS_REGION_NAME: The OpenStack Cloud region you want to use // @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('NOVA_API_KEY') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. From 0b319f3eeb6008b1fba3b88aca19e21da2b6a001 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 17:29:27 -0700 Subject: [PATCH 080/835] Using template file that exists. --- samples/Orchestration/validate-template-from-template-file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/validate-template-from-template-file.php b/samples/Orchestration/validate-template-from-template-file.php index 92ab34a39..9a0638a94 100644 --- a/samples/Orchestration/validate-template-from-template-file.php +++ b/samples/Orchestration/validate-template-from-template-file.php @@ -39,5 +39,5 @@ // 3. Validate template from file. $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/sample_template.yml') + 'template' => file_get_contents(__DIR__ . '/lamp.yml') )); From cfac8abf04b7ad7160d340ac37bff997ec1c0ce4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 17:30:52 -0700 Subject: [PATCH 081/835] Fixing typo in template file extension. --- samples/Orchestration/adopt-stack.php | 2 +- samples/Orchestration/create-stack-from-template-file.php | 2 +- samples/Orchestration/preview-stack-from-template-file.php | 2 +- samples/Orchestration/update-stack-from-template-file.php | 2 +- samples/Orchestration/validate-template-from-template-file.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index 487edd22d..d27c03937 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -40,7 +40,7 @@ // 3. Adopt a stack. $stack = $orchestrationService->adoptStack(array( 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), 'adoptStackData' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 4ed9c73b9..54d89b4bc 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -40,7 +40,7 @@ // 3. Create a stack. $stack = $orchestrationService->createStack(array( 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' diff --git a/samples/Orchestration/preview-stack-from-template-file.php b/samples/Orchestration/preview-stack-from-template-file.php index 7b9974cc8..f23fc18fd 100644 --- a/samples/Orchestration/preview-stack-from-template-file.php +++ b/samples/Orchestration/preview-stack-from-template-file.php @@ -40,7 +40,7 @@ // 3. Preview a stack. $stack = $orchestrationService->previewStack(array( 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' diff --git a/samples/Orchestration/update-stack-from-template-file.php b/samples/Orchestration/update-stack-from-template-file.php index 77555ebfb..a8ce37c87 100644 --- a/samples/Orchestration/update-stack-from-template-file.php +++ b/samples/Orchestration/update-stack-from-template-file.php @@ -43,7 +43,7 @@ // 4. Update stack. $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' diff --git a/samples/Orchestration/validate-template-from-template-file.php b/samples/Orchestration/validate-template-from-template-file.php index 9a0638a94..bb6932ade 100644 --- a/samples/Orchestration/validate-template-from-template-file.php +++ b/samples/Orchestration/validate-template-from-template-file.php @@ -39,5 +39,5 @@ // 3. Validate template from file. $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yml') + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') )); From efd93e435d40f656d8d45dcfdad82a49ffcc5eaa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 17:52:50 -0700 Subject: [PATCH 082/835] Throw exception if template is invalid instead of returning false (so reason can be encapsulated). --- docs/userguide/Orchestration/USERGUIDE.md | 28 ++++++++++++------- .../Exceptions/InvalidTemplateError.php | 22 +++++++++++++++ lib/OpenCloud/Orchestration/Service.php | 8 ++++-- .../validate-template-from-template-file.php | 11 ++++++-- .../validate-template-from-template-url.php | 11 ++++++-- .../Tests/Orchestration/ServiceTest.php | 17 ++++++----- 6 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 lib/OpenCloud/Common/Exceptions/InvalidTemplateError.php diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 56f76a2a8..d45cebdca 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -76,29 +76,37 @@ If your template is stored on your local computer as a JSON or YAML file, you can validate it as shown below: ```php -$orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yml') -)); +use OpenCloud\Common\Exceptions\InvalidTemplateError; + +try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); +} catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid +} ``` [ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] -If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseException`](http://api.guzzlephp.org/class-Guzzle.Http.Exception.ClientErrorResponseException.html) will be thrown. - #### Validate Template from URL If your template is stored in a remote location accessible via HTTP or HTTPS, as a JSON or YAML file, you can validate it as shown below: ```php -$orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' -)); +use OpenCloud\Common\Exceptions\InvalidTemplateError; + +try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); +} catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid +} ``` [ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] -If the template is invalid, a [`Guzzle\Http\Exception\ClientErrorResponseException`](http://api.guzzlephp.org/class-Guzzle.Http.Exception.ClientErrorResponseException.html) will be thrown. - ## Stacks A stack is a running instance of a template. When a stack is created, the [resources](#stack-resources) specified in the template are created. diff --git a/lib/OpenCloud/Common/Exceptions/InvalidTemplateError.php b/lib/OpenCloud/Common/Exceptions/InvalidTemplateError.php new file mode 100644 index 000000000..cfa1014ed --- /dev/null +++ b/lib/OpenCloud/Common/Exceptions/InvalidTemplateError.php @@ -0,0 +1,22 @@ +getClient()->post($url, self::getJsonHeader(), $json)->send(); - return true; } catch (ClientErrorResponseException $e) { - return false; + $response = Formatter::decode($e->getResponse()); + throw new InvalidTemplateError($response->explanation, $response->code); } } diff --git a/samples/Orchestration/validate-template-from-template-file.php b/samples/Orchestration/validate-template-from-template-file.php index bb6932ade..f31acebef 100644 --- a/samples/Orchestration/validate-template-from-template-file.php +++ b/samples/Orchestration/validate-template-from-template-file.php @@ -26,6 +26,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; +use OpenCloud\Common\Exceptions\InvalidTemplateError; // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( @@ -38,6 +39,10 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from file. -$orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') -)); +try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); +} catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid +} diff --git a/samples/Orchestration/validate-template-from-template-url.php b/samples/Orchestration/validate-template-from-template-url.php index d6bef70ca..f466ae18a 100644 --- a/samples/Orchestration/validate-template-from-template-url.php +++ b/samples/Orchestration/validate-template-from-template-url.php @@ -26,6 +26,7 @@ require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; +use OpenCloud\Common\Exceptions\InvalidTemplateError; // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( @@ -38,6 +39,10 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Validate template from URL -$orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' -)); +try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); +} catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid +} diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index 8d9b8a0c5..4193410fa 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -25,6 +25,7 @@ namespace OpenCloud\Tests\Orchestration; use OpenCloud\Orchestration\Service; +use OpenCloud\Common\Exceptions\InvalidTemplateError; class ServiceTest extends OrchestrationTestCase { @@ -112,17 +113,15 @@ public function testGetBuildInfo() $this->assertEquals('2014.j3-20141003-1139', $buildInfo->getApi()->revision); } - - public function testValidateValidTemplate() - { - $this->addMockSubscriber($this->makeResponse()); - $this->assertTrue($this->service->validateTemplate(array('template' => 'heat_template_version: 2013-05-23'))); - } - public function testValidateInvalidTemplate() { - $this->addMockSubscriber($this->makeResponse(null, 400)); - $this->assertFalse($this->service->validateTemplate(array('template' => 'foobar: baz'))); + $this->addMockSubscriber($this->makeResponse('{"explanation":"Template version not specified","code":400}', 400)); + try { + $this->service->validateTemplate(array('template' => 'foobar: baz')); + $this->fail("InvalidTemplateError exception should have been thrown!"); + } catch (InvalidTemplateError $e) { + $this->assertEquals("Template version not specified", $e->getMessage()); + } } } \ No newline at end of file From 29c5c51c15359e22cce200fba882ea20592d6db1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 18:17:57 -0700 Subject: [PATCH 083/835] Allow retrieval of stack by name only. --- docs/userguide/Orchestration/USERGUIDE.md | 21 +-------- lib/OpenCloud/Orchestration/Service.php | 12 ++--- .../get-stack-by-name-and-id.php | 44 ------------------- .../{get-stack-by-name.php => get-stack.php} | 0 4 files changed, 8 insertions(+), 69 deletions(-) delete mode 100644 samples/Orchestration/get-stack-by-name-and-id.php rename samples/Orchestration/{get-stack-by-name.php => get-stack.php} (100%) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index d45cebdca..f6160711f 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -227,30 +227,13 @@ foreach ($stacks as $stack) { [ [Get the executable PHP script for this example](/samples/Orchestration/list-stacks.php) ] ### Get Stack -You can retrieve a specific stack either by its name OR by its name and ID. - -This operation takes the following positional parameters: - -| Position | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| 1 | Name of the stack | String | Yes | | `simple-lamp-setup` | -| 2 | ID of the stack | String | No | | `1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619` | - -#### Get Stack by Name +You can retrieve a specific stack using its name, as shown below: ```php $stack = $orchestrationService->getStack('simple-lamp-setup'); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name.php) ] - -#### Get Stack by Name and ID - -```php -$stack = $orchestrationService->getStack('simple-lamp-setup', '1ded0b2a-36b6-4f7b-9a8b-b45acf3b5619'); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-by-name-and-id.php) ] +[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack.php) ] ### Get Stack Template You can retrieve the template used to create a stack. Please note that a JSON diff --git a/lib/OpenCloud/Orchestration/Service.php b/lib/OpenCloud/Orchestration/Service.php index 8c07adac1..61fc57fc8 100644 --- a/lib/OpenCloud/Orchestration/Service.php +++ b/lib/OpenCloud/Orchestration/Service.php @@ -41,12 +41,12 @@ class Service extends CatalogService /** * Returns a Stack object associated with this Orchestration service * - * @param string $id - the stack with the ID is retrieved + * @param string $name Name of stack to retrieve * @return Stack object */ - public function stack($id = null) + public function stack($name = null) { - return $this->resource('Stack', $id); + return $this->resource('Stack', $name); } /** @@ -91,12 +91,12 @@ public function adoptStack($params = array()) /** * Returns a Stack object associated with this Orchestration service * - * @param string $id - the stack with the ID is retrieved + * @param string $name Name of stack to retrieve * @return Stack object */ - public function getStack($id) + public function getStack($name) { - return $this->stack($id); + return $this->stack($name); } /** diff --git a/samples/Orchestration/get-stack-by-name-and-id.php b/samples/Orchestration/get-stack-by-name-and-id.php deleted file mode 100644 index fe10ee75e..000000000 --- a/samples/Orchestration/get-stack-by-name-and-id.php +++ /dev/null @@ -1,44 +0,0 @@ - getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') -)); - -// 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); - -// 3. Get stack using the stack name. -$stack = $orchestrationService->getStack(getenv('STACK_NAME'), getenv('STACK_ID')); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/get-stack-by-name.php b/samples/Orchestration/get-stack.php similarity index 100% rename from samples/Orchestration/get-stack-by-name.php rename to samples/Orchestration/get-stack.php From d98d3a19e8941df27f2229e60b2564a47ad598be Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 18:54:37 -0700 Subject: [PATCH 084/835] Adding links, thus obviating the need for overriding getUrl. --- lib/OpenCloud/Orchestration/Resource/Event.php | 1 + lib/OpenCloud/Orchestration/Resource/Resource.php | 1 + lib/OpenCloud/Orchestration/Resource/Stack.php | 11 +---------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Event.php b/lib/OpenCloud/Orchestration/Resource/Event.php index 068244fed..6fa89e825 100644 --- a/lib/OpenCloud/Orchestration/Resource/Event.php +++ b/lib/OpenCloud/Orchestration/Resource/Event.php @@ -35,6 +35,7 @@ class Event extends ReadOnlyResource protected $resourceStatus; protected $resourceStatusReason; protected $resourceType; + protected $links; protected $aliases = array( 'event_time' => 'time', diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index 23d0bbfba..fa235fccb 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -36,6 +36,7 @@ class Resource extends ReadOnlyResource protected $requiredBy; protected $updatedTime; protected $type; + protected $links; protected $aliases = array( 'resource_name' => 'name', diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index 36bd221a4..ad92fc0a3 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -43,6 +43,7 @@ class Stack extends PersistentResource protected $templateUrl; protected $template; protected $adoptStackData; + protected $links; protected $aliases = array( 'disable_rollback' => 'disableRollback', @@ -204,14 +205,4 @@ protected function primaryKeyField() { return 'name'; } - - public function getUrl($path = NULL, array $query = array()) - { - $url = parent::getUrl($path, $query); - if ($this->id) { - $url->addPath($this->id); - } - return $url; - } - } From 4ba400187dea3fc85a01ae618ac026bec14407c3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 19:04:07 -0700 Subject: [PATCH 085/835] Fixing metadata response. --- tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php index 602ac6a79..93bc86285 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php @@ -48,7 +48,7 @@ public function testDelete() public function testGetMetadata() { - $this->addMockSubscriber($this->makeResponse('{"foo":"bar","baz":17}')); + $this->addMockSubscriber($this->makeResponse('{"metadata":{"foo":"bar","baz":17}}')); $metadata = $this->resource->getMetadata(); $this->assertEquals("bar", $metadata->foo); } From 89cdd45fd175c53e1f30987c1468a0c6cb0f0f44 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 9 Oct 2014 19:04:32 -0700 Subject: [PATCH 086/835] Fixing metadata resource path. Parsing using Formatter. --- .../Orchestration/Resource/Resource.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index fa235fccb..6c4995a60 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -18,6 +18,7 @@ namespace OpenCloud\Orchestration\Resource; use OpenCloud\Common\Resource\ReadOnlyResource; +use OpenCloud\Common\Http\Message\Formatter; /** * @@ -56,16 +57,15 @@ public function primaryKeyField() public function getMetadata() { - $metadataUrl = $this->metadataUrl(); - $response = $this->getClient()->get($metadataUrl)->send(); - return json_decode($response->getBody(true)); - } - protected function metadataUrl() - { - return $this->getParent()->url('metadata'); - } + $url = clone $this->getUrl(); + $url->addPath('metadata'); + + $response = $this->getClient()->get($url)->send(); + $json = Formatter::decode($response); + return $json->metadata; + } /** * Returns a list of Events associated with this Resource From 9b91a2e4b05863874472512a43ac3ecfb75cbd73 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 07:24:34 -0700 Subject: [PATCH 087/835] Set the primary key field for the resource_type resource. --- lib/OpenCloud/Orchestration/Resource/ResourceType.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php index a122771d0..316304acf 100644 --- a/lib/OpenCloud/Orchestration/Resource/ResourceType.php +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -62,4 +62,9 @@ public function getTemplate() $response = $this->getClient()->get($url)->send(); return $response->getBody(); } + + protected function primaryKeyField() + { + return 'resourceType'; + } } From 2188054ba379357260ed6dd9706c31c0efe14d0f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 07:39:20 -0700 Subject: [PATCH 088/835] Return body as string. --- lib/OpenCloud/Orchestration/Resource/ResourceType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php index 316304acf..c3dd8f3d9 100644 --- a/lib/OpenCloud/Orchestration/Resource/ResourceType.php +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -60,7 +60,7 @@ public function getTemplate() $url->addPath('template'); $response = $this->getClient()->get($url)->send(); - return $response->getBody(); + return $response->getBody(true); } protected function primaryKeyField() From 9fefed2ffea6f3a608b3a528fff17eb09dcd6e1f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 09:00:41 -0700 Subject: [PATCH 089/835] Fixing class name. --- docs/userguide/Orchestration/USERGUIDE.md | 6 +++--- samples/Orchestration/get-stack-resource-event.php | 2 +- samples/Orchestration/list-stack-events.php | 2 +- samples/Orchestration/list-stack-resource-events.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index f6160711f..3bcbed78a 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -385,7 +385,7 @@ You can list all events for all resources in a stack as shown below: ```php $stackEvents = $stack->listEvents(); foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ } ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] @@ -396,7 +396,7 @@ You can list all events for a specific resource in a stack as shown below: ```php $resourceEvents = $resource->listEvents(); foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ } ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] @@ -407,7 +407,7 @@ the resource event's ID, as shown below: ```php $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -/** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ +/** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] diff --git a/samples/Orchestration/get-stack-resource-event.php b/samples/Orchestration/get-stack-resource-event.php index 898c4c37c..c5d3971d5 100644 --- a/samples/Orchestration/get-stack-resource-event.php +++ b/samples/Orchestration/get-stack-resource-event.php @@ -48,4 +48,4 @@ // 5. Get stack resource event. $resourceEvent = $resource->getEvent(getenv('STACK_RESOURCE_EVENT_ID')); -/** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ +/** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index bca8901d7..3c92d5391 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -44,5 +44,5 @@ // 4. Get list of events for the stack. $stackEvents = $stack->listEvents(); foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ } diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index cce05ed86..77a8cc64f 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -48,5 +48,5 @@ // 5. Get list of events for the stack resource. $resourceEvents = $resource->listEvents(); foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\ResourceEvent **/ + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ } From 0f641b3f6de0051fac53484657b5818ab9e316e1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 09:00:54 -0700 Subject: [PATCH 090/835] Adding method to return stack template. --- lib/OpenCloud/Orchestration/Resource/Stack.php | 14 ++++++++++++++ samples/Orchestration/get-stack-template.php | 2 +- .../Tests/Orchestration/Resource/StackTest.php | 10 ++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index ad92fc0a3..b35e46a79 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -191,6 +191,20 @@ public function event($id) return $this->getService()->resource('Event', $id, $this); } + /** + * Returns the template for this stack. + * + * @return String template + */ + public function getStackTemplate() + { + $url = clone $this->getUrl(); + $url->addPath('template'); + + $response = $this->getClient()->get($url)->send(); + return $response->getBody(true); + } + protected function previewUrl() { return $this->getParent()->url('preview'); diff --git a/samples/Orchestration/get-stack-template.php b/samples/Orchestration/get-stack-template.php index 81a863b73..fc646d03a 100644 --- a/samples/Orchestration/get-stack-template.php +++ b/samples/Orchestration/get-stack-template.php @@ -42,5 +42,5 @@ $stack = $orchestrationService->getStack(getenv('STACK_NAME')); // 4. Get stack template. -$stackTemplate = $stack->getTemplate(); +$stackTemplate = $stack->getStackTemplate(); /** @var $stackTemplate string **/ diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index 01b496f05..e1bd86838 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -139,4 +139,14 @@ public function testListEvents() $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); $this->assertEquals('474bfdf0-a450-46ec-a78a-0c7faa404073', $firstEvent->getId()); } + + public function testGetTemplate() + { + $expectedTemplateStr = '{"description":"Heat Orchestration Template that spins up a single server","heat_template_version":"2013-05-23","parameters":{"compute_flavor":{"constraints":[{"allowed_values":["1 GB Performance","2 GB Performance","4 GB Performance","8 GB Performance","16 GB Performance"],"description":"Must be a valid Rackspace Cloud Server flavor."}],"default":"2 GB Performance","description":"flavor id for the compute instance","type":"String"},"compute_image":{"constraints":[{"allowed_values":["Ubuntu 13.10 (Saucy Salamander)","Ubuntu 12.10 (Quantal Quetzal)"],"description":"Must be a valid Rackspace Cloud Server image name."}],"default":"Ubuntu 13.10 (Saucy Salamander)","description":"The Image to use for the host OS.","type":"String"}},"resources":{"server_instance":{"type":"OS::Nova::Server","properties":{"flavor":{"get_param":"compute_flavor"},"image":{"get_param":"compute_image"},"name":"Single Node Compute Instance"}}}}'; + + $this->addMockSubscriber($this->makeResponse($expectedTemplateStr)); + + $actualTemplateStr = $this->stack->getStackTemplate(); + $this->assertEquals($expectedTemplateStr, $actualTemplateStr); + } } From c81afc5e54ccfb283efd5b6178ecfc15d62ff7ba Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 11:02:30 -0700 Subject: [PATCH 091/835] Adding resourceProperties. --- lib/OpenCloud/Orchestration/Resource/Event.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Event.php b/lib/OpenCloud/Orchestration/Resource/Event.php index 6fa89e825..7856cafb2 100644 --- a/lib/OpenCloud/Orchestration/Resource/Event.php +++ b/lib/OpenCloud/Orchestration/Resource/Event.php @@ -34,6 +34,7 @@ class Event extends ReadOnlyResource protected $resourcePhysicalId; protected $resourceStatus; protected $resourceStatusReason; + protected $resourceProperties; protected $resourceType; protected $links; @@ -44,6 +45,7 @@ class Event extends ReadOnlyResource 'physical_resource_id' => 'resourcePhysicalId', 'resource_status' => 'resourceStatus', 'resource_status_reason' => 'resourceStatusReason', - 'resource_type' => 'resourceType' + 'resource_type' => 'resourceType', + 'resource_properties' => 'resourceProperties' ); } From 129dffda629f1741910f635a23a27c4daf573124 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 12:30:22 -0700 Subject: [PATCH 092/835] Adding updated template for stack update operation. --- docs/userguide/Orchestration/USERGUIDE.md | 6 +- samples/Orchestration/lamp-updated.yaml | 184 ++++++++++++++++++ .../update-stack-from-template-file.php | 2 +- 3 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 samples/Orchestration/lamp-updated.yaml diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 3bcbed78a..88e046d53 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -254,7 +254,7 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | | `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | +| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | | `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | | `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | | 5 | @@ -265,7 +265,7 @@ can use it to update a stack as shown below: ```php $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' @@ -283,7 +283,7 @@ as a JSON or YAML file, you can use it to update a stack as shown below: ```php $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' diff --git a/samples/Orchestration/lamp-updated.yaml b/samples/Orchestration/lamp-updated.yaml new file mode 100644 index 000000000..feebf366c --- /dev/null +++ b/samples/Orchestration/lamp-updated.yaml @@ -0,0 +1,184 @@ +heat_template_version: 2013-05-23 + +description: | + This is a Heat template to deploy a server with LAMP + +parameter_groups: +- label: Server Settings + parameters: + - server_hostname + - image + - flavor + +- label: phpMyAdmin Settings + parameters: + - phpmyadmin_user + +- label: rax-dev-params + parameters: + - kitchen + - chef_version + +parameters: + server_hostname: + label: Server Name + description: Hostname to use for setting the server name. + type: string + default: web + constraints: + - length: + min: 1 + max: 64 + - allowed_pattern: "^[a-zA-Z0-9]([a-zA-Z0-9.-])*$" + description: | + Must begin with a letter or number and be alphanumeric or '-' and '.' + + image: + label: Operating System + description: | + Required: Server image used for all servers that are created as a part of + this deployment. + type: string + default: CentOS 6.5 (PVHVM) + constraints: + - allowed_values: + - CentOS 6.5 (PVHVM) + - Red Hat Enterprise Linux 6.5 (PVHVM) + - Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM) + - Ubuntu 12.04 LTS (Precise Pangolin) + - Debian 7 (Wheezy) (PVHVM) + description: Must be a supported operating system. + + flavor: + label: Server Size + description: | + Required: Rackspace Cloud Server flavor to use. The size is based on the + amount of RAM for the provisioned server. + type: string + default: 1 GB Performance + constraints: + - allowed_values: + - 1 GB Performance + - 2 GB Performance + - 4 GB Performance + - 8 GB Performance + - 15 GB Performance + - 30 GB Performance + description: | + Must be a valid Rackspace Cloud Server flavor for the region you have + selected to deploy into. + + phpmyadmin_user: + label: Username + description: "Username for phpMyAdmin logins." + type: string + default: serverinfo + constraints: + - allowed_pattern: "^(.){1,16}$" + description: | + Must be shorter than 16 characters, this is due to MySQL's maximum + username length. + + kitchen: + description: URL for the kitchen to use + type: string + default: https://github.com/rackspace-orchestration-templates/lamp + + chef_version: + description: Version of chef client to use + type: string + default: 11.12.8 + +resources: + + ssh_key: + type: "OS::Nova::KeyPair" + properties: + name: { get_param: "OS::stack_id" } + save_private_key: true + + mysql_root_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_repl_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_debian_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + phpmyadmin_pass: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + linux_server: + type: "Rackspace::Cloud::Server" + properties: + name: { get_param: server_hostname } + flavor: { get_param: flavor } + image: { get_param: image } + key_name: { get_resource: ssh_key } + config_drive: "true" + metadata: + rax-heat: { get_param: "OS::stack_id" } + metadata: + foo: bar + baz: qux + + linux_setup: + type: "OS::Heat::ChefSolo" + depends_on: linux_server + properties: + username: root + private_key: { get_attr: [ssh_key, private_key] } + host: { get_attr: [linux_server, accessIPv4] } + kitchen: { get_param: kitchen } + chef_version: { get_param: chef_version } + node: + mysql: + server_root_password: { get_attr: [mysql_root_password, value] } + server_repl_password: { get_attr: [mysql_repl_password, value] } + server_debian_password: { get_attr: [mysql_debian_password, value] } + phpmyadmin: + pass: { get_attr: [phpmyadmin_pass, value] } + user: { get_param: phpmyadmin_user } + run_list: ["recipe[LAMP]"] + +outputs: + private_key: + description: SSH Private Key + value: { get_attr: [ssh_key, private_key] } + + server_ip: + description: Server IP + value: { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_url: + description: phpMyAdmin URL + value: + str_replace: + template: "http://%server_ip%/phpmyadmin" + params: + "%server_ip%": { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_user: + description: phpMyAdmin User + value: { get_param: phpmyadmin_user } + + phpmyadmin_password: + description: phpMyAdmin Password + value: { get_attr: [phpmyadmin_pass, value] } + + mysql_root_password: + description: MySQL Root Password + value: { get_attr: [mysql_root_password, value] } diff --git a/samples/Orchestration/update-stack-from-template-file.php b/samples/Orchestration/update-stack-from-template-file.php index a8ce37c87..93318e097 100644 --- a/samples/Orchestration/update-stack-from-template-file.php +++ b/samples/Orchestration/update-stack-from-template-file.php @@ -43,7 +43,7 @@ // 4. Update stack. $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yaml'), 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' From 734d20e949d9ddd522e87e51b414cfafd92a3543 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 13:31:41 -0700 Subject: [PATCH 093/835] Fixing preview URL. --- lib/OpenCloud/Orchestration/Resource/Stack.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index b35e46a79..7a187903a 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -207,7 +207,11 @@ public function getStackTemplate() protected function previewUrl() { - return $this->getParent()->url('preview'); + $url = clone $this->getParent()->getUrl(); + $url->addPath(self::resourceName()); + $url->addPath('preview'); + + return $url; } protected function abandonUrl() From 974fb2dd8a0997a3de9db47a26be7f3bb0ec82e7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 13:48:17 -0700 Subject: [PATCH 094/835] Save abandoned stack data to file (so it could be later used with adopt stack). --- docs/userguide/Orchestration/USERGUIDE.md | 2 ++ samples/Orchestration/abandon-stack.php | 1 + 2 files changed, 3 insertions(+) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 88e046d53..b1d01bcd2 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -312,6 +312,8 @@ it as shown below: ```php $abandonStackData = $stack->abandon(); /** @var $abandonStackData string **/ + +file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/abandon-stack.php) ] diff --git a/samples/Orchestration/abandon-stack.php b/samples/Orchestration/abandon-stack.php index 97efd9c10..ef27082fe 100644 --- a/samples/Orchestration/abandon-stack.php +++ b/samples/Orchestration/abandon-stack.php @@ -45,3 +45,4 @@ $abandonStackData = $stack->abandon(); /** @var $abandonStackData string **/ +file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); From 45ae2b0e61c30f6fa779de8606888b47c9149c79 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 10 Oct 2014 13:48:42 -0700 Subject: [PATCH 095/835] Fixing abandon stack URL. --- lib/OpenCloud/Orchestration/Resource/Stack.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index 7a187903a..9c9841e76 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -216,7 +216,10 @@ protected function previewUrl() protected function abandonUrl() { - return $this->getParent()->url('abandon'); + $url = clone $this->getUrl(); + $url->addPath('abandon'); + + return $url; } protected function primaryKeyField() From 1688b9864d791c2a74c130b18b562f3d3214717b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 13 Oct 2014 09:57:08 -0700 Subject: [PATCH 096/835] Replacing IDs with fake strings. --- tests/OpenCloud/Tests/_response/Auth.resp | 54 +++++++++++------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/OpenCloud/Tests/_response/Auth.resp b/tests/OpenCloud/Tests/_response/Auth.resp index a6ff3fb5e..ce1ea5efc 100644 --- a/tests/OpenCloud/Tests/_response/Auth.resp +++ b/tests/OpenCloud/Tests/_response/Auth.resp @@ -14,29 +14,29 @@ Front-End-Https: on { "endpoints": [ { - "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://cdn2.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "ORD", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "publicURL": "https://cdn1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://cdn1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "DFW", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "publicURL": "https://cdn4.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://cdn4.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "SYD", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "publicURL": "https://cdn5.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://cdn5.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "IAD", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "publicURL": "https://cdn6.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "publicURL": "https://cdn6.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "HKG", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } ], "name": "cloudFilesCDN", @@ -45,34 +45,34 @@ Front-End-Https: on { "endpoints": [ { - "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", - "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "internalURL": "https://snet-storage101.ord1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + "publicURL": "https://storage101.ord1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "ORD", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "internalURL": "https://snet-storage101.syd2.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", - "publicURL": "https://storage101.syd2.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "internalURL": "https://snet-storage101.syd2.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + "publicURL": "https://storage101.syd2.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "SYD", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "internalURL": "https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", - "publicURL": "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "internalURL": "https://snet-storage101.dfw1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + "publicURL": "https://storage101.dfw1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "DFW", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "internalURL": "https://snet-storage101.iad3.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", - "publicURL": "https://storage101.iad3.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "internalURL": "https://snet-storage101.iad3.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + "publicURL": "https://storage101.iad3.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "IAD", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { - "internalURL": "https://snet-storage101.hkg1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", - "publicURL": "https://storage101.hkg1.clouddrive.com/v1/MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa", + "internalURL": "https://snet-storage101.hkg1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", + "publicURL": "https://storage101.hkg1.clouddrive.com/v1/MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "region": "HKG", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } ], "name": "cloudFiles", @@ -355,7 +355,7 @@ Front-End-Https: on "APIKEY" ], "expires": "2013-11-13T10:49:29.409Z", - "id": "cc81a27bf50249e5b5878d8b51f2eea6", + "id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "tenant": { "id": "123456", "name": "123456" @@ -380,7 +380,7 @@ Front-End-Https: on "description": "A Role that allows a user access to keystone Service methods", "id": "5", "name": "object-store:default", - "tenantId": "MossoCloudFS_b42b6e1f-9ec5-4003-91f6-a7667ac19cfa" + "tenantId": "MossoCloudFS_XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { "description": "A Role that allows a user access to keystone Service methods", From 340cb41debff604681df9d20556dc7a63f663283 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 13 Oct 2014 11:40:27 -0700 Subject: [PATCH 097/835] Adding explanation of arguments for orchestrationService method. --- docs/userguide/Orchestration/USERGUIDE.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index b1d01bcd2..55646557e 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -50,7 +50,14 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R ``` ### Orchestration Service -All orchestration operations are done via an orchestration service object. +All orchestration operations are done via an orchestration service object. To +instantiate this object, call the `orchestrationService` method on the `$client` +object. This method takes two arguments: + +| Position | Description | Data type | Required? | Default value | Example value | +| -------- | ----------- | ----------| --------- | ------------- | ------------- | +| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudOrchestration` | +| 2 | Cloud region | String | Yes | - | `DFW` | ```php $region = 'DFW'; From 18a5f38af51afa194ce8634afc9392822e9bee16 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 13 Oct 2014 11:47:21 -0700 Subject: [PATCH 098/835] Adding comments for resource classes. --- lib/OpenCloud/Orchestration/Resource/BuildInfo.php | 3 +++ lib/OpenCloud/Orchestration/Resource/Event.php | 3 +++ lib/OpenCloud/Orchestration/Resource/Resource.php | 3 +++ lib/OpenCloud/Orchestration/Resource/ResourceType.php | 3 +++ lib/OpenCloud/Orchestration/Resource/Stack.php | 3 +++ 5 files changed, 15 insertions(+) diff --git a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php index 2b50eb5b8..04152794c 100644 --- a/lib/OpenCloud/Orchestration/Resource/BuildInfo.php +++ b/lib/OpenCloud/Orchestration/Resource/BuildInfo.php @@ -20,7 +20,10 @@ use OpenCloud\Common\Resource\ReadOnlyResource; /** + * Class that represents the current Orchestration service build. + * @see http://developer.openstack.org/api-ref-orchestration-v1.html#build-info * + * @package OpenCloud\Orchestration\Resource */ class BuildInfo extends ReadOnlyResource { diff --git a/lib/OpenCloud/Orchestration/Resource/Event.php b/lib/OpenCloud/Orchestration/Resource/Event.php index 7856cafb2..49371b06b 100644 --- a/lib/OpenCloud/Orchestration/Resource/Event.php +++ b/lib/OpenCloud/Orchestration/Resource/Event.php @@ -20,7 +20,10 @@ use OpenCloud\Common\Resource\ReadOnlyResource; /** + * Class that represents an event associated with a resource in a stack. + * @see http://developer.openstack.org/api-ref-orchestration-v1.html#stack-events * + * @package OpenCloud\Orchestration\Resource */ class Event extends ReadOnlyResource { diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index 6c4995a60..15aa3b36a 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -21,7 +21,10 @@ use OpenCloud\Common\Http\Message\Formatter; /** + * Class that represents a resource in a stack. + * @see http://developer.openstack.org/api-ref-orchestration-v1.html#stack-resources * + * @package OpenCloud\Orchestration\Resource */ class Resource extends ReadOnlyResource { diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php index c3dd8f3d9..1d8717af7 100644 --- a/lib/OpenCloud/Orchestration/Resource/ResourceType.php +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -20,7 +20,10 @@ use OpenCloud\Common\Resource\ReadOnlyResource; /** + * Class that represents a type of resource that can be managed by the Orchestration service. + * @see http://developer.openstack.org/api-ref-orchestration-v1.html#stack-resources * + * @package OpenCloud\Orchestration\Resource */ class ResourceType extends ReadOnlyResource { diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index 9c9841e76..8e053aed4 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -20,7 +20,10 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * Class that represents a stack. + * @see http://developer.openstack.org/api-ref-orchestration-v1.html#stacks * + * @package OpenCloud\Orchestration\Resource */ class Stack extends PersistentResource { From 44cf2f188d1969c864e276b6e2d4d50b36992fd0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 13 Oct 2014 12:24:03 -0700 Subject: [PATCH 099/835] Removing timeout from preview stack operation. --- docs/userguide/Orchestration/USERGUIDE.md | 6 ++---- samples/Orchestration/preview-stack-from-template-file.php | 3 +-- samples/Orchestration/preview-stack-from-template-url.php | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 55646557e..51dfa9032 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -144,8 +144,7 @@ $stack = $orchestrationService->previewStack(array( 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 + ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` @@ -164,8 +163,7 @@ $stack = $orchestrationService->previewStack(array( 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 + ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` diff --git a/samples/Orchestration/preview-stack-from-template-file.php b/samples/Orchestration/preview-stack-from-template-file.php index f23fc18fd..fce13084b 100644 --- a/samples/Orchestration/preview-stack-from-template-file.php +++ b/samples/Orchestration/preview-stack-from-template-file.php @@ -44,7 +44,6 @@ 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 + ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/preview-stack-from-template-url.php b/samples/Orchestration/preview-stack-from-template-url.php index cfbb07a68..d24e40c5c 100644 --- a/samples/Orchestration/preview-stack-from-template-url.php +++ b/samples/Orchestration/preview-stack-from-template-url.php @@ -44,7 +44,6 @@ 'parameters' => array( 'server_hostname' => 'web01', 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 + ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ From 21304c9473c18b03978f3714f05326f7e5851540 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 13 Oct 2014 12:42:47 -0700 Subject: [PATCH 100/835] Making changes suggested by our editor, Kelly Holcomb. --- docs/userguide/Orchestration/USERGUIDE.md | 232 +++++++++++----------- 1 file changed, 117 insertions(+), 115 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 51dfa9032..b73d03f6b 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -1,33 +1,32 @@ -# The Complete User Guide to the Orchestration Service +# Complete User Guide for the Orchestration Service -Orchestration is a service that can be used to create and manage cloud resources. -Examples of such resources are databases, load balancers, servers and software -installed on them. +Orchestration is a service that you can use to create and manage cloud resources +such as databases, load balancers, and servers, and the software installed on +servers. ## Concepts -To use the Orchestration service effectively, you should understand several key -concepts: +To use the Orchestration service effectively, you should understand the following +key concepts: -* **Template**: An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment. The template specifies what resources should be used, what attributes -of these resources are parameterized and what information is output to the user -when a template is instantiated. +* **Template**: A JSON or YAML document that describes how a set of resources should +be assembled to produce a working deployment. The template specifies the resources +to use, the attributes of these resources that are parameterizedn and the information +that is sent to the user when a template is instantiated. -* **Resource**: A resource is a template artifact that represents some component -of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a -load balancer, some configuration management system, and so forth). +* **Resource**: A template artifact that represents some component of your architecture +(a cloud server, a group of scaled cloud servers, a load balancer, some configuration +management system, and so on). -* **Stack**: A stack is a running instance of a template. When a stack is created, -the resources specified in the template are created. +* **Stack**: A running instance of a template. When a stack is created, the resources +specified in the template are created. ## Prerequisites ### Client To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. +* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: ```php use OpenCloud\OpenStack; @@ -38,7 +37,7 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R )); ``` -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: ```php use OpenCloud\Rackspace; @@ -49,8 +48,8 @@ To use the Orchestration service, you must first instantiate a `OpenStack` or `R )); ``` -### Orchestration Service -All orchestration operations are done via an orchestration service object. To +### Orchestration service +All Orchestration operations are done via an _orchestration service object_. To instantiate this object, call the `orchestrationService` method on the `$client` object. This method takes two arguments: @@ -59,28 +58,29 @@ object. This method takes two arguments: | 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudOrchestration` | | 2 | Cloud region | String | Yes | - | `DFW` | + +In the following example, you are connecting to the ``DFW`` region of the cloud. +Any resources and stacks created with this `$orchestrationService` instance are +created in that region. + ```php $region = 'DFW'; $orchestrationService = $client->orchestrationService(null, $region); ``` -In the example above, you are connecting to the ``DFW`` region of the cloud. Any -resources and stacks created with this `$orchestrationService` instance will be -created in that cloud region. - ## Templates An Orchestration template is a JSON or YAML document that describes how a set of resources should be assembled to produce a working -deployment (known as a [stack](#stacks)). The template specifies what resources -should be used, what attributes of these resources are parameterized and what -information is output to the user when a template is instantiated. +deployment (known as a [stack](#stacks)). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. -### Validate Template -Prior to actually _using_ a template to create a stack, you may want to validate it. +### Validate template +Before you use a template to create a stack, you might want to validate it. -#### Validate Template from File +#### Validate a template from a file If your template is stored on your local computer as a JSON or YAML file, you -can validate it as shown below: +can validate it as shown in the following example: ```php use OpenCloud\Common\Exceptions\InvalidTemplateError; @@ -97,8 +97,8 @@ try { [ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] #### Validate Template from URL -If your template is stored in a remote location accessible via HTTP or HTTPS, -as a JSON or YAML file, you can validate it as shown below: +If your template is stored as a JSON or YAML file in a remote location accessible +via HTTP or HTTPS, you can validate it as shown in the following example: ```php use OpenCloud\Common\Exceptions\InvalidTemplateError; @@ -118,24 +118,23 @@ try { A stack is a running instance of a template. When a stack is created, the [resources](#stack-resources) specified in the template are created. -### Preview Stack -Before _actually_ creating a stack from a template, you may want to simply -get a glimpse of what that stack might look like. This is called previewing the -stack. +### Preview stack +Before you create a stack from a template, you might want to see what that stack +will look like. This is called _previewing the stack_. This operation takes one parameter, an associative array, with the following keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `simple-lamp-setup` | -| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | +| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters | Yes | - | `simple-lamp-setup` | +| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | +| `templateUrl` | URL of the template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null` | `array('flavor_id' => 'performance1_1')` | -#### Preview Stack from Template File +#### Preview a stack from a template file If your template is stored on your local computer as a JSON or YAML file, you -can use it to preview a stack as shown below: +can use it to preview a stack as shown in the following example: ```php $stack = $orchestrationService->previewStack(array( @@ -151,10 +150,11 @@ $stack = $orchestrationService->previewStack(array( [ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-file.php) ] -#### Preview Stack from Template URL +#### Preview a stack from a template URL -If your template is stored in a remote location accessible via HTTP or HTTPS, -as a JSON or YAML file, you can use it to preview a stack as shown below: +If your template is stored as a JSON or YAML file in a remote location accessible +via HTTP or HTTPS, you can use it to preview a stack as shown in the following +example: ```php $stack = $orchestrationService->previewStack(array( @@ -170,23 +170,23 @@ $stack = $orchestrationService->previewStack(array( [ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-url.php) ] -### Create Stack +### Create stack You can create a stack from a template. This operation takes one parameter, an associative array, with the following keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with a alphabet. Must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | | `simple-lamp-setup` | -| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('server_hostname' => 'web01')` | -| `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | | 5 | +| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | - | `simple-lamp-setup` | +| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | +| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null` | `array('server_hostname' => 'web01')` | +| `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -#### Create Stack from Template File +#### Create a stack from a template file If your template is stored on your local computer as a JSON or YAML file, you -can use it to create a stack as shown below: +can use it to create a stack as shown in the following example: ```php $stack = $orchestrationService->createStack(array( @@ -202,9 +202,10 @@ $stack = $orchestrationService->createStack(array( ``` [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-file.php) ] -#### Create Stack from Template URL -If your template is stored in a remote location accessible via HTTP or HTTPS, -as a JSON or YAML file, you can use it to create a stack as shown below: +#### Create a stack from a template URL +If your template is stored as a JSON or YAML file in a remote location accessible +via HTTP or HTTPS, you can use it to create a stack as shown in the following +example: ```php $stack = $orchestrationService->stack(); @@ -220,8 +221,8 @@ $stack->create(array( ``` [ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-url.php) ] -### List Stacks -You can list all the stacks that you have created as shown below: +### List stacks +You can list all the stacks that you have created as shown in the following example: ```php $stacks = $orchestrationService->listStacks(); @@ -231,8 +232,8 @@ foreach ($stacks as $stack) { ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stacks.php) ] -### Get Stack -You can retrieve a specific stack using its name, as shown below: +### Get stack +You can retrieve a specific stack using its name, as shown in the following example: ```php $stack = $orchestrationService->getStack('simple-lamp-setup'); @@ -240,10 +241,9 @@ $stack = $orchestrationService->getStack('simple-lamp-setup'); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack.php) ] -### Get Stack Template -You can retrieve the template used to create a stack. Please note that a JSON -string is returned, regardless of whether a JSON or YAML template was used to -create the stack. +### Get stack template +You can retrieve the template used to create a stack. Note that a JSON string is +returned, regardless of whether a JSON or YAML template was used to create the stack. ```php $stackTemplate = $stack->getTemplate(); @@ -251,22 +251,22 @@ $stackTemplate = $stack->getTemplate(); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-template.php) ] -### Update Stack +### Update stack You can update a running stack. This operation takes one parameter, an associative array, with the following keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `template` | Template contents | String, JSON or YAML | No, if `templateUrl` is specified | | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String, HTTP or HTTPS URL | No, if `template` is specified | | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | | `array('flavor_id' => 'performance1_1')` | -| `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | | 5 | +| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | +| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null`| `array('flavor_id' => 'performance1_1')` | +| `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -#### Update Stack from Template File +#### Update a stack from a template file If your template is stored on your local computer as a JSON or YAML file, you -can use it to update a stack as shown below: +can use it to update a stack as shown in the following example: ```php $stack->update(array( @@ -283,8 +283,9 @@ $stack->update(array( #### Update Stack from Template URL -If your template is stored in a remote location accessible via HTTP or HTTPS, -as a JSON or YAML file, you can use it to update a stack as shown below: +If your template is stored as a JSON or YAML file in a remote location accessible +via HTTP or HTTPS, you can use it to update a stack as shown in the following +example: ```php $stack->update(array( @@ -299,10 +300,10 @@ $stack->update(array( ``` [ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-url.php) ] -### Delete Stack +### Delete stack -If you no longer need a stack and all its resources, you can delete it as shown -below: +If you no longer need a stack and all its resources, you can delete the stack +_and_ the resources as shown in the following example: ```php $stack->delete(); @@ -311,8 +312,8 @@ $stack->delete(); ### Abandon Stack -If you want to delete the stack but preserve all its resources, you can abandon -it as shown below: +If you want to delete a stack but preserve all its resources, you can abandon +the stack as shown in the following example: ```php $abandonStackData = $stack->abandon(); @@ -322,14 +323,14 @@ file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/abandon-stack.php) ] -Note that this operation returns data about the abandoned stack as a string. This -data may be used to recreate the stack using the [Adopt Stack](#adopt-stack) +Note that this operation returns data about the abandoned stack as a string. You +can use this data to recreate the stack by using the [adopt stack](#adopt-stack) operation. -### Adopt Stack +### Adopt stack -If you have data from an abandoned stack, you may adopt it and recreate the stack -as shown below: +If you have data from an abandoned stack, you can re-create the stack as shown +in the following example: ```php $stack = $orchestrationService->adoptStack(array( @@ -342,14 +343,14 @@ $stack = $orchestrationService->adoptStack(array( ``` [ [Get the executable PHP script for this example](/samples/Orchestration/adopt-stack.php) ] -## Stack Resources +## Stack resources -A stack is made up of zero or more resources. Examples of resources are databases, -load balancers, servers or the software installed on servers. +A stack is made up of zero or more resources such as databases, load balancers, +and servers, and the software installed on servers. -### List stack Resources +### List stack resources -You can list all the resources for a stack as shown below: +You can list all the resources for a stack as shown in the following example: ```php $resources = $stack->listResources(); @@ -359,35 +360,35 @@ foreach ($resources as $resource) { ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] -### Get Stack Resource +### Get stack resource -You can retrieve a specific resource in a stack using that resource's name, as shown -below: +You can retrieve a specific resource in a stack bt using that resource's name, +as shown in the following example: ```php -// Get resource in stack. $resource = $stack->getResource('load-balancer'); /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] -### Get Stack Resource Metadata +### Get stack resource metadata -You can retrieve the metadata for a specific resource in a stack as shown below: +You can retrieve the metadata for a specific resource in a stack as shown in the +following example: ```php -// Get stack resource metadata. $resourceMetadata = $resource->getMetadata(); /** @var $resourceMetadata \stdClass **/ ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] -## Stack Resource Events -Operations on resources within a stack (such as creation of a resource) produce +## Stack resource events +Operations on resources within a stack (such as the creation of a resource) produce events. -### List Stack Events -You can list all events for all resources in a stack as shown below: +### List stack events +You can list all of the events for all of the resources in a stack as shown in +the following example: ```php $stackEvents = $stack->listEvents(); @@ -397,8 +398,9 @@ foreach ($stackEvents as $stackEvent) { ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] -### List Stack Resource Events -You can list all events for a specific resource in a stack as shown below: +### List stack resource events +You can list all of the events for a specific resource in a stack as shown in the +following example: ```php $resourceEvents = $resource->listEvents(); @@ -408,9 +410,9 @@ foreach ($resourceEvents as $resourceEvent) { ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] -### Get Stack Resource Event +### Get stack resource event You can retrieve a specific event for a specific resource in a stack, by using -the resource event's ID, as shown below: +the resource event's ID, as shown in the following example: ```php $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); @@ -418,11 +420,11 @@ $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] -## Resource Types -When defining a template, resource types supported by your Cloud must be used. +## Resource types +When you define a template, you must use resource types supported by your cloud. -### List Resource Types -You can list all supported resource types as shown below: +### List resource types +You can list all supported resource types as shown in the following example: ```php $resourceTypes = $orchestrationService->listResourceTypes(); @@ -432,8 +434,8 @@ foreach ($resourceTypes as $resourceType) { ``` [ [Get the executable PHP script for this example](/samples/Orchestration/list-resource-types.php) ] -### Get Resource Type -You can retrieve a specific resource type's schema as shown below: +### Get resource type +You can retrieve a specific resource type's schema as shown in the following example: ```php $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); @@ -441,9 +443,9 @@ $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type.php) ] -### Get Resource Type Template +### Get resource type template You can retrieve a specific resource type's representation as it would appear -in a template, as shown below: +in a template, as shown in the following example: ```php $resourceTypeTemplate = $resourceType->getTemplate(); @@ -451,11 +453,11 @@ $resourceTypeTemplate = $resourceType->getTemplate(); ``` [ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type-template.php) ] -## Build Info +## Build info -### Get Build Info +### Get build info You can retrieve information about the current Orchestration service build as -shown below: +shown in the following example: ```php $buildInfo = $orchestrationService->getBuildInfo(); From 2a693b3ed00d1cf3daef1a2142407ee4c1dcd13d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 14 Oct 2014 11:12:02 -0700 Subject: [PATCH 101/835] Adding sample code for creating a server and retrieving its IP address. --- samples/Compute/create_server_and_get_ip.php | 86 ++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 samples/Compute/create_server_and_get_ip.php diff --git a/samples/Compute/create_server_and_get_ip.php b/samples/Compute/create_server_and_get_ip.php new file mode 100644 index 000000000..46b1d0dcf --- /dev/null +++ b/samples/Compute/create_server_and_get_ip.php @@ -0,0 +1,86 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Create Compute service +$region = 'ORD'; +$service = $client->computeService(null, $region); + +// 3. Get empty server +$server = $service->server(); + +// 4. Select an OS image +$images = $service->imageList(); +foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntuImage = $image; + break; + } +} + +// 5. Select a hardware flavor +$flavors = $service->flavorList(); +foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } +} + +// 6. Create +try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntuImage, + 'flavor' => $twoGbFlavor + )); +} catch (BadResponseException $e) { + // No! Something failed. Let's find out: + $badResponse = $e->getResponse(); + echo sprintf( + 'Status: %s\nBody: %s\nHeaders: %s', + $badResponse->getStatusCode(), + $badResponse->getBody(true), + implode(', ', $response->getHeaderLines()) + ); +} + +// 7. Wait for the server to become ACTIVE. +$server->waitFor(ServerState::ACTIVE); + +// 8. Retreive the server's IP +$ipv4Address = $server->accessIPv4; From d0bfff8fc37bd0c03bf016613ab0fd097ef405b8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:01:34 -0700 Subject: [PATCH 102/835] Sorting imports alphabetically. --- lib/OpenCloud/Orchestration/Resource/Resource.php | 2 +- lib/OpenCloud/Orchestration/Service.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index 15aa3b36a..da90f9859 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -17,8 +17,8 @@ namespace OpenCloud\Orchestration\Resource; -use OpenCloud\Common\Resource\ReadOnlyResource; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\Common\Resource\ReadOnlyResource; /** * Class that represents a resource in a stack. diff --git a/lib/OpenCloud/Orchestration/Service.php b/lib/OpenCloud/Orchestration/Service.php index 61fc57fc8..a9cf082ab 100644 --- a/lib/OpenCloud/Orchestration/Service.php +++ b/lib/OpenCloud/Orchestration/Service.php @@ -17,12 +17,12 @@ namespace OpenCloud\Orchestration; +use Guzzle\Http\Exception\ClientErrorResponseException; +use OpenCloud\Common\Exceptions\InvalidTemplateError; use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Service\CatalogService; -use OpenCloud\Common\Exceptions\InvalidTemplateError; -use OpenCloud\Orchestration\Resource\Stack; use OpenCloud\Orchestration\Resource\ResourceType; -use Guzzle\Http\Exception\ClientErrorResponseException; +use OpenCloud\Orchestration\Resource\Stack; /** * The Orchestration class represents the OpenStack Heat service. From d7488b7251f120f932f51421a93f90bba4a523c2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:04:05 -0700 Subject: [PATCH 103/835] Replacing the word "artifact". --- docs/userguide/Orchestration/USERGUIDE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index b73d03f6b..32e5ce73b 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -11,12 +11,12 @@ key concepts: * **Template**: A JSON or YAML document that describes how a set of resources should be assembled to produce a working deployment. The template specifies the resources -to use, the attributes of these resources that are parameterizedn and the information +to use, the attributes of these resources that are parameterized and the information that is sent to the user when a template is instantiated. -* **Resource**: A template artifact that represents some component of your architecture -(a cloud server, a group of scaled cloud servers, a load balancer, some configuration -management system, and so on). +* **Resource**: Some component of your architecture (a cloud server, a group of scaled +cloud servers, a load balancer, some configuration management system, and so on) that +is defined in a template. * **Stack**: A running instance of a template. When a stack is created, the resources specified in the template are created. From d6fd1652d6e599b5229fc24670be4626d666ba78 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:06:04 -0700 Subject: [PATCH 104/835] Making README and USERGUIDE consistent. --- docs/userguide/Orchestration/README.md | 13 +++++++++---- docs/userguide/Orchestration/USERGUIDE.md | 9 ++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index a963a84c7..5395d5fa0 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -24,9 +24,9 @@ the resources specified in the template are created. ### 1. Instantiate an OpenStack or Rackspace client. -Choose one of the following two options: +To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. +* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: ```php use OpenCloud\OpenStack; @@ -37,7 +37,7 @@ Choose one of the following two options: )); ``` -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: ```php use OpenCloud\Rackspace; @@ -49,12 +49,17 @@ Choose one of the following two options: ``` ### 2. Obtain an Orchestration service object from the client. +All Orchestration operations are done via an _orchestration service object_. To +instantiate this object, call the `orchestrationService` method on the `$client` +object as shown in the following example: + ```php $region = ''; $orchestrationService = $client->orchestrationService(null, $region); ``` -In the example above, you are connecting to the ``DFW`` region of the cloud. Any stacks and resources created with this `$orchestrationService` instance will be stored in that cloud region. +Any stacks and resources created with this `$orchestrationService` instance will +be stored in the cloud region specified by `$region`. ### 3. Create a stack from a template. ```php diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 32e5ce73b..81f651876 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -59,15 +59,14 @@ object. This method takes two arguments: | 2 | Cloud region | String | Yes | - | `DFW` | -In the following example, you are connecting to the ``DFW`` region of the cloud. -Any resources and stacks created with this `$orchestrationService` instance are -created in that region. - ```php -$region = 'DFW'; +$region = ''; $orchestrationService = $client->orchestrationService(null, $region); ``` +Any stacks and resources created with this `$orchestrationService` instance will +be stored in the cloud region specified by `$region`. + ## Templates An Orchestration template is a JSON or YAML document that describes how a set of resources should be assembled to produce a working From 72843d8ff5bddf7d53cd498769f5b1bf7b48f1af Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:17:03 -0700 Subject: [PATCH 105/835] Cleaning up some newlines. --- lib/OpenCloud/Orchestration/Resource/Resource.php | 1 - tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php | 3 --- tests/OpenCloud/Tests/Orchestration/ServiceTest.php | 3 +-- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index da90f9859..0ab58cfd3 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -60,7 +60,6 @@ public function primaryKeyField() public function getMetadata() { - $url = clone $this->getUrl(); $url->addPath('metadata'); diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index e1bd86838..191763f19 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -37,7 +37,6 @@ class StackTest extends OrchestrationTestCase { public function testCreateJson() { - $createParams = array( 'name' => 'foobar', 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', @@ -69,7 +68,6 @@ public function testCreateJson() public function testUpdateJson() { - $updateParams = array( 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( @@ -99,7 +97,6 @@ public function testUpdateJson() public function testAbandonStack() { - $expectedAbandonStackData = '{"status":"COMPLETE","name":"g","dry_run":true,"template":{"outputs":{"instance_ip":{"value":{"str_replace":{"params":{"username":"ec2-user","hostname":{"get_attr":["server","first_address"]}},"template":"ssh username@hostname"}}}},"heat_template_version":"2013-05-23","resources":{"server":{"type":"OS::Nova::Server","properties":{"key_name":{"get_param":"key_name"},"image":{"get_param":"image"},"flavor":{"get_param":"flavor"}}}},"parameters":{"key_name":{"default":"heat_key","type":"string"},"image":{"default":"Ubuntu 12.04 LTS (Precise Pangolin)","type":"string"},"flavor":{"default":"1 GB Performance","type":"string"}}},"action":"CREATE","id":"16934ca3-40e0-4fb2-a289-a700662ec05a","resources":{"server":{"status":"COMPLETE","name":"server","resource_data":{},"resource_id":"39d5dad7-7d7a-4cc8-bd84-851e9e2ff4ea","action":"CREATE","type":"OS::Nova::Server","metadata":{}}}}'; $this->addMockSubscriber($this->makeResponse($expectedAbandonStackData)); diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index 4193410fa..c288ccb36 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -123,5 +123,4 @@ public function testValidateInvalidTemplate() $this->assertEquals("Template version not specified", $e->getMessage()); } } - -} \ No newline at end of file +} From e65bbac2d45e45c83714af7eede4fb7e0af265ff Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:21:00 -0700 Subject: [PATCH 106/835] Fixing quick start example to make it consistent with user guide. --- docs/userguide/Orchestration/README.md | 12 +++++------- samples/Orchestration/quickstart.php | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 5395d5fa0..cb3c6245a 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -63,14 +63,12 @@ be stored in the cloud region specified by `$region`. ### 3. Create a stack from a template. ```php -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'Cloud server with attached block storage', - 'templateUrl' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', +$stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'key_name' => 'mine', - 'flavor' => 'performance1_1', - 'image' => '0112b238-4267-4a22-9785-fcf75814bc2f' // Ubuntu 14.04 LTS (Trusty Tahr) + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index d5ac6f8da..31183a1fe 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -37,14 +37,12 @@ $orchestrationService = $client->orchestrationService(null, $region); // 3. Create a stack. -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'Cloud server with attached block storage', - 'templateUrl' => 'https://raw.githubusercontent.com/openstack/heat-templates/master/hot/vm_with_cinder.yaml', +$stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'key_name' => 'mine', - 'flavor' => 'performance1_1', - 'image' => '0112b238-4267-4a22-9785-fcf75814bc2f' // Ubuntu 14.04 LTS (Trusty Tahr) + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); From 8aa2f03a6c1c4263efe2c5ceec3fb45d70e7f776 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:21:34 -0700 Subject: [PATCH 107/835] Aligning =>s. --- docs/userguide/Orchestration/USERGUIDE.md | 24 +++++++++---------- .../Orchestration/Resource/Event.php | 14 +++++------ .../Orchestration/Resource/Resource.php | 14 +++++------ samples/Orchestration/abandon-stack.php | 2 +- samples/Orchestration/adopt-stack.php | 2 +- .../create-stack-from-template-file.php | 6 ++--- .../create-stack-from-template-url.php | 6 ++--- samples/Orchestration/delete-stack.php | 2 +- .../get-resource-type-template.php | 2 +- samples/Orchestration/get-resource-type.php | 2 +- .../get-stack-resource-event.php | 2 +- .../get-stack-resource-metadata.php | 2 +- samples/Orchestration/get-stack-resource.php | 2 +- samples/Orchestration/get-stack-template.php | 2 +- samples/Orchestration/get-stack.php | 2 +- samples/Orchestration/list-resource-types.php | 2 +- samples/Orchestration/list-stack-events.php | 2 +- .../list-stack-resource-events.php | 2 +- .../Orchestration/list-stack-resources.php | 2 +- samples/Orchestration/list-stacks.php | 2 +- .../preview-stack-from-template-file.php | 10 ++++---- .../preview-stack-from-template-url.php | 6 ++--- samples/Orchestration/quickstart.php | 2 +- .../update-stack-from-template-file.php | 4 ++-- .../update-stack-from-template-url.php | 4 ++-- .../validate-template-from-template-file.php | 4 ++-- .../validate-template-from-template-url.php | 2 +- 27 files changed, 63 insertions(+), 63 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 81f651876..75c7cd159 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -86,7 +86,7 @@ use OpenCloud\Common\Exceptions\InvalidTemplateError; try { $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') )); } catch (InvalidTemplateError $e) { // Use $e->getMessage() for explanation of why template is invalid @@ -137,11 +137,11 @@ can use it to preview a stack as shown in the following example: ```php $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ @@ -157,11 +157,11 @@ example: ```php $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ @@ -193,9 +193,9 @@ $stack = $orchestrationService->createStack(array( 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), - 'timeoutMins' => 5 + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ ``` @@ -213,7 +213,7 @@ $stack->create(array( 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); @@ -272,7 +272,7 @@ $stack->update(array( 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); @@ -291,7 +291,7 @@ $stack->update(array( 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); diff --git a/lib/OpenCloud/Orchestration/Resource/Event.php b/lib/OpenCloud/Orchestration/Resource/Event.php index 49371b06b..bf0da51a8 100644 --- a/lib/OpenCloud/Orchestration/Resource/Event.php +++ b/lib/OpenCloud/Orchestration/Resource/Event.php @@ -42,13 +42,13 @@ class Event extends ReadOnlyResource protected $links; protected $aliases = array( - 'event_time' => 'time', - 'resource_name' => 'resourceName', - 'logical_resource_id' => 'resourceLogicalId', - 'physical_resource_id' => 'resourcePhysicalId', - 'resource_status' => 'resourceStatus', + 'event_time' => 'time', + 'resource_name' => 'resourceName', + 'logical_resource_id' => 'resourceLogicalId', + 'physical_resource_id' => 'resourcePhysicalId', + 'resource_status' => 'resourceStatus', 'resource_status_reason' => 'resourceStatusReason', - 'resource_type' => 'resourceType', - 'resource_properties' => 'resourceProperties' + 'resource_type' => 'resourceType', + 'resource_properties' => 'resourceProperties' ); } diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index 0ab58cfd3..60d8b4288 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -43,14 +43,14 @@ class Resource extends ReadOnlyResource protected $links; protected $aliases = array( - 'resource_name' => 'name', - 'resource_status' => 'status', + 'resource_name' => 'name', + 'resource_status' => 'status', 'resource_status_reason' => 'statusReason', - 'logical_resource_id' => 'logicalId', - 'physical_resource_id' => 'physicalId', - 'required_by' => 'requiredBy', - 'updated_time' => 'updatedTime', - 'resource_type' => 'type' + 'logical_resource_id' => 'logicalId', + 'physical_resource_id' => 'physicalId', + 'required_by' => 'requiredBy', + 'updated_time' => 'updatedTime', + 'resource_type' => 'type' ); public function primaryKeyField() diff --git a/samples/Orchestration/abandon-stack.php b/samples/Orchestration/abandon-stack.php index ef27082fe..475cd5684 100644 --- a/samples/Orchestration/abandon-stack.php +++ b/samples/Orchestration/abandon-stack.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index d27c03937..337e16178 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 54d89b4bc..0934e64de 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -41,9 +41,9 @@ $stack = $orchestrationService->createStack(array( 'name' => 'simple-lamp-setup', 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), - 'parameters' => array( + 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index ebe6195b1..879c131e8 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -43,8 +43,8 @@ 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), - 'timeoutMins' => 5 + 'timeoutMins' => 5 )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index 811e91036..aa0919fec 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-resource-type-template.php b/samples/Orchestration/get-resource-type-template.php index 0c79dc661..1f9f1e509 100644 --- a/samples/Orchestration/get-resource-type-template.php +++ b/samples/Orchestration/get-resource-type-template.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-resource-type.php b/samples/Orchestration/get-resource-type.php index b85f9e956..8f4022b83 100644 --- a/samples/Orchestration/get-resource-type.php +++ b/samples/Orchestration/get-resource-type.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-resource-event.php b/samples/Orchestration/get-stack-resource-event.php index c5d3971d5..f5f679c7e 100644 --- a/samples/Orchestration/get-stack-resource-event.php +++ b/samples/Orchestration/get-stack-resource-event.php @@ -33,7 +33,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-resource-metadata.php b/samples/Orchestration/get-stack-resource-metadata.php index c342390c6..dfb04f707 100644 --- a/samples/Orchestration/get-stack-resource-metadata.php +++ b/samples/Orchestration/get-stack-resource-metadata.php @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-resource.php b/samples/Orchestration/get-stack-resource.php index f585a9d34..b347c8462 100644 --- a/samples/Orchestration/get-stack-resource.php +++ b/samples/Orchestration/get-stack-resource.php @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack-template.php b/samples/Orchestration/get-stack-template.php index fc646d03a..2d251ace6 100644 --- a/samples/Orchestration/get-stack-template.php +++ b/samples/Orchestration/get-stack-template.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index 5abd220eb..0f924e7de 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 58edb5c56..5c60b1215 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index 3c92d5391..3d74384d4 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index 77a8cc64f..99e38559b 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -32,7 +32,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index cb33a88dd..e49284078 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index b9197c7e0..8092a367d 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/preview-stack-from-template-file.php b/samples/Orchestration/preview-stack-from-template-file.php index fce13084b..e0e793421 100644 --- a/samples/Orchestration/preview-stack-from-template-file.php +++ b/samples/Orchestration/preview-stack-from-template-file.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -39,11 +39,11 @@ // 3. Preview a stack. $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), - 'parameters' => array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), + 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/preview-stack-from-template-url.php b/samples/Orchestration/preview-stack-from-template-url.php index d24e40c5c..66167f7b3 100644 --- a/samples/Orchestration/preview-stack-from-template-url.php +++ b/samples/Orchestration/preview-stack-from-template-url.php @@ -30,7 +30,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -41,9 +41,9 @@ $stack = $orchestrationService->previewStack(array( 'name' => 'simple-lamp-setup', 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( + 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ) )); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index 31183a1fe..fd20d682e 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -29,7 +29,7 @@ // 1. Instantiate a Rackspace client. $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. diff --git a/samples/Orchestration/update-stack-from-template-file.php b/samples/Orchestration/update-stack-from-template-file.php index 93318e097..c28c18113 100644 --- a/samples/Orchestration/update-stack-from-template-file.php +++ b/samples/Orchestration/update-stack-from-template-file.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -46,7 +46,7 @@ 'template' => file_get_contents(__DIR__ . '/lamp-updated.yaml'), 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/update-stack-from-template-url.php b/samples/Orchestration/update-stack-from-template-url.php index 5d39b6916..683a70dfd 100644 --- a/samples/Orchestration/update-stack-from-template-url.php +++ b/samples/Orchestration/update-stack-from-template-url.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -46,7 +46,7 @@ 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' ), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/validate-template-from-template-file.php b/samples/Orchestration/validate-template-from-template-file.php index f31acebef..5b6c4cbcc 100644 --- a/samples/Orchestration/validate-template-from-template-file.php +++ b/samples/Orchestration/validate-template-from-template-file.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. @@ -41,7 +41,7 @@ // 3. Validate template from file. try { $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') )); } catch (InvalidTemplateError $e) { // Use $e->getMessage() for explanation of why template is invalid diff --git a/samples/Orchestration/validate-template-from-template-url.php b/samples/Orchestration/validate-template-from-template-url.php index f466ae18a..e391fd9c1 100644 --- a/samples/Orchestration/validate-template-from-template-url.php +++ b/samples/Orchestration/validate-template-from-template-url.php @@ -31,7 +31,7 @@ // 1. Instantiate an OpenStack client. $client = new OpenStack(getenv('OS_AUTH_URL'), array( 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') + 'password' => getenv('OS_PASSWORD') )); // 2. Obtain an Orchestration service object from the client. From 605266a691464fbd172db9670bea9f994c652522 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:36:52 -0700 Subject: [PATCH 108/835] Adding properties being initialized in setupObjects. --- .../OpenCloud/Tests/Orchestration/OrchestrationTestCase.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php index cf22a3a8b..d80f34532 100644 --- a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -23,7 +23,10 @@ class OrchestrationTestCase extends OpenCloudTestCase { protected $service; - protected $container; + protected $buildInfo; + protected $resourceType; + protected $stack; + protected $resource; public function setupObjects() { From 31be71133e0fb70384ca2ac9716cc7544f12dbfe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:43:25 -0700 Subject: [PATCH 109/835] Making test method names more descriptive. --- .../Tests/Orchestration/Resource/BuildInfoTest.php | 6 +++--- .../OpenCloud/Tests/Orchestration/Resource/ResourceTest.php | 6 +++--- .../Tests/Orchestration/Resource/ResourceTypeTest.php | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php index 908e0053d..9c990af14 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/BuildInfoTest.php @@ -25,7 +25,7 @@ class BuildInfoTest extends OrchestrationTestCase /** * @expectedException OpenCloud\Common\Exceptions\CreateError */ - public function testCreate() + public function testCannotCreate() { $this->buildInfo->create(); } @@ -33,7 +33,7 @@ public function testCreate() /** * @expectedException OpenCloud\Common\Exceptions\UpdateError */ - public function testUpdate() + public function testCannotUpdate() { $this->buildInfo->update(); } @@ -41,7 +41,7 @@ public function testUpdate() /** * @expectedException OpenCloud\Common\Exceptions\DeleteError */ - public function testDelete() + public function testCannotDelete() { $this->buildInfo->delete(); } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php index 93bc86285..cf30bf799 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php @@ -25,7 +25,7 @@ class ResourceTest extends OrchestrationTestCase /** * @expectedException OpenCloud\Common\Exceptions\CreateError */ - public function testCreate() + public function testCannotCreate() { $this->resource->create(); } @@ -33,7 +33,7 @@ public function testCreate() /** * @expectedException OpenCloud\Common\Exceptions\UpdateError */ - public function testUpdate() + public function testCannotUpdate() { $this->resource->update(); } @@ -41,7 +41,7 @@ public function testUpdate() /** * @expectedException OpenCloud\Common\Exceptions\DeleteError */ - public function testDelete() + public function testCannotDelete() { $this->resource->delete(); } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php index 68a9dc87e..0261beb9c 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTypeTest.php @@ -25,7 +25,7 @@ class ResourceTypeTest extends OrchestrationTestCase /** * @expectedException OpenCloud\Common\Exceptions\CreateError */ - public function testCreate() + public function testCannotCreate() { $this->resourceType->create(); } @@ -33,7 +33,7 @@ public function testCreate() /** * @expectedException OpenCloud\Common\Exceptions\UpdateError */ - public function testUpdate() + public function testCannotUpdate() { $this->resourceType->update(); } @@ -41,7 +41,7 @@ public function testUpdate() /** * @expectedException OpenCloud\Common\Exceptions\DeleteError */ - public function testDelete() + public function testCannotDelete() { $this->resourceType->delete(); } From fefb1f9d55ac81d0161d1c6d9351b54a58ea7c47 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:45:15 -0700 Subject: [PATCH 110/835] Aligning =>s. --- .../Tests/Orchestration/Resource/StackTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index 191763f19..426b3899f 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -38,9 +38,9 @@ class StackTest extends OrchestrationTestCase public function testCreateJson() { $createParams = array( - 'name' => 'foobar', + 'name' => 'foobar', 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', - 'parameters' => array( + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' @@ -53,9 +53,9 @@ public function testCreateJson() $createJson = $stack->createJson(); $expectedObj = (object) array( - 'stack_name' => 'foobar', + 'stack_name' => 'foobar', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', - 'parameters' => array( + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' @@ -70,7 +70,7 @@ public function testUpdateJson() { $updateParams = array( 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', - 'parameters' => array( + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupalwebuser' @@ -84,7 +84,7 @@ public function testUpdateJson() $expectedObj = (object) array( 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', - 'parameters' => array( + 'parameters' => array( 'flavor_id' => 'performance1_1', 'db_name' => 'drupaldb', 'db_user' => 'drupalwebuser' From 18f933f3174966288b235df423c896d2786b5e99 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:47:44 -0700 Subject: [PATCH 111/835] Using isCollection. --- tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php | 2 +- tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php | 4 ++-- tests/OpenCloud/Tests/Orchestration/ServiceTest.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php index cf30bf799..1b43d709b 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php @@ -58,7 +58,7 @@ public function testListEvents() $this->addMockSubscriber($this->makeResponse('{"events":[{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/474bfdf0-a450-46ec-a78a-0c7faa404073","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_FAILED","resource_status_reason":"NotFound: Subnet f8a699d0-3537-429e-87a5-6b5a8d0c2bf0 could not be found","physical_resource_id":null,"id":"474bfdf0-a450-46ec-a78a-0c7faa404073"},{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/66fa95b6-e6f8-4f05-b1af-e828f5aba04c","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_IN_PROGRESS","resource_status_reason":"state changed","physical_resource_id":null,"id":"66fa95b6-e6f8-4f05-b1af-e828f5aba04c"}]}')); $events = $this->resource->listEvents(); - $this->assertInstanceOf(self::COLLECTION_CLASS, $events); + $this->isCollection($events); $firstEvent = $events->getElement(0); $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index 426b3899f..aa45d3327 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -109,7 +109,7 @@ public function testListResources() $this->addMockSubscriber($this->makeResponse('{"resources":[{"resource_name":"MySqlCloudDatabaseServer","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7xxxxxd99068/resources/MySqlCloudDatabaseServer","rel":"self"},{"href":"http:s//dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7xxxxx068","rel":"stack"}],"logical_resource_id":"MySqlCloudDatabaseServer","resource_status_reason":"state changed","updated_time":"2014-02-05T19:20:31Z","required_by":[],"resource_status":"CREATE_COMPLETE","physical_resource_id":"984xxxxxe0-c7x8-4x6e-be15-3f0xxxxx711","resource_type":"OS::Trove::Instance"}]}')); $resources = $this->stack->listResources(); - $this->assertInstanceOf(self::COLLECTION_CLASS, $resources); + $this->isCollection($resources); $firstResource = $resources->getElement(0); $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $firstResource); @@ -130,7 +130,7 @@ public function testListEvents() $this->addMockSubscriber($this->makeResponse('{"events":[{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/474bfdf0-a450-46ec-a78a-0c7faa404073","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_FAILED","resource_status_reason":"NotFound: Subnet f8a699d0-3537-429e-87a5-6b5a8d0c2bf0 could not be found","physical_resource_id":null,"id":"474bfdf0-a450-46ec-a78a-0c7faa404073"},{"resource_name":"mysql_server","event_time":"2014-07-23T08:14:47Z","links":[{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server/events/66fa95b6-e6f8-4f05-b1af-e828f5aba04c","rel":"self"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5/resources/mysql_server","rel":"resource"},{"href":"http://192.168.123.200:8004/v1/dc4b074874244f7693dd65583733a758/stacks/teststack/db467ed1-50b5-4a3e-aeb1-396ff1d151c5","rel":"stack"}],"logical_resource_id":"mysql_server","resource_status":"CREATE_IN_PROGRESS","resource_status_reason":"state changed","physical_resource_id":null,"id":"66fa95b6-e6f8-4f05-b1af-e828f5aba04c"}]}')); $events = $this->stack->listEvents(); - $this->assertInstanceOf(self::COLLECTION_CLASS, $events); + $this->isCollection($events); $firstEvent = $events->getElement(0); $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index c288ccb36..b8cb4b069 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -64,7 +64,7 @@ public function testListStacks() $this->addMockSubscriber($this->makeResponse('{"stacks":[{"description":"HEAT template for deploying a multi-node wordpress deployment on Rackspace Cloud\nusing Cloud Servers, Cloud Load Balancers and Cloud Databases. This version uses\na user-defined template resource to specify the implementation of the web-heads\n","links":[{"href":"http:\/\/xxxxx\/v1\/xxxx\/stacks\/xxxx\/11cd9b5e-c7ff-43b5-bff8-b0e7429cd87e","rel":"self"}],"stack_status_reason":"Resource suspend failed: Error: State invalid for suspend","stack_name":"timswp6","creation_time":"2014-01-30T20:47:57Z","updated_time":"2014-02-03T18:04:39Z","stack_status":"SUSPEND_FAILED","id":"11cd9b5e-c7ff-43b5-bff8-b0e7429cd87e"},{"description":"HEAT template for deploying a multi-node wordpress deployment on Rackspace Cloud\nusing Cloud Servers, Cloud Load Balancers and Cloud Databases. This version uses\na user-defined template resource to specify the implementation of the web-heads\n","links":[{"href":"http:\/\/xxxx\/v1\/xxxx\/stacks\/xxxx\/1b2ed5de-9b8c-43fa-9392-1da17b5dee7c","rel":"self"}],"stack_status_reason":"Stack create completed successfully","stack_name":"timswp5","creation_time":"2014-01-30T18:18:12Z","updated_time":"2014-01-30T18:42:07Z","stack_status":"CREATE_COMPLETE","id":"1b2ed5de-9b8c-43fa-9392-1da17b5dee7c"}]}')); $stacks = $this->service->listStacks(); - $this->assertInstanceOf(self::COLLECTION_CLASS, $stacks); + $this->isCollection($stacks); $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stacks->getElement(0)); } @@ -91,7 +91,7 @@ public function testListResourceTypes() $this->addMockSubscriber($this->makeResponse('{"resource_types":["OS::Nova::Server","OS::Heat::RandomString","OS::Swift::Container","Rackspace::Cloud::Server","OS::Heat::ChefSolo","Rackspace::AutoScale::WebHook","Rackspace::AutoScale::Group","Rackspace::Cloud::Network","OS::Cinder::Volume","Rackspace::Cloud::WinServer","Rackspace::Cloud::LoadBalancer","OS::Heat::ResourceGroup","Rackspace::AutoScale::ScalingPolicy","Rackspace::Cloud::DNS","OS::Trove::Instance","OS::Nova::FloatingIPAssociation","OS::Cinder::VolumeAttachment","OS::Nova::FloatingIP","OS::Nova::KeyPair"]}')); $resourceTypes = $this->service->listResourceTypes(); - $this->assertInstanceOf(self::COLLECTION_CLASS, $resourceTypes); + $this->isCollection($resourceTypes); $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $resourceTypes->getElement(0)); } From e70388c946acb5805858657d3ac007c826f5d70c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 17:48:45 -0700 Subject: [PATCH 112/835] Removing superfluous docblock. --- tests/OpenCloud/Tests/Orchestration/ServiceTest.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index b8cb4b069..df294c2d7 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -15,13 +15,6 @@ * limitations under the License. */ -/** - * PHP OpenCloud library. - * - * @copyright 2014 Rackspace Hosting, Inc. See LICENSE for information. - * @license https://www.apache.org/licenses/LICENSE-2.0 - * @author Shaunak Kashyap - */ namespace OpenCloud\Tests\Orchestration; use OpenCloud\Orchestration\Service; From b309dc30de1692a35b8361280e77ebd3c6d34faa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 18:00:16 -0700 Subject: [PATCH 113/835] Refactoring assertInstanceOf calls. --- .../Orchestration/OrchestrationTestCase.php | 26 ++++++++++++++++++- .../Orchestration/Resource/ResourceTest.php | 2 +- .../Orchestration/Resource/StackTest.php | 6 ++--- .../Tests/Orchestration/ServiceTest.php | 20 +++++++------- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php index d80f34532..66e2fb9bf 100644 --- a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -44,4 +44,28 @@ public function setupObjects() $this->addMockSubscriber($this->makeResponse('{"resource":{"resource_name":"MySqlCloudDatabaseServer","description":"","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx21-9xx9-4xxxe-bxxf-a7fxxxxx68/resources/MySqlCloudDatabaseServer","rel":"self"},{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7fxxxxxx68","rel":"stack"}],"logical_resource_id":"MySqlCloudDatabaseServer","resource_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","required_by":[],"resource_status_reason":"state changed","physical_resource_id":"98xxx0-cxx8-4xxe-bxx5-3fxxxx11","resource_type":"OS::Trove::Instance"}}')); $this->resource = $this->stack->getResource('MySqlCloudDatabaseServer'); } -} + + protected function assertIsService($object) { + $this->assertInstanceOf('OpenCloud\Orchestration\Service', $object); + } + + protected function assertIsBuildInfo($object) { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\BuildInfo', $object); + } + + protected function assertIsEvent($object) { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $object); + } + + protected function assertIsResource($object) { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $object); + } + + protected function assertIsResourceType($object) { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $object); + } + + protected function assertIsStack($object) { + $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $object); + } +} diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php index 1b43d709b..6b34a2997 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/ResourceTest.php @@ -61,7 +61,7 @@ public function testListEvents() $this->isCollection($events); $firstEvent = $events->getElement(0); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); + $this->assertIsEvent($firstEvent); $this->assertEquals('474bfdf0-a450-46ec-a78a-0c7faa404073', $firstEvent->getId()); } diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index aa45d3327..35c982d50 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -112,7 +112,7 @@ public function testListResources() $this->isCollection($resources); $firstResource = $resources->getElement(0); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $firstResource); + $this->assertIsResource($firstResource); $this->assertEquals('MySqlCloudDatabaseServer', $firstResource->getName()); } @@ -121,7 +121,7 @@ public function testGetResource() $this->addMockSubscriber($this->makeResponse('{"resource":{"resource_name":"MySqlCloudDatabaseServer","description":"A MySQL DB instance","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx21-9xx9-4xxxe-bxxf-a7fxxxxx68/resources/MySqlCloudDatabaseServer","rel":"self"},{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxxx1-9xx9-4xxe-bxxf-a7fxxxxxx68","rel":"stack"}],"logical_resource_id":"MySqlCloudDatabaseServer","resource_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","required_by":[],"resource_status_reason":"state changed","physical_resource_id":"98xxx0-cxx8-4xxe-bxx5-3fxxxx11","resource_type":"OS::Trove::Instance"}}')); $resource = $this->stack->getResource('MySqlCloudDatabaseServer'); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $resource); + $this->assertIsResource($resource); $this->assertEquals('A MySQL DB instance', $resource->getDescription()); } @@ -133,7 +133,7 @@ public function testListEvents() $this->isCollection($events); $firstEvent = $events->getElement(0); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $firstEvent); + $this->assertIsEvent($firstEvent); $this->assertEquals('474bfdf0-a450-46ec-a78a-0c7faa404073', $firstEvent->getId()); } diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index df294c2d7..4d5c1032d 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -26,17 +26,17 @@ class ServiceTest extends OrchestrationTestCase public function test__construct() { $service = $this->getClient()->orchestrationService(null, 'DFW'); - $this->assertInstanceOf('OpenCloud\Orchestration\Service', $service); + $this->assertIsService($service); } public function testPreviewStack() { - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->previewStack()); + $this->assertIsStack($this->service->previewStack()); } public function testCreateStack() { - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->createStack()); + $this->assertIsStack($this->service->createStack()); } /** @@ -49,7 +49,7 @@ public function testAdoptStackWithoutAdoptStackData() public function testAdoptStackWithAdoptStackData() { - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $this->service->adoptStack(array('adoptStackData' => 'foobar'))); + $this->assertIsStack($this->service->adoptStack(array('adoptStackData' => 'foobar'))); } public function testListStacks() @@ -58,7 +58,7 @@ public function testListStacks() $stacks = $this->service->listStacks(); $this->isCollection($stacks); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stacks->getElement(0)); + $this->assertIsStack($stacks->getElement(0)); } public function testGetStackByName() @@ -66,7 +66,7 @@ public function testGetStackByName() $this->addMockSubscriber($this->makeResponse('{"stack":{"disable_rollback":true,"description":"MYSQL server cloud database instance running on Rackspace cloud","parameters":{"instance_name":"testdb","OS::stack_id":"87xxx21-9db9-49be-bc4f-a7f2exxxx68","OS::stack_name":"trove2"},"stack_status_reason":"Stack create completed successfully","stack_name":"trove2","outputs":[],"creation_time":"2014-02-05T19:18:56Z","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxx21-9db9-49be-bc4f-a7f2exxxx68","rel":"self"}],"capabilities":[],"notification_topics":[],"timeout_mins":60,"stack_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","id":"879xxx21-9db9-49be-bc4f-a7f2exxxx9068","template_description":"MYSQL server cloud database instance running on Rackspace cloud"}}')); $stack = $this->service->getStack('foobar'); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stack); + $this->assertIsStack($stack); $this->assertEquals('MYSQL server cloud database instance running on Rackspace cloud', $stack->getDescription()); } @@ -75,7 +75,7 @@ public function testGetStackByNameAndId() $this->addMockSubscriber($this->makeResponse('{"stack":{"disable_rollback":true,"description":"MYSQL server cloud database instance running on Rackspace cloud","parameters":{"instance_name":"testdb","OS::stack_id":"87xxx21-9db9-49be-bc4f-a7f2exxxx68","OS::stack_name":"trove2"},"stack_status_reason":"Stack create completed successfully","stack_name":"trove2","outputs":[],"creation_time":"2014-02-05T19:18:56Z","links":[{"href":"https://dfw.orchestration.rackspacecloud.com/v1/tenant_id/stacks/trove2/87xxx21-9db9-49be-bc4f-a7f2exxxx68","rel":"self"}],"capabilities":[],"notification_topics":[],"timeout_mins":60,"stack_status":"CREATE_COMPLETE","updated_time":"2014-02-05T19:20:31Z","id":"879xxx21-9db9-49be-bc4f-a7f2exxxx9068","template_description":"MYSQL server cloud database instance running on Rackspace cloud"}}')); $stack = $this->service->getStack('foobar', 1234); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $stack); + $this->assertIsStack($stack); $this->assertEquals('MYSQL server cloud database instance running on Rackspace cloud', $stack->getDescription()); } @@ -85,7 +85,7 @@ public function testListResourceTypes() $resourceTypes = $this->service->listResourceTypes(); $this->isCollection($resourceTypes); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $resourceTypes->getElement(0)); + $this->assertIsResourceType($resourceTypes->getElement(0)); } public function testGetResourceType() @@ -93,7 +93,7 @@ public function testGetResourceType() $this->addMockSubscriber($this->makeResponse('{"attributes":{"an_attribute":{"description":"An attribute description ."}},"properties":{"a_property":{"update_allowed":false,"required":true,"type":"string","description":"A resource description."}},"resource_type":"OS::Nova::Server"}')); $resourceType = $this->service->getResourceType('OS::Nova::Server'); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $resourceType); + $this->assertIsResourceType($resourceType); $this->assertEquals('OS::Nova::Server', $resourceType->getResourceType()); } @@ -102,7 +102,7 @@ public function testGetBuildInfo() $this->addMockSubscriber($this->makeResponse('{"engine":{"revision":"2014.j3-20141003-1139"},"fusion-api":{"revision":"j1-20140915-10d9ee4-98"},"api":{"revision":"2014.j3-20141003-1139"}}')); $buildInfo = $this->service->getBuildInfo(); - $this->assertInstanceOf('OpenCloud\Orchestration\Resource\BuildInfo', $buildInfo); + $this->assertIsBuildInfo($buildInfo); $this->assertEquals('2014.j3-20141003-1139', $buildInfo->getApi()->revision); } From b9fe0c0adc2cb0fa1db201a3a82698d6c836b62c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 18:13:04 -0700 Subject: [PATCH 114/835] Adding TOC. --- docs/userguide/Orchestration/USERGUIDE.md | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 75c7cd159..3205caf50 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -4,6 +4,47 @@ Orchestration is a service that you can use to create and manage cloud resources such as databases, load balancers, and servers, and the software installed on servers. +## Table of Contents + +* [Concepts](#concepts) +* [Prerequisites](#prerequisites) + * [Client](#client) + * [Orchestration service](#orchestration-service) +* [Templates](#templates) + * [Validate template](#validate-template) + * [Validate a template from a file](#validate-a-template-from-a-file) + * [Validate Template from URL](#validate-template-from-url) +* [Stacks](#stacks) + * [Preview stack](#preview-stack) + * [Preview a stack from a template file](#preview-a-stack-from-a-template-file) + * [Preview a stack from a template URL](#preview-a-stack-from-a-template-url) + * [Create stack](#create-stack) + * [Create a stack from a template file](#create-a-stack-from-a-template-file) + * [Create a stack from a template URL](#create-a-stack-from-a-template-url) + * [List stacks](#list-stacks) + * [Get stack](#get-stack) + * [Get stack template](#get-stack-template) + * [Update stack](#update-stack) + * [Update a stack from a template file](#update-a-stack-from-a-template-file) + * [Update Stack from Template URL](#update-stack-from-template-url) + * [Delete stack](#delete-stack) + * [Abandon Stack](#abandon-stack) + * [Adopt stack](#adopt-stack) +* [Stack resources](#stack-resources) + * [List stack resources](#list-stack-resources) + * [Get stack resource](#get-stack-resource) + * [Get stack resource metadata](#get-stack-resource-metadata) +* [Stack resource events](#stack-resource-events) + * [List stack events](#list-stack-events) + * [List stack resource events](#list-stack-resource-events) + * [Get stack resource event](#get-stack-resource-event) +* [Resource types](#resource-types) + * [List resource types](#list-resource-types) + * [Get resource type](#get-resource-type) + * [Get resource type template](#get-resource-type-template) +* [Build info](#build-info) + * [Get build info](#get-build-info) + ## Concepts To use the Orchestration service effectively, you should understand the following From fbe6d8287baed00f4e5b81a81573c7ad9391d72c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 15 Oct 2014 18:18:23 -0700 Subject: [PATCH 115/835] Documenting return type of getMetadata method. --- lib/OpenCloud/Orchestration/Resource/Resource.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/OpenCloud/Orchestration/Resource/Resource.php b/lib/OpenCloud/Orchestration/Resource/Resource.php index 60d8b4288..431251e73 100644 --- a/lib/OpenCloud/Orchestration/Resource/Resource.php +++ b/lib/OpenCloud/Orchestration/Resource/Resource.php @@ -58,6 +58,11 @@ public function primaryKeyField() return 'name'; } + /** + * Returns metadata properties associated with this Resource + * + * @return \stdClass + */ public function getMetadata() { $url = clone $this->getUrl(); From dd6476075a6e215d218b1651a8edb30dec64390e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 17 Oct 2014 16:44:01 -0700 Subject: [PATCH 116/835] Renaming file to be more specific. --- docs/userguide/ObjectStore/USERGUIDE.md | 2 +- ...ist-objects-with-params.php => list-objects-with-prefix.php} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename samples/ObjectStore/{list-objects-with-params.php => list-objects-with-prefix.php} (100%) diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 0cfab2680..d4e94288f 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -351,7 +351,7 @@ $options = array( $objects = $container->objectList($options); ``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects-with-params.php) ] +[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects-with-prefix.php) ] In general, the `objectList()` method described above takes an optional parameter (`$options` in the example above). This parameter is an associative array of various options. Here is a complete listing of keys that can be specified in the `$options` array: diff --git a/samples/ObjectStore/list-objects-with-params.php b/samples/ObjectStore/list-objects-with-prefix.php similarity index 100% rename from samples/ObjectStore/list-objects-with-params.php rename to samples/ObjectStore/list-objects-with-prefix.php From 11ca167bbe912daac642ba994013b174317cb57f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 17 Oct 2014 16:48:12 -0700 Subject: [PATCH 117/835] Adding more samples for listing objects with params. --- .../list-objects-with-limit-and-prefix.php | 54 +++++++++++++++++++ .../ObjectStore/list-objects-with-limit.php | 52 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 samples/ObjectStore/list-objects-with-limit-and-prefix.php create mode 100644 samples/ObjectStore/list-objects-with-limit.php diff --git a/samples/ObjectStore/list-objects-with-limit-and-prefix.php b/samples/ObjectStore/list-objects-with-limit-and-prefix.php new file mode 100644 index 000000000..d286843fe --- /dev/null +++ b/samples/ObjectStore/list-objects-with-limit-and-prefix.php @@ -0,0 +1,54 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Object Store service object from the client. +$region = 'DFW'; +$objectStoreService = $client->objectStoreService(null, $region); + +// 3. Get container. +$container = $objectStoreService->getContainer('logos'); + +// 4. Get list of objects whose names start with "php" in container. +$options = array( + 'limit' => getenv('NUM_OBJECTS'), + 'prefix' => getenv('OBJECT_PREFIX') +); +$objects = $container->objectList($options); +foreach ($objects as $object) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + printf("Object name: %s\n", $object->getName()); +} diff --git a/samples/ObjectStore/list-objects-with-limit.php b/samples/ObjectStore/list-objects-with-limit.php new file mode 100644 index 000000000..fa1d2dd2e --- /dev/null +++ b/samples/ObjectStore/list-objects-with-limit.php @@ -0,0 +1,52 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Object Store service object from the client. +$region = 'DFW'; +$objectStoreService = $client->objectStoreService(null, $region); + +// 3. Get container. +$container = $objectStoreService->getContainer('logos'); + +// 4. Get list of objects whose names start with "php" in container. +$options = array( + 'limit' => getenv('NUM_OBJECTS') +); +$objects = $container->objectList($options); +foreach ($objects as $object) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + printf("Object name: %s\n", $object->getName()); +} From b79eb064d62570808fb25d067819d578d97e02ec Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 06:10:40 -0700 Subject: [PATCH 118/835] Changing default region from ORD to IAD. --- tests/OpenCloud/Smoke/Enum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Smoke/Enum.php b/tests/OpenCloud/Smoke/Enum.php index 76b293cde..d464051f0 100644 --- a/tests/OpenCloud/Smoke/Enum.php +++ b/tests/OpenCloud/Smoke/Enum.php @@ -49,7 +49,7 @@ class Enum const ENV_IDENTITY_ENDPOINT = 'IDENTITY_ENDPOINT'; // Defaults - const DEFAULT_REGION = 'ORD'; + const DEFAULT_REGION = 'IAD'; // How many iterations do we want for resource lists? We don't have all day... const DISPLAY_ITER_LIMIT = 10; From d0469e227ad2814decc39b9996307cef818c1b2d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 06:11:01 -0700 Subject: [PATCH 119/835] Adding link to sample. --- docs/userguide/Orchestration/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 3205caf50..c98ceadc9 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -169,7 +169,7 @@ This operation takes one parameter, an associative array, with the following key | `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters | Yes | - | `simple-lamp-setup` | | `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | | `templateUrl` | URL of the template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null` | `array('flavor_id' => 'performance1_1')` | +| `parameters` | Arguments to the template, based on the template's parameters. For example, see the parameters in [this template section](https://github.com/rackspace-orchestration-templates/lamp/blob/master/lamp.yaml#L22) | Associative array | No | `null` | `array('flavor_id' => 'performance1_1')` | #### Preview a stack from a template file From b2c396449e66ef503441aa9b403c6b3e1f3a9580 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 06:11:22 -0700 Subject: [PATCH 120/835] Aliasing templateUrl => template_url. --- lib/OpenCloud/Orchestration/Service.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/OpenCloud/Orchestration/Service.php b/lib/OpenCloud/Orchestration/Service.php index a9cf082ab..a1acb2559 100644 --- a/lib/OpenCloud/Orchestration/Service.php +++ b/lib/OpenCloud/Orchestration/Service.php @@ -160,6 +160,11 @@ public function validateTemplate(array $params = array()) $url = clone $this->getUrl(); $url->addPath('validate'); + // Aliases + if (array_key_exists('templateUrl', $params)) { + $params['template_url'] = $params['templateUrl']; + } + $json = json_encode($params); try { From cc1b45ec1b7ea376fae8023ad92e6a33cf7a204d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 06:11:48 -0700 Subject: [PATCH 121/835] Extracting up getResourceDir for reuse in multiple smoke test classes. --- tests/OpenCloud/Smoke/Unit/AbstractUnit.php | 6 ++++++ tests/OpenCloud/Smoke/Unit/ObjectStore.php | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/AbstractUnit.php b/tests/OpenCloud/Smoke/Unit/AbstractUnit.php index 4ff1011cf..aa2ac945f 100644 --- a/tests/OpenCloud/Smoke/Unit/AbstractUnit.php +++ b/tests/OpenCloud/Smoke/Unit/AbstractUnit.php @@ -184,4 +184,10 @@ public function createSubStep($string, $outputType = null) return $step->output(); } + protected function getResourceDir() + { + $className = get_class($this); + $className = join('', array_slice(explode('\\', $className), -1)); + return __DIR__ . '/../Resource/' . $className; + } } diff --git a/tests/OpenCloud/Smoke/Unit/ObjectStore.php b/tests/OpenCloud/Smoke/Unit/ObjectStore.php index 213981412..5ab4a3b0c 100644 --- a/tests/OpenCloud/Smoke/Unit/ObjectStore.php +++ b/tests/OpenCloud/Smoke/Unit/ObjectStore.php @@ -70,7 +70,7 @@ public function main() // Upload 50 objects $this->step('Upload ' . self::UPLOAD_COUNT . ' files'); - $dir = __DIR__ . '/../Resource/ObjectStore/'; + $dir = $this->getResourceDir(); if (!file_exists($dir)) { mkdir($dir); } @@ -81,7 +81,7 @@ public function main() $files = array(); for ($i = 1; $i <= 50; $i++) { $file = self::OBJECT_NAME . "_$i"; - $files[] = array('name' => $file . '.txt', 'path' => __DIR__ . '/../Resource/ObjectStore/' . $file); + $files[] = array('name' => $file . '.txt', 'path' => $dir . '/' . $file); } $container->uploadObjects($files); From 5816520e0f84da00fddcd16d5dfd2f5f9b84625f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 08:33:38 -0700 Subject: [PATCH 122/835] Adding smoke tests for Orchestration service. --- tests/OpenCloud/Smoke/Unit/Orchestration.php | 200 +++++++++++++++++-- 1 file changed, 187 insertions(+), 13 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Orchestration.php b/tests/OpenCloud/Smoke/Unit/Orchestration.php index 9498c12a4..e03465dbf 100644 --- a/tests/OpenCloud/Smoke/Unit/Orchestration.php +++ b/tests/OpenCloud/Smoke/Unit/Orchestration.php @@ -17,6 +17,8 @@ namespace OpenCloud\Smoke\Unit; +use OpenCloud\Smoke\Utils; + /** * Description of Orchestration * @@ -24,27 +26,199 @@ */ class Orchestration extends AbstractUnit implements UnitInterface { - /** - * {@inheritDoc} - */ + + const STACK_NAME = 'PHPOpenCloudSmokeTestStack'; + + protected $cleanupStackIds = array(); + public function setupService() { - + return $this->getConnection()->orchestrationService('cloudOrchestration', Utils::getRegion()); } - - /** - * {@inheritDoc} - */ + public function main() { + + $this->step('Validate template from a file'); + $this->getService()->validateTemplate(array( + 'template' => file_get_contents($this->getResourceDir() . '/lamp.yaml') + )); + + $this->step('Validate template from a URL'); + $this->getService()->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + + $this->step('Preview stack from template file'); + $stack = $this->getService()->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents($this->getResourceDir() . '/lamp.yaml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + + $this->step('Preview stack from template URL'); + $stack = $this->getService()->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + + $this->step('Create stack from template file'); + $stack = $this->getService()->createStack(array( + 'name' => 'simple-lamp-setup-from-template-file', + 'template' => file_get_contents($this->getResourceDir() . '/lamp.yaml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + $this->stepInfo('Stack ID: ' . $stack->getId()); + $this->stepInfo('Stack Name: ' . $stack->getName()); + $this->cleanupStackIds[] = $stack->getId(); + + $this->step('Create stack from template URL'); + $stack = $this->getService()->createStack(array( + 'name' => 'simple-lamp-setup-from-template-url', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + $this->stepInfo('Stack ID: ' . $stack->getId()); + $this->stepInfo('Stack Name: ' . $stack->getName()); + + $this->step('List stacks'); + $stacks = $this->getService()->listStacks(); + $this->stepInfo('%-40s | %s', 'Stack ID', 'Stack name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($stacks as $stack) { + $this->stepInfo('%-40s | %s', $stack->getId(), $stack->getName()); + } + + $this->step('Get stack'); + $stack = $this->getService()->getStack('simple-lamp-setup-from-template-file'); + $this->stepInfo('Stack ID: ' . $stack->getId()); + $this->stepInfo('Stack name: ' . $stack->getName()); + + $this->step('Get stack template'); + $stackTemplateJsonStr = $stack->getStackTemplate(); + $this->stepInfo('Stack template JSON: %s ', $stackTemplateJsonStr); + + $this->step('List stack resources'); + $resources = $stack->listResources(); + $this->stepInfo('Resource name'); + $this->stepInfo(str_repeat('-', 25)); + foreach ($resources as $resource) { + $this->stepInfo($resource->getName()); + } + $this->step('Get stack resource'); + $resource = $stack->getResource('linux_server'); + $this->stepInfo('Resource name: ' . $resource->getName()); + + $this->step('Get stack resource metadata'); + $metadata = $resource->getMetadata(); + $this->stepInfo('%-25s | %s', 'Metadata key', 'Metadata value'); + foreach ($metadata as $key => $value) { + $this->stepInfo('%-25s | %s', $key, $value); + + $this->step('List stack events'); + $events = $stack->listEvents(); + $this->stepInfo('%-40s | %-20s | %-25s | %s', 'Event ID', 'Event time', 'Resource name', 'Resource status'); + $this->stepInfo('%-40s | %-20s | %-25s | %s', str_repeat('-', 40), str_repeat('-', 20), str_repeat('-', 25), str_repeat('-', 20)); + foreach ($events as $event) { + $this->stepInfo('%-40s | %-20s | %-25s | %s', + $event->getId(), $event->getTime(), $event->getResourceName(), $event->getResourceStatus()); + } + + $this->step('List stack resource events'); + $events = $resource->listEvents(); + $this->stepInfo('%-40s | %-20s | %-25s | %s', 'Event ID', 'Event time', 'Resource name', 'Resource status'); + $this->stepInfo('%-40s | %-20s | %-25s | %s', str_repeat('-', 40), str_repeat('-', 20), str_repeat('-', 25), str_repeat('-', 20)); + foreach ($events as $event) { + $this->stepInfo('%-40s | %-20s | %-25s | %s', + $event->getId(), $event->getTime(), $event->getResourceName(), $event->getResourceStatus()); + $lastEventId = $event->getId(); + } + + $this->step('Get stack resource event'); + $event = $resource->getEvent($lastEventId); + $this->stepInfo('Event ID: ' . $event->getId()); + $this->stepInfo('Event time: ' . $event->getTime()); + $this->stepInfo('Resource name: ' . $event->getResourceName()); + $this->stepInfo('Resource status: ' . $event->getResourceStatus()); + + $this->step('List resource types'); + $resourceTypes = $this->getService()->listResourceTypes(); + $this->stepInfo('Resource type'); + $this->stepInfo(str_repeat('-', 40)); + foreach ($resourceTypes as $resourceType) { + $this->stepInfo($resourceType->getResourceType()); + $lastResourceType = $resourceType->getResourceType(); + } + + $this->step('Get resource type'); + $resourceType = $this->getService()->getResourceType($lastResourceType); + $this->stepInfo('Resource type: ' . $resourceType->getResourceType()); + + $this->step('Get resource type template'); + $this->stepInfo('Resource type template: ' . $resourceType->getTemplate()); + + $this->step('Get build info'); + $buildInfo = $this->getService()->getBuildInfo(); + $this->stepInfo('API revision: ' . $buildInfo->getApi()->revision); + $this->stepInfo('Engine revision: ' . $buildInfo->getEngine()->revision); + + $this->step('Update stack from template file'); + $stack->waitFor('CREATE_COMPLETE', null, function($s) { + $this->stepInfo('Stack is still being created. Waiting...'); + }); + $stack->update(array( + 'template' => file_get_contents($this->getResourceDir() . '/lamp-updated.yaml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + $this->stepInfo('Done! Stack update requested.'); + + $this->step('Abandon stack'); + $stack = $this->getService()->getStack('simple-lamp-setup-from-template-url'); + $stack->waitFor('CREATE_COMPLETE', null, function($s) { + $this->stepInfo('Stack is still being created. Waiting...'); + }); + $abandonedStackData = $stack->abandon(); + $this->stepInfo('Abandon stack data: %s ', $abandonedStackData); + + $this->step('Adopt stack'); + $stack = $this->getService()->adoptStack(array( + 'name' => 'simple-lamp-setup-from-template-url', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'adoptStackData' => $abandonedStackData, + 'timeoutMins' => 5 + )); + $this->stepInfo('Stack ID: ' . $stack->getId()); + $this->stepInfo('Stack name: ' . $stack->getName()); + $this->cleanupStackIds[] = $stack->getId(); } - - /** - * {@inheritDoc} - */ + public function teardown() { - + foreach ($this->cleanupStackIds as $stackId) { + $stack = $this->getService()->getStack($stackId); + $stack->delete(); + } } } From e44d36e20bf5defbf6c7f38c0c263e3a64a91949 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 08:37:54 -0700 Subject: [PATCH 123/835] Including orchestration resource files. --- tests/OpenCloud/Smoke/.gitignore | 1 - tests/OpenCloud/Smoke/Resource/.gitignore | 1 + .../Resource/Orchestration/lamp-updated.yaml | 184 ++++++++++++++++++ .../Smoke/Resource/Orchestration/lamp.yaml | 184 ++++++++++++++++++ 4 files changed, 369 insertions(+), 1 deletion(-) delete mode 100644 tests/OpenCloud/Smoke/.gitignore create mode 100644 tests/OpenCloud/Smoke/Resource/.gitignore create mode 100644 tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml create mode 100644 tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml diff --git a/tests/OpenCloud/Smoke/.gitignore b/tests/OpenCloud/Smoke/.gitignore deleted file mode 100644 index 62112125e..000000000 --- a/tests/OpenCloud/Smoke/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Resource/ diff --git a/tests/OpenCloud/Smoke/Resource/.gitignore b/tests/OpenCloud/Smoke/Resource/.gitignore new file mode 100644 index 000000000..836d1ecc8 --- /dev/null +++ b/tests/OpenCloud/Smoke/Resource/.gitignore @@ -0,0 +1 @@ +ObjectStore/ diff --git a/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml new file mode 100644 index 000000000..feebf366c --- /dev/null +++ b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml @@ -0,0 +1,184 @@ +heat_template_version: 2013-05-23 + +description: | + This is a Heat template to deploy a server with LAMP + +parameter_groups: +- label: Server Settings + parameters: + - server_hostname + - image + - flavor + +- label: phpMyAdmin Settings + parameters: + - phpmyadmin_user + +- label: rax-dev-params + parameters: + - kitchen + - chef_version + +parameters: + server_hostname: + label: Server Name + description: Hostname to use for setting the server name. + type: string + default: web + constraints: + - length: + min: 1 + max: 64 + - allowed_pattern: "^[a-zA-Z0-9]([a-zA-Z0-9.-])*$" + description: | + Must begin with a letter or number and be alphanumeric or '-' and '.' + + image: + label: Operating System + description: | + Required: Server image used for all servers that are created as a part of + this deployment. + type: string + default: CentOS 6.5 (PVHVM) + constraints: + - allowed_values: + - CentOS 6.5 (PVHVM) + - Red Hat Enterprise Linux 6.5 (PVHVM) + - Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM) + - Ubuntu 12.04 LTS (Precise Pangolin) + - Debian 7 (Wheezy) (PVHVM) + description: Must be a supported operating system. + + flavor: + label: Server Size + description: | + Required: Rackspace Cloud Server flavor to use. The size is based on the + amount of RAM for the provisioned server. + type: string + default: 1 GB Performance + constraints: + - allowed_values: + - 1 GB Performance + - 2 GB Performance + - 4 GB Performance + - 8 GB Performance + - 15 GB Performance + - 30 GB Performance + description: | + Must be a valid Rackspace Cloud Server flavor for the region you have + selected to deploy into. + + phpmyadmin_user: + label: Username + description: "Username for phpMyAdmin logins." + type: string + default: serverinfo + constraints: + - allowed_pattern: "^(.){1,16}$" + description: | + Must be shorter than 16 characters, this is due to MySQL's maximum + username length. + + kitchen: + description: URL for the kitchen to use + type: string + default: https://github.com/rackspace-orchestration-templates/lamp + + chef_version: + description: Version of chef client to use + type: string + default: 11.12.8 + +resources: + + ssh_key: + type: "OS::Nova::KeyPair" + properties: + name: { get_param: "OS::stack_id" } + save_private_key: true + + mysql_root_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_repl_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_debian_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + phpmyadmin_pass: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + linux_server: + type: "Rackspace::Cloud::Server" + properties: + name: { get_param: server_hostname } + flavor: { get_param: flavor } + image: { get_param: image } + key_name: { get_resource: ssh_key } + config_drive: "true" + metadata: + rax-heat: { get_param: "OS::stack_id" } + metadata: + foo: bar + baz: qux + + linux_setup: + type: "OS::Heat::ChefSolo" + depends_on: linux_server + properties: + username: root + private_key: { get_attr: [ssh_key, private_key] } + host: { get_attr: [linux_server, accessIPv4] } + kitchen: { get_param: kitchen } + chef_version: { get_param: chef_version } + node: + mysql: + server_root_password: { get_attr: [mysql_root_password, value] } + server_repl_password: { get_attr: [mysql_repl_password, value] } + server_debian_password: { get_attr: [mysql_debian_password, value] } + phpmyadmin: + pass: { get_attr: [phpmyadmin_pass, value] } + user: { get_param: phpmyadmin_user } + run_list: ["recipe[LAMP]"] + +outputs: + private_key: + description: SSH Private Key + value: { get_attr: [ssh_key, private_key] } + + server_ip: + description: Server IP + value: { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_url: + description: phpMyAdmin URL + value: + str_replace: + template: "http://%server_ip%/phpmyadmin" + params: + "%server_ip%": { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_user: + description: phpMyAdmin User + value: { get_param: phpmyadmin_user } + + phpmyadmin_password: + description: phpMyAdmin Password + value: { get_attr: [phpmyadmin_pass, value] } + + mysql_root_password: + description: MySQL Root Password + value: { get_attr: [mysql_root_password, value] } diff --git a/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml new file mode 100644 index 000000000..d70b2cc4c --- /dev/null +++ b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml @@ -0,0 +1,184 @@ +heat_template_version: 2013-05-23 + +description: | + This is a Heat template to deploy a server with LAMP + +parameter_groups: +- label: Server Settings + parameters: + - server_hostname + - image + - flavor + +- label: phpMyAdmin Settings + parameters: + - phpmyadmin_user + +- label: rax-dev-params + parameters: + - kitchen + - chef_version + +parameters: + server_hostname: + label: Server Name + description: Hostname to use for setting the server name. + type: string + default: web + constraints: + - length: + min: 1 + max: 64 + - allowed_pattern: "^[a-zA-Z0-9]([a-zA-Z0-9.-])*$" + description: | + Must begin with a letter or number and be alphanumeric or '-' and '.' + + image: + label: Operating System + description: | + Required: Server image used for all servers that are created as a part of + this deployment. + type: string + default: CentOS 6.5 (PVHVM) + constraints: + - allowed_values: + - CentOS 6.5 (PVHVM) + - Red Hat Enterprise Linux 6.5 (PVHVM) + - Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM) + - Ubuntu 12.04 LTS (Precise Pangolin) + - Debian 7 (Wheezy) (PVHVM) + description: Must be a supported operating system. + + flavor: + label: Server Size + description: | + Required: Rackspace Cloud Server flavor to use. The size is based on the + amount of RAM for the provisioned server. + type: string + default: 1 GB Performance + constraints: + - allowed_values: + - 1 GB Performance + - 2 GB Performance + - 4 GB Performance + - 8 GB Performance + - 15 GB Performance + - 30 GB Performance + description: | + Must be a valid Rackspace Cloud Server flavor for the region you have + selected to deploy into. + + phpmyadmin_user: + label: Username + description: "Username for phpMyAdmin logins." + type: string + default: serverinfo + constraints: + - allowed_pattern: "^(.){1,16}$" + description: | + Must be shorter than 16 characters, this is due to MySQL's maximum + username length. + + kitchen: + description: URL for the kitchen to use + type: string + default: https://github.com/rackspace-orchestration-templates/lamp + + chef_version: + description: Version of chef client to use + type: string + default: 11.12.8 + +resources: + + ssh_key: + type: "OS::Nova::KeyPair" + properties: + name: { get_param: "OS::stack_id" } + save_private_key: true + + mysql_root_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_repl_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + mysql_debian_password: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + phpmyadmin_pass: + type: "OS::Heat::RandomString" + properties: + length: 16 + sequence: lettersdigits + + linux_server: + type: "Rackspace::Cloud::Server" + properties: + name: { get_param: server_hostname } + flavor: { get_param: flavor } + image: { get_param: image } + key_name: { get_resource: ssh_key } + config_drive: "true" + metadata: + rax-heat: { get_param: "OS::stack_id" } + metadata: + foo: bar + baz: brrr + + linux_setup: + type: "OS::Heat::ChefSolo" + depends_on: linux_server + properties: + username: root + private_key: { get_attr: [ssh_key, private_key] } + host: { get_attr: [linux_server, accessIPv4] } + kitchen: { get_param: kitchen } + chef_version: { get_param: chef_version } + node: + mysql: + server_root_password: { get_attr: [mysql_root_password, value] } + server_repl_password: { get_attr: [mysql_repl_password, value] } + server_debian_password: { get_attr: [mysql_debian_password, value] } + phpmyadmin: + pass: { get_attr: [phpmyadmin_pass, value] } + user: { get_param: phpmyadmin_user } + run_list: ["recipe[LAMP]"] + +outputs: + private_key: + description: SSH Private Key + value: { get_attr: [ssh_key, private_key] } + + server_ip: + description: Server IP + value: { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_url: + description: phpMyAdmin URL + value: + str_replace: + template: "http://%server_ip%/phpmyadmin" + params: + "%server_ip%": { get_attr: [linux_server, accessIPv4] } + + phpmyadmin_user: + description: phpMyAdmin User + value: { get_param: phpmyadmin_user } + + phpmyadmin_password: + description: phpMyAdmin Password + value: { get_attr: [phpmyadmin_pass, value] } + + mysql_root_password: + description: MySQL Root Password + value: { get_attr: [mysql_root_password, value] } From 36a201b72526d63f2475852545b0cad0c17dc028 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 19 Oct 2014 08:38:47 -0700 Subject: [PATCH 124/835] Removing unused class constant. --- tests/OpenCloud/Smoke/Unit/Orchestration.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Orchestration.php b/tests/OpenCloud/Smoke/Unit/Orchestration.php index e03465dbf..f642c86e2 100644 --- a/tests/OpenCloud/Smoke/Unit/Orchestration.php +++ b/tests/OpenCloud/Smoke/Unit/Orchestration.php @@ -27,8 +27,6 @@ class Orchestration extends AbstractUnit implements UnitInterface { - const STACK_NAME = 'PHPOpenCloudSmokeTestStack'; - protected $cleanupStackIds = array(); public function setupService() From e9d72d3f7177c1f0d773655893097221b2dc1ab8 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 20 Oct 2014 18:09:14 +0100 Subject: [PATCH 125/835] Let composer automatically decide what version constraint to use By default, composer will select `~1.10` right now. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c8fd1aa1..6b05d1d58 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ You must install this library through Composer: curl -sS https://getcomposer.org/installer | php # Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master +php composer.phar require rackspace/php-opencloud ``` Once you have installed the library, you will need to load Composer's autoloader (which registers all the required From 305a91f5c40d90f6c635afb0dc148c062df59e06 Mon Sep 17 00:00:00 2001 From: Jasper Aikema Date: Mon, 20 Oct 2014 22:37:31 +0200 Subject: [PATCH 126/835] Added suspend and resume support in class Server --- lib/OpenCloud/Compute/Resource/Server.php | 34 +++++++++++++++++++ tests/OpenCloud/Smoke/Unit/Compute.php | 24 +++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 12 +++++++ 3 files changed, 70 insertions(+) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index c1bce2a18..a30b0bc54 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -738,4 +738,38 @@ protected function updateJson($params = array()) { return (object) array('server' => (object) $params); } + + /** + * Suspend a server + * + * A suspend request suspend an instance, its VM state is stored on disk, all memory is written + * to disk, and the virtual machine is stopped. Suspending an instance is similar to placing a + * device in hibernation; memory and vCPUs become available to create other instances. + * + * @api + * @return \Guzzle\Http\Message\Response + */ + public function suspend() + { + $object = (object) array('suspend' => 'none'); + + return $this->action($object); + } + + /** + * Resume a server + * + * A resume request resumes a suspended instance, its VM state was stored on disk, all memory was written + * to disk, and the virtual machine was stopped. Resuming a suspended instance is similar to resuming a + * device from hibernation. + * + * @api + * @return \Guzzle\Http\Message\Response + */ + public function resume() + { + $object = (object) array('resume' => 'none'); + + return $this->action($object); + } } diff --git a/tests/OpenCloud/Smoke/Unit/Compute.php b/tests/OpenCloud/Smoke/Unit/Compute.php index d49837b41..ea3c04d31 100644 --- a/tests/OpenCloud/Smoke/Unit/Compute.php +++ b/tests/OpenCloud/Smoke/Unit/Compute.php @@ -165,6 +165,30 @@ public function main() return false; } + sleep(3); + + // Suspend + $this->step('Suspend the server'); + $server->suspend() + $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); + + if ($server->status() == 'ERROR') { + $this->stepInfo("Server suspension failed with ERROR\n"); + return false; + } + + sleep(3); + + // Resume + $this->step('Resume the server'); + $server->resume() + $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); + + if ($server->status() == 'ERROR') { + $this->stepInfo("Server resuming failed with ERROR\n"); + return false; + } + sleep(3); // Attach volume diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index d894e69a4..eb741f96e 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -116,6 +116,18 @@ public function test_Rebuild3() $this->assertNotNull($resp->getStatusCode()); } + public function test_Suspend() + { + $resp = $this->server->suspend(); + $this->assertNotNull($resp->getStatusCode()); + } + + public function test_Resume() + { + $resp = $this->server->resume(); + $this->assertNotNull($resp->getStatusCode()); + } + public function test_Delete() { $resp = $this->server->delete(); From b3db52dbc7ef06bdb54167fcddddfd96cebe97f9 Mon Sep 17 00:00:00 2001 From: Jasper Aikema Date: Mon, 20 Oct 2014 22:37:31 +0200 Subject: [PATCH 127/835] Add suspend and resume support in class Server --- lib/OpenCloud/Compute/Resource/Server.php | 34 +++++++++++++++++++ tests/OpenCloud/Smoke/Unit/Compute.php | 24 +++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 12 +++++++ 3 files changed, 70 insertions(+) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index c1bce2a18..a30b0bc54 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -738,4 +738,38 @@ protected function updateJson($params = array()) { return (object) array('server' => (object) $params); } + + /** + * Suspend a server + * + * A suspend request suspend an instance, its VM state is stored on disk, all memory is written + * to disk, and the virtual machine is stopped. Suspending an instance is similar to placing a + * device in hibernation; memory and vCPUs become available to create other instances. + * + * @api + * @return \Guzzle\Http\Message\Response + */ + public function suspend() + { + $object = (object) array('suspend' => 'none'); + + return $this->action($object); + } + + /** + * Resume a server + * + * A resume request resumes a suspended instance, its VM state was stored on disk, all memory was written + * to disk, and the virtual machine was stopped. Resuming a suspended instance is similar to resuming a + * device from hibernation. + * + * @api + * @return \Guzzle\Http\Message\Response + */ + public function resume() + { + $object = (object) array('resume' => 'none'); + + return $this->action($object); + } } diff --git a/tests/OpenCloud/Smoke/Unit/Compute.php b/tests/OpenCloud/Smoke/Unit/Compute.php index d49837b41..ea3c04d31 100644 --- a/tests/OpenCloud/Smoke/Unit/Compute.php +++ b/tests/OpenCloud/Smoke/Unit/Compute.php @@ -165,6 +165,30 @@ public function main() return false; } + sleep(3); + + // Suspend + $this->step('Suspend the server'); + $server->suspend() + $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); + + if ($server->status() == 'ERROR') { + $this->stepInfo("Server suspension failed with ERROR\n"); + return false; + } + + sleep(3); + + // Resume + $this->step('Resume the server'); + $server->resume() + $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); + + if ($server->status() == 'ERROR') { + $this->stepInfo("Server resuming failed with ERROR\n"); + return false; + } + sleep(3); // Attach volume diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index d894e69a4..eb741f96e 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -116,6 +116,18 @@ public function test_Rebuild3() $this->assertNotNull($resp->getStatusCode()); } + public function test_Suspend() + { + $resp = $this->server->suspend(); + $this->assertNotNull($resp->getStatusCode()); + } + + public function test_Resume() + { + $resp = $this->server->resume(); + $this->assertNotNull($resp->getStatusCode()); + } + public function test_Delete() { $resp = $this->server->delete(); From e4394ba90bd567546e5d0be1a586bd11b67700d7 Mon Sep 17 00:00:00 2001 From: Jasper Aikema Date: Mon, 20 Oct 2014 23:39:21 +0200 Subject: [PATCH 128/835] Added a check for the os-admin-actions extension when using the suspend and resume command --- lib/OpenCloud/Compute/Resource/Server.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index a30b0bc54..27fa2c479 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -751,6 +751,9 @@ protected function updateJson($params = array()) */ public function suspend() { + // The resume action is only available when the os-admin-actions extension is installed. + $this->checkExtension('os-admin-actions'); + $object = (object) array('suspend' => 'none'); return $this->action($object); @@ -768,6 +771,9 @@ public function suspend() */ public function resume() { + // The resume action is only available when the os-admin-actions extension is installed. + $this->checkExtension('os-admin-actions'); + $object = (object) array('resume' => 'none'); return $this->action($object); From 5d28e2ae34efe6765098ccf78acadeb3cdfaee2d Mon Sep 17 00:00:00 2001 From: Jasper Aikema Date: Mon, 20 Oct 2014 23:52:19 +0200 Subject: [PATCH 129/835] Added a check for the os-admin-actions extension when using the suspend and resume command --- lib/OpenCloud/Compute/Resource/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 27fa2c479..e4e421250 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -751,7 +751,7 @@ protected function updateJson($params = array()) */ public function suspend() { - // The resume action is only available when the os-admin-actions extension is installed. + // The suspend action is only available when the os-admin-actions extension is installed. $this->checkExtension('os-admin-actions'); $object = (object) array('suspend' => 'none'); From 40a67eaf1510c4f93acda7938e9cb23f13747a93 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 21 Oct 2014 14:35:41 -0700 Subject: [PATCH 130/835] Removing detail switch as the corresponding HTTP API call doesn't exist. --- lib/OpenCloud/LoadBalancer/Service.php | 5 +-- samples/LoadBalancer/list-load-balancers.php | 45 ++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 samples/LoadBalancer/list-load-balancers.php diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index b4bd6d414..8ba659f87 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -44,7 +44,7 @@ public function loadBalancer($id = null) * Return a paginated collection of load balancers * * @param bool $detail If TRUE, all details are returned; otherwise, a - * minimal set (ID, name) is retrieved + * minimal set (ID, name) is retrieved [DEPRECATED] * @param array $filter Optional query params used for search * @return \OpenCloud\Common\Collection\PaginatedIterator */ @@ -52,9 +52,6 @@ public function loadBalancerList($detail = true, array $filter = array()) { $url = $this->getUrl(); $url->addPath(Resource\LoadBalancer::resourceName()); - if ($detail) { - $url->addPath('detail'); - } $url->setQuery($filter); return $this->resourceList('LoadBalancer', $url); diff --git a/samples/LoadBalancer/list-load-balancers.php b/samples/LoadBalancer/list-load-balancers.php new file mode 100644 index 000000000..a5e68c1bc --- /dev/null +++ b/samples/LoadBalancer/list-load-balancers.php @@ -0,0 +1,45 @@ + getenv('OS_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an LoadBalancer service object from the client. +$region = getenv('OS_REGION_NAME'); +$loadBalancerService = $client->loadBalancerService(null, $region); + +// 3. Get load balancers. +$loadBalancers = $loadBalancerService->loadBalancerList(); +foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + var_dump($loadBalancer); +} From e9edc7601c34d4e2477d0425229bbb737cb98c5c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 22 Oct 2014 19:38:45 -0700 Subject: [PATCH 131/835] Fixing syntax error. --- tests/OpenCloud/Smoke/Unit/Orchestration.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/OpenCloud/Smoke/Unit/Orchestration.php b/tests/OpenCloud/Smoke/Unit/Orchestration.php index f642c86e2..425526468 100644 --- a/tests/OpenCloud/Smoke/Unit/Orchestration.php +++ b/tests/OpenCloud/Smoke/Unit/Orchestration.php @@ -126,6 +126,7 @@ public function main() $this->stepInfo('%-25s | %s', 'Metadata key', 'Metadata value'); foreach ($metadata as $key => $value) { $this->stepInfo('%-25s | %s', $key, $value); + } $this->step('List stack events'); $events = $stack->listEvents(); From dd10757d22b5cb2be05a2ee6d6e4a327f8ea82cc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 22 Oct 2014 20:14:28 -0700 Subject: [PATCH 132/835] Using aliases for disambiguating name of member. --- .../Orchestration/Resource/ResourceType.php | 18 +++--------------- lib/OpenCloud/Orchestration/Resource/Stack.php | 2 ++ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Orchestration/Resource/ResourceType.php b/lib/OpenCloud/Orchestration/Resource/ResourceType.php index 1d8717af7..4f6a81bf9 100644 --- a/lib/OpenCloud/Orchestration/Resource/ResourceType.php +++ b/lib/OpenCloud/Orchestration/Resource/ResourceType.php @@ -33,25 +33,13 @@ class ResourceType extends ReadOnlyResource protected $resourceType; protected $attributes; - protected $_properties; // Named so because the Base class has a $properties member. + protected $resourceTypeProperties; // Named so because the Base class has a $properties member. protected $aliases = array( - 'resource_type' => 'resourceType' + 'resource_type' => 'resourceType', + 'properties' => 'resourceTypeProperties' ); - /** - * Required to prevent the Base class from attempting to populate $this->properties. - */ - protected function setProperties($properties) - { - $this->_properties = $properties; - } - - public function getProperties() - { - return $this->_properties; - } - /** * Returns the template representation for this resource type. * diff --git a/lib/OpenCloud/Orchestration/Resource/Stack.php b/lib/OpenCloud/Orchestration/Resource/Stack.php index 8e053aed4..53162b1ec 100644 --- a/lib/OpenCloud/Orchestration/Resource/Stack.php +++ b/lib/OpenCloud/Orchestration/Resource/Stack.php @@ -31,6 +31,7 @@ class Stack extends PersistentResource protected static $json_name = 'stack'; protected $id; + protected $parentStack; // Named so because the Base class has a $parent member. protected $disableRollback; protected $description; protected $parameters; @@ -49,6 +50,7 @@ class Stack extends PersistentResource protected $links; protected $aliases = array( + 'parent' => 'parentStack', 'disable_rollback' => 'disableRollback', 'stack_name' => 'name', 'stack_status' => 'status', From dd42e178e28e3530aeb67386a91ffe61b6a4740b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 23 Oct 2014 13:14:19 -0700 Subject: [PATCH 133/835] Sleeping for 10 seconds to let abandoned stack be deleted. --- tests/OpenCloud/Smoke/Unit/Orchestration.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/OpenCloud/Smoke/Unit/Orchestration.php b/tests/OpenCloud/Smoke/Unit/Orchestration.php index 425526468..57ee8223b 100644 --- a/tests/OpenCloud/Smoke/Unit/Orchestration.php +++ b/tests/OpenCloud/Smoke/Unit/Orchestration.php @@ -198,6 +198,7 @@ public function main() $this->stepInfo('Abandon stack data: %s ', $abandonedStackData); $this->step('Adopt stack'); + sleep(10); // For abandoned stack to get deleted. $stack = $this->getService()->adoptStack(array( 'name' => 'simple-lamp-setup-from-template-url', 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', From 0f221dcd05c22cd36be8d5d1da43916289302383 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 04:40:11 -0700 Subject: [PATCH 134/835] Fixing syntax boo boos. --- tests/OpenCloud/Smoke/Unit/Compute.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Compute.php b/tests/OpenCloud/Smoke/Unit/Compute.php index ea3c04d31..a288d68e3 100644 --- a/tests/OpenCloud/Smoke/Unit/Compute.php +++ b/tests/OpenCloud/Smoke/Unit/Compute.php @@ -169,7 +169,7 @@ public function main() // Suspend $this->step('Suspend the server'); - $server->suspend() + $server->suspend(); $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); if ($server->status() == 'ERROR') { @@ -181,7 +181,7 @@ public function main() // Resume $this->step('Resume the server'); - $server->resume() + $server->resume(); $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); if ($server->status() == 'ERROR') { From b7ec7a72287a626838e145dbbc48f29a1b1fd740 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 05:19:03 -0700 Subject: [PATCH 135/835] Adding lint checking since we've been bitten a couple times now. --- .travis.yml | 1 + composer.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fad14d88c..dd45013f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ branches: before_script: - composer install --prefer-source + - ./vendor/bin/parallel-lint --exclude vendor . after_script: - php vendor/bin/coveralls -v diff --git a/composer.json b/composer.json index 5bf3d0d36..55e7974bc 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ }, "require-dev" : { "guzzle/guzzle": "~3.8", - "satooshi/php-coveralls": "0.6.*@dev" + "satooshi/php-coveralls": "0.6.*@dev", + "jakub-onderka/php-parallel-lint": "0.*" } } From fc9420a176ffe399eac11b46106b77484a8b25fe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 05:53:51 -0700 Subject: [PATCH 136/835] Running php-cs-fixer with level=psr2. --- .../Resource/LaunchConfiguration.php | 1 - .../Collection/MonitoringIterator.php | 2 +- .../CloudMonitoring/Resource/Agent.php | 2 +- .../CloudMonitoring/Resource/AgentHost.php | 2 +- .../CloudMonitoring/Resource/AgentTarget.php | 1 - .../Resource/ReadOnlyResource.php | 1 - lib/OpenCloud/Common/Base.php | 11 --------- lib/OpenCloud/Common/Collection.php | 1 - .../Common/Collection/PaginatedIterator.php | 1 - .../Exceptions/ResourceBucketException.php | 1 - .../Common/Http/Message/Formatter.php | 1 - lib/OpenCloud/Common/Lang.php | 1 - lib/OpenCloud/Common/Log/Logger.php | 3 +-- .../Common/Resource/BaseResource.php | 1 - .../Common/Resource/PersistentResource.php | 1 - lib/OpenCloud/Common/Service/NovaService.php | 1 - .../Common/Service/ServiceBuilder.php | 1 - lib/OpenCloud/Compute/Resource/Flavor.php | 1 - lib/OpenCloud/Compute/Resource/Network.php | 2 -- lib/OpenCloud/Compute/Resource/Server.php | 3 --- .../Compute/Resource/ServerMetadata.php | 1 - .../Compute/Resource/VolumeAttachment.php | 1 - lib/OpenCloud/DNS/Resource/AsyncResponse.php | 1 - lib/OpenCloud/DNS/Resource/Domain.php | 2 -- lib/OpenCloud/Database/Resource/Datastore.php | 2 +- lib/OpenCloud/Identity/Constants/User.php | 1 - lib/OpenCloud/Identity/Service.php | 1 - lib/OpenCloud/Image/Resource/Image.php | 1 - .../Image/Resource/JsonPatch/Operation.php | 2 +- .../ObjectStore/Resource/Container.php | 1 - .../ObjectStore/Upload/ConcurrentTransfer.php | 2 -- .../Upload/ConsecutiveTransfer.php | 2 -- .../ObjectStore/Upload/DirectorySync.php | 1 - lib/OpenCloud/OpenStack.php | 3 --- lib/OpenCloud/Orchestration/Service.php | 2 +- lib/OpenCloud/Queues/Resource/Claim.php | 1 - lib/OpenCloud/Queues/Resource/Message.php | 1 - lib/OpenCloud/Queues/Resource/Queue.php | 1 - lib/OpenCloud/Rackspace.php | 1 - samples/Compute/attach_volume.php | 2 +- samples/Compute/create_network.php | 2 +- samples/Compute/create_new_keypair.php | 2 +- samples/Compute/create_server.php | 2 +- .../create_server_from_bootable_volume.php | 2 +- ..._bootable_volume_delete_on_termination.php | 2 +- .../Compute/create_server_with_keypair.php | 2 +- samples/Compute/create_volume.php | 2 +- samples/Compute/delete_server.php | 2 +- samples/Compute/detach_volume.php | 2 +- samples/Compute/update_server.php | 2 +- samples/Compute/upload_existing_keypair.php | 2 +- samples/Database/delete-configuration.php | 2 +- .../delete-container-recursive.php | 2 +- samples/ObjectStore/delete-container.php | 2 +- samples/ObjectStore/delete-object.php | 2 +- .../ObjectStore/get-account-bytes-used.php | 2 +- .../get-account-container-count.php | 2 +- .../ObjectStore/get-account-object-count.php | 2 +- .../get-account-temp-url-secret.php | 2 +- .../ObjectStore/get-container-bytes-quota.php | 2 +- .../ObjectStore/get-container-bytes-used.php | 2 +- .../ObjectStore/get-container-count-quota.php | 2 +- .../get-container-object-count.php | 2 +- samples/ObjectStore/get-object.php | 2 +- samples/ObjectStore/list-containers.php | 2 +- samples/ObjectStore/quickstart.php | 2 +- .../ObjectStore/set-container-count-quota.php | 2 +- .../ObjectStore/set-container-metadata.php | 2 +- samples/ObjectStore/upload-large-object.php | 2 +- .../ObjectStore/upload-multiple-objects.php | 2 +- samples/Volume/list-snapshots.php | 2 +- samples/Volume/list-volumes.php | 2 +- tests/OpenCloud/Smoke/Enum.php | 6 ++--- tests/OpenCloud/Smoke/Logger.php | 2 -- tests/OpenCloud/Smoke/Runner.php | 12 ++++------ tests/OpenCloud/Smoke/SmokeException.php | 4 ++-- tests/OpenCloud/Smoke/Step.php | 15 ++++++------ tests/OpenCloud/Smoke/Unit/AbstractUnit.php | 23 +++++++++--------- tests/OpenCloud/Smoke/Unit/Autoscale.php | 5 ---- .../OpenCloud/Smoke/Unit/CloudMonitoring.php | 4 ++-- tests/OpenCloud/Smoke/Unit/DNS.php | 24 +++++++++---------- tests/OpenCloud/Smoke/Unit/Database.php | 5 ++-- tests/OpenCloud/Smoke/Unit/Identity.php | 2 +- tests/OpenCloud/Smoke/Unit/LoadBalancer.php | 22 ++++++++--------- tests/OpenCloud/Smoke/Unit/ObjectStore.php | 7 +++--- tests/OpenCloud/Smoke/Unit/Orchestration.php | 12 ++++------ tests/OpenCloud/Smoke/Unit/UnitInterface.php | 4 +--- tests/OpenCloud/Smoke/Unit/Volume.php | 6 ++--- tests/OpenCloud/Smoke/Utils.php | 10 ++++---- .../Tests/Autoscale/Resource/GroupTest.php | 2 +- .../Autoscale/Resource/ScalingPolicyTest.php | 1 - .../CloudMonitoring/Resource/AgentTest.php | 1 - .../Resource/AgentTokenTest.php | 1 - .../CloudMonitoring/Resource/AlarmTest.php | 1 - .../Resource/ChangelogTest.php | 1 - .../CloudMonitoring/Resource/CheckTest.php | 1 - .../CloudMonitoring/Resource/EntityTest.php | 1 - .../CloudMonitoring/Resource/MetricTest.php | 1 - .../Resource/NotificationHistoryTest.php | 1 - .../Resource/NotificationPlanTest.php | 1 - .../Resource/NotificationTest.php | 1 - .../Resource/NotificationTypeTest.php | 1 - .../CloudMonitoring/Resource/ViewTest.php | 1 - .../CloudMonitoring/Resource/ZoneTest.php | 1 - tests/OpenCloud/Tests/Common/BaseTest.php | 2 -- .../Common/Collection/ArrayCollectionTest.php | 1 - .../OpenCloud/Tests/Common/Log/LoggerTest.php | 1 - tests/OpenCloud/Tests/Common/MetadataTest.php | 1 - .../Tests/Common/PersistentObjectTest.php | 1 - tests/OpenCloud/Tests/Common/ServiceTest.php | 1 - .../Tests/Compute/Resource/ImageTest.php | 1 - .../Tests/Compute/Resource/KeyPairTest.php | 1 - .../Tests/Compute/Resource/NetworkTest.php | 1 - .../Compute/Resource/ServerMetadataTest.php | 1 - .../Compute/Resource/VolumeAttachmentTest.php | 1 - tests/OpenCloud/Tests/Compute/ServiceTest.php | 3 +-- .../Tests/DNS/Resource/DomainTest.php | 1 - .../Tests/DNS/Resource/PtrRecordTest.php | 2 +- .../Tests/DNS/Resource/RecordTest.php | 1 - tests/OpenCloud/Tests/DNS/ServiceTest.php | 1 - .../Database/Resource/ConfigurationTest.php | 5 +--- .../Tests/Database/Resource/DatastoreTest.php | 3 +-- .../Resource/DatastoreVersionTest.php | 3 +-- .../Tests/Database/Resource/InstanceTest.php | 1 - .../OpenCloud/Tests/Database/ServiceTest.php | 1 - .../Tests/Identity/Resource/RoleTest.php | 2 +- .../Tests/Identity/Resource/TokenTest.php | 2 +- .../Tests/Identity/Resource/UserTest.php | 1 - .../Tests/Image/Resource/ImageTest.php | 2 +- .../Image/Resource/JsonPatch/DocumentTest.php | 2 +- .../Image/Resource/JsonPatch/EncoderTest.php | 2 +- .../Resource/JsonPatch/OperationTest.php | 2 +- .../Tests/Image/Resource/MemberTest.php | 2 +- .../Image/Resource/Schema/PropertyTest.php | 2 +- .../Image/Resource/Schema/SchemaTest.php | 2 +- tests/OpenCloud/Tests/Image/ServiceTest.php | 2 +- .../Tests/LoadBalancer/ServiceTest.php | 1 - tests/OpenCloud/Tests/MockSubscriber.php | 2 +- .../Tests/ObjectStore/ObjectStoreTestCase.php | 2 +- .../ObjectStore/Resource/DataObjectTest.php | 1 - .../ObjectStore/Resource/TransferTest.php | 1 - .../Tests/ObjectStore/ServiceTest.php | 1 - tests/OpenCloud/Tests/OpenStackTest.php | 2 +- .../Orchestration/OrchestrationTestCase.php | 18 +++++++++----- .../Tests/Orchestration/ServiceTest.php | 1 - .../Tests/Queues/Resource/QueueTest.php | 5 +--- tests/OpenCloud/Tests/Queues/ServiceTest.php | 1 - tests/OpenCloud/Tests/RackspaceTest.php | 1 - tests/OpenCloud/Tests/VersionTest.php | 2 +- .../Tests/Volume/Resource/SnapshotTest.php | 5 ++-- .../Tests/Volume/Resource/VolumeTest.php | 4 ++-- .../Tests/Volume/Resource/VolumeTypeTest.php | 1 - tests/OpenCloud/Tests/Volume/ServiceTest.php | 1 - .../OpenCloud/Tests/Volume/VolumeTestCase.php | 2 +- tests/bootstrap.php | 2 +- 155 files changed, 150 insertions(+), 267 deletions(-) diff --git a/lib/OpenCloud/Autoscale/Resource/LaunchConfiguration.php b/lib/OpenCloud/Autoscale/Resource/LaunchConfiguration.php index f4f683f7a..29951254c 100644 --- a/lib/OpenCloud/Autoscale/Resource/LaunchConfiguration.php +++ b/lib/OpenCloud/Autoscale/Resource/LaunchConfiguration.php @@ -38,7 +38,6 @@ */ class LaunchConfiguration extends AbstractResource { - public $type; public $args; diff --git a/lib/OpenCloud/CloudMonitoring/Collection/MonitoringIterator.php b/lib/OpenCloud/CloudMonitoring/Collection/MonitoringIterator.php index 3f348e2f4..0f629bf39 100644 --- a/lib/OpenCloud/CloudMonitoring/Collection/MonitoringIterator.php +++ b/lib/OpenCloud/CloudMonitoring/Collection/MonitoringIterator.php @@ -32,4 +32,4 @@ public function parseResponseBody($body) return $parsed; } -} \ No newline at end of file +} diff --git a/lib/OpenCloud/CloudMonitoring/Resource/Agent.php b/lib/OpenCloud/CloudMonitoring/Resource/Agent.php index a566dc83f..eead7c6f4 100644 --- a/lib/OpenCloud/CloudMonitoring/Resource/Agent.php +++ b/lib/OpenCloud/CloudMonitoring/Resource/Agent.php @@ -89,4 +89,4 @@ public function getAgentHost() { return $this->getService()->resource('AgentHost', null, $this); } -} \ No newline at end of file +} diff --git a/lib/OpenCloud/CloudMonitoring/Resource/AgentHost.php b/lib/OpenCloud/CloudMonitoring/Resource/AgentHost.php index 3e71c8a9f..b056c60c3 100644 --- a/lib/OpenCloud/CloudMonitoring/Resource/AgentHost.php +++ b/lib/OpenCloud/CloudMonitoring/Resource/AgentHost.php @@ -57,4 +57,4 @@ public function info($type) 'resourceClass' => 'AgentHostInfo' )); } -} \ No newline at end of file +} diff --git a/lib/OpenCloud/CloudMonitoring/Resource/AgentTarget.php b/lib/OpenCloud/CloudMonitoring/Resource/AgentTarget.php index 795bd4a1c..b5b85025b 100644 --- a/lib/OpenCloud/CloudMonitoring/Resource/AgentTarget.php +++ b/lib/OpenCloud/CloudMonitoring/Resource/AgentTarget.php @@ -24,7 +24,6 @@ */ class AgentTarget extends ReadOnlyResource { - private $type = 'agent.filesystem'; protected static $json_name = 'targets'; diff --git a/lib/OpenCloud/CloudMonitoring/Resource/ReadOnlyResource.php b/lib/OpenCloud/CloudMonitoring/Resource/ReadOnlyResource.php index 48f2c0329..4fd3681f3 100644 --- a/lib/OpenCloud/CloudMonitoring/Resource/ReadOnlyResource.php +++ b/lib/OpenCloud/CloudMonitoring/Resource/ReadOnlyResource.php @@ -24,7 +24,6 @@ */ class ReadOnlyResource extends AbstractResource { - public function create($params = array()) { return $this->noCreate(); diff --git a/lib/OpenCloud/Common/Base.php b/lib/OpenCloud/Common/Base.php index f0b0986ad..3e0bc3dfb 100644 --- a/lib/OpenCloud/Common/Base.php +++ b/lib/OpenCloud/Common/Base.php @@ -110,7 +110,6 @@ protected function setProperty($property, $value) if (method_exists($this, $setter)) { return call_user_func(array($this, $setter), $value); } elseif (false !== ($propertyVal = $this->propertyExists($property))) { - // Are we setting a public or private property? if ($this->isAccessible($propertyVal)) { $this->$propertyVal = $value; @@ -120,7 +119,6 @@ protected function setProperty($property, $value) return $this; } else { - $this->getLogger()->warning( 'Attempted to set {property} with value {value}, but the' . ' property has not been defined. Please define first.', @@ -284,15 +282,11 @@ public function url($path = null, array $query = array()) public function populate($info, $setObjects = true) { if (is_string($info) || is_integer($info)) { - $this->setProperty($this->primaryKeyField(), $info); $this->refresh($info); } elseif (is_object($info) || is_array($info)) { - foreach ($info as $key => $value) { - if ($key == 'metadata' || $key == 'meta') { - // Try retrieving existing value if (null === ($metadata = $this->getProperty($key))) { // If none exists, create new object @@ -305,10 +299,8 @@ public function populate($info, $setObjects = true) // Set object property $this->setProperty($key, $metadata); } elseif (!empty($this->associatedResources[$key]) && $setObjects === true) { - // Associated resource try { - $resource = $this->getService()->resource($this->associatedResources[$key], $value); $resource->setParent($this); @@ -316,10 +308,8 @@ public function populate($info, $setObjects = true) } catch (Exception\ServiceException $e) { } } elseif (!empty($this->associatedCollections[$key]) && $setObjects === true) { - // Associated collection try { - $className = $this->associatedCollections[$key]; $options = $this->makeResourceIteratorOptions($className); $iterator = ResourceIterator::factory($this, $options, $value); @@ -328,7 +318,6 @@ public function populate($info, $setObjects = true) } catch (Exception\ServiceException $e) { } } elseif (!empty($this->aliases[$key])) { - // Sometimes we might want to preserve camelCase // or covert `rax-bandwidth:bandwidth` to `raxBandwidth` $this->setProperty($this->aliases[$key], $value); diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index 7d4990c55..f2710639e 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -23,7 +23,6 @@ */ class Collection extends Base { - private $service; private $itemClass; private $itemList = array(); diff --git a/lib/OpenCloud/Common/Collection/PaginatedIterator.php b/lib/OpenCloud/Common/Collection/PaginatedIterator.php index 7c202f0fb..a0628993d 100644 --- a/lib/OpenCloud/Common/Collection/PaginatedIterator.php +++ b/lib/OpenCloud/Common/Collection/PaginatedIterator.php @@ -265,7 +265,6 @@ public function extractNextLink($body) public function constructNextUrl() { if (!$url = $this->nextUrl) { - $url = clone $this->getOption('baseUrl'); $query = $url->getQuery(); diff --git a/lib/OpenCloud/Common/Exceptions/ResourceBucketException.php b/lib/OpenCloud/Common/Exceptions/ResourceBucketException.php index 94dda945e..82c7ea2b0 100644 --- a/lib/OpenCloud/Common/Exceptions/ResourceBucketException.php +++ b/lib/OpenCloud/Common/Exceptions/ResourceBucketException.php @@ -19,5 +19,4 @@ class ResourceBucketException extends \Exception { - } diff --git a/lib/OpenCloud/Common/Http/Message/Formatter.php b/lib/OpenCloud/Common/Http/Message/Formatter.php index 0c6864029..1f2b8c2a4 100644 --- a/lib/OpenCloud/Common/Http/Message/Formatter.php +++ b/lib/OpenCloud/Common/Http/Message/Formatter.php @@ -24,7 +24,6 @@ class Formatter { - public static function decode(Response $response) { if (strpos($response->getHeader(Header::CONTENT_TYPE), Mime::JSON) !== false) { diff --git a/lib/OpenCloud/Common/Lang.php b/lib/OpenCloud/Common/Lang.php index bffdbffb0..7c124e5bc 100644 --- a/lib/OpenCloud/Common/Lang.php +++ b/lib/OpenCloud/Common/Lang.php @@ -19,7 +19,6 @@ class Lang { - public static function translate($word = null) { return $word; diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index c9de3ea3c..7eb48cdc0 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -235,7 +235,6 @@ private function dispatch($message, $context) // Output to file file_put_contents($file, $this->formatFileLine($output), FILE_APPEND); } else { - echo $output; } } @@ -246,4 +245,4 @@ public function deprecated($method, $new) return $this->warning($string); } -} \ No newline at end of file +} diff --git a/lib/OpenCloud/Common/Resource/BaseResource.php b/lib/OpenCloud/Common/Resource/BaseResource.php index 1a9c5e80e..76c48cf10 100644 --- a/lib/OpenCloud/Common/Resource/BaseResource.php +++ b/lib/OpenCloud/Common/Resource/BaseResource.php @@ -148,7 +148,6 @@ public function getMetadata() public function getUrl($path = null, array $query = array()) { if (!$url = $this->findLink('self')) { - // ...otherwise construct a URL from parent and this resource's // "URL name". If no name is set, resourceName() throws an error. $url = $this->getParent()->getUrl($this->resourceName()); diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 7b449abdc..e87d46323 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -184,7 +184,6 @@ public function waitFor($state = null, $timeout = null, $callback = null, $inter $states = array('ERROR', $state); while (true) { - $this->refresh($this->getProperty($this->primaryKeyField())); if ($callback) { diff --git a/lib/OpenCloud/Common/Service/NovaService.php b/lib/OpenCloud/Common/Service/NovaService.php index e3fb1a894..e03508ad4 100644 --- a/lib/OpenCloud/Common/Service/NovaService.php +++ b/lib/OpenCloud/Common/Service/NovaService.php @@ -24,7 +24,6 @@ */ abstract class NovaService extends CatalogService { - /** * Returns a flavor from the service * diff --git a/lib/OpenCloud/Common/Service/ServiceBuilder.php b/lib/OpenCloud/Common/Service/ServiceBuilder.php index 3fa394ea9..cf59f4a2b 100644 --- a/lib/OpenCloud/Common/Service/ServiceBuilder.php +++ b/lib/OpenCloud/Common/Service/ServiceBuilder.php @@ -25,7 +25,6 @@ */ class ServiceBuilder { - /** * Simple factory method for creating services. * diff --git a/lib/OpenCloud/Compute/Resource/Flavor.php b/lib/OpenCloud/Compute/Resource/Flavor.php index 13c55bbb5..b21a1fe97 100644 --- a/lib/OpenCloud/Compute/Resource/Flavor.php +++ b/lib/OpenCloud/Compute/Resource/Flavor.php @@ -25,7 +25,6 @@ */ class Flavor extends PersistentObject { - public $status; public $updated; public $vcpus; diff --git a/lib/OpenCloud/Compute/Resource/Network.php b/lib/OpenCloud/Compute/Resource/Network.php index c2fefd016..82aba2580 100644 --- a/lib/OpenCloud/Compute/Resource/Network.php +++ b/lib/OpenCloud/Compute/Resource/Network.php @@ -28,7 +28,6 @@ */ class Network extends PersistentObject { - public $id; public $label; public $cidr; @@ -128,7 +127,6 @@ protected function createJson() public function getUrl($path = null, array $query = array()) { if (!$url = $this->findLink('self')) { - $url = $this->getParent()->getUrl($this->getResourcePath()); if (null !== ($primaryKey = $this->getProperty($this->primaryKeyField()))) { diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index e4e421250..20a7692e5 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -664,7 +664,6 @@ protected function createJson() // Boot from volume if ($this->volume instanceof Volume) { - $this->checkExtension('os-block-device-mapping-v2-boot'); $server->block_device_mapping_v2 = array(); @@ -675,12 +674,10 @@ protected function createJson() 'boot_index' => 0, 'delete_on_termination' => (boolean) $this->volumeDeleteOnTermination ); - } // Networks if (is_array($this->networks) && count($this->networks)) { - $server->networks = array(); foreach ($this->networks as $network) { diff --git a/lib/OpenCloud/Compute/Resource/ServerMetadata.php b/lib/OpenCloud/Compute/Resource/ServerMetadata.php index 11d851f99..28f2f8650 100644 --- a/lib/OpenCloud/Compute/Resource/ServerMetadata.php +++ b/lib/OpenCloud/Compute/Resource/ServerMetadata.php @@ -51,7 +51,6 @@ public function __construct(Server $parent, $key = null) // set the URL according to whether or not we have a key if ($this->getParent()->getId()) { - $this->url = $this->getParent()->url('metadata'); $this->key = $key; diff --git a/lib/OpenCloud/Compute/Resource/VolumeAttachment.php b/lib/OpenCloud/Compute/Resource/VolumeAttachment.php index 3caa28867..07892942c 100644 --- a/lib/OpenCloud/Compute/Resource/VolumeAttachment.php +++ b/lib/OpenCloud/Compute/Resource/VolumeAttachment.php @@ -26,7 +26,6 @@ */ class VolumeAttachment extends PersistentObject { - public $id; public $device; public $serverId; diff --git a/lib/OpenCloud/DNS/Resource/AsyncResponse.php b/lib/OpenCloud/DNS/Resource/AsyncResponse.php index 97e04808a..952aa1642 100644 --- a/lib/OpenCloud/DNS/Resource/AsyncResponse.php +++ b/lib/OpenCloud/DNS/Resource/AsyncResponse.php @@ -118,7 +118,6 @@ public function waitFor($state = null, $timeout = null, $callback = null, $inter $states = array('ERROR', $state); while ($continue) { - $body = $this->getClient()->get($jobUrl)->send()->json(); if ($callback) { diff --git a/lib/OpenCloud/DNS/Resource/Domain.php b/lib/OpenCloud/DNS/Resource/Domain.php index 9a3f7e004..cdd8ef28e 100644 --- a/lib/OpenCloud/DNS/Resource/Domain.php +++ b/lib/OpenCloud/DNS/Resource/Domain.php @@ -224,7 +224,6 @@ protected function createJson() // add records, if any if (count($this->records)) { - $recordsObject = (object) array('records' => array()); foreach ($this->records as $record) { @@ -241,7 +240,6 @@ protected function createJson() // add subdomains, if any if (count($this->subdomains)) { - $subdomainsObject = (object) array('domains' => array()); foreach ($this->subdomains as $subdomain) { diff --git a/lib/OpenCloud/Database/Resource/Datastore.php b/lib/OpenCloud/Database/Resource/Datastore.php index dadfe2915..3123fc740 100644 --- a/lib/OpenCloud/Database/Resource/Datastore.php +++ b/lib/OpenCloud/Database/Resource/Datastore.php @@ -36,7 +36,7 @@ class Datastore extends PersistentResource protected static $url_resource = 'datastores'; protected $associatedCollections = array( - 'version' => 'DatastoreVersion' + 'version' => 'DatastoreVersion' ); /** diff --git a/lib/OpenCloud/Identity/Constants/User.php b/lib/OpenCloud/Identity/Constants/User.php index efa58f085..2d944d4ab 100644 --- a/lib/OpenCloud/Identity/Constants/User.php +++ b/lib/OpenCloud/Identity/Constants/User.php @@ -19,7 +19,6 @@ class User { - const MODE_NAME = 'name'; const MODE_EMAIL = 'email'; const MODE_ID = 'id'; diff --git a/lib/OpenCloud/Identity/Service.php b/lib/OpenCloud/Identity/Service.php index d0f3064e4..9c90d2811 100644 --- a/lib/OpenCloud/Identity/Service.php +++ b/lib/OpenCloud/Identity/Service.php @@ -31,7 +31,6 @@ */ class Service extends AbstractService { - /** * Factory method which allows for easy service creation * diff --git a/lib/OpenCloud/Image/Resource/Image.php b/lib/OpenCloud/Image/Resource/Image.php index a10a4e12e..b8ab7407c 100644 --- a/lib/OpenCloud/Image/Resource/Image.php +++ b/lib/OpenCloud/Image/Resource/Image.php @@ -50,7 +50,6 @@ public function update(array $params, Schema $schema = null) $document = new JsonDocument(); foreach ($params as $propertyName => $value) { - // find property object if (!($property = $schema->getProperty($propertyName))) { // check whether additional properties are found diff --git a/lib/OpenCloud/Image/Resource/JsonPatch/Operation.php b/lib/OpenCloud/Image/Resource/JsonPatch/Operation.php index e5845c46c..583e4ee9f 100644 --- a/lib/OpenCloud/Image/Resource/JsonPatch/Operation.php +++ b/lib/OpenCloud/Image/Resource/JsonPatch/Operation.php @@ -75,7 +75,7 @@ public static function factory(Schema $schema, Property $property, $operationTyp */ public function setType($type) { - $this->type = $type; + $this->type = $type; } /** diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 8049a2c3c..3f47c2227 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -441,7 +441,6 @@ public function uploadObjects(array $files, array $commonHeaders = array()) $requests = $entities = array(); foreach ($files as $entity) { - if (empty($entity['name'])) { throw new Exceptions\InvalidArgumentError('You must provide a name.'); } diff --git a/lib/OpenCloud/ObjectStore/Upload/ConcurrentTransfer.php b/lib/OpenCloud/ObjectStore/Upload/ConcurrentTransfer.php index f011e4569..d0a58f736 100644 --- a/lib/OpenCloud/ObjectStore/Upload/ConcurrentTransfer.php +++ b/lib/OpenCloud/ObjectStore/Upload/ConcurrentTransfer.php @@ -36,13 +36,11 @@ public function transfer() $parts = $this->collectParts($workers); while ($this->transferState->count() < $totalParts) { - $completedParts = $this->transferState->count(); $requests = array(); // Iterate over number of workers until total completed parts is what we need it to be for ($i = 0; $i < $workers && ($completedParts + $i) < $totalParts; $i++) { - // Offset is the current pointer multiplied by the standard chunk length $offset = ($completedParts + $i) * $this->partSize; $parts[$i]->setOffset($offset); diff --git a/lib/OpenCloud/ObjectStore/Upload/ConsecutiveTransfer.php b/lib/OpenCloud/ObjectStore/Upload/ConsecutiveTransfer.php index acd13c519..50196802d 100644 --- a/lib/OpenCloud/ObjectStore/Upload/ConsecutiveTransfer.php +++ b/lib/OpenCloud/ObjectStore/Upload/ConsecutiveTransfer.php @@ -29,11 +29,9 @@ */ class ConsecutiveTransfer extends AbstractTransfer { - public function transfer() { while (!$this->entityBody->isConsumed()) { - if ($this->entityBody->getContentLength() && $this->entityBody->isSeekable()) { // Stream directly from the data $body = new ReadLimitEntityBody($this->entityBody, $this->partSize, $this->entityBody->ftell()); diff --git a/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php b/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php index 0e5a77e3e..b95475bf3 100644 --- a/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php +++ b/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php @@ -112,7 +112,6 @@ public function execute() // Handle PUT requests (create/update files) foreach ($localFiles as $filename) { - $callback = $this->getCallback($filename); $filePath = rtrim($this->basePath, '/') . '/' . $filename; diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index c0a415877..0ab054eef 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -123,7 +123,6 @@ public function setToken($token) $identity = IdentityService::factory($this); if (is_string($token)) { - if (!$this->token) { $this->setTokenObject($identity->resource('Token')); } @@ -198,7 +197,6 @@ public function setTenant($tenant) $identity = IdentityService::factory($this); if (is_numeric($tenant)) { - if (!$this->tenant) { $this->setTenantObject($identity->resource('Tenant')); } @@ -306,7 +304,6 @@ public function hasExpired() public function getCredentials() { if (!empty($this->secret['username']) && !empty($this->secret['password'])) { - $credentials = array('auth' => array( 'passwordCredentials' => array( 'username' => $this->secret['username'], diff --git a/lib/OpenCloud/Orchestration/Service.php b/lib/OpenCloud/Orchestration/Service.php index a1acb2559..81c1a3e53 100644 --- a/lib/OpenCloud/Orchestration/Service.php +++ b/lib/OpenCloud/Orchestration/Service.php @@ -147,7 +147,7 @@ public function getBuildInfo() { $buildInfo = $this->resource('BuildInfo'); $buildInfo->refresh(); - return $buildInfo; + return $buildInfo; } /** diff --git a/lib/OpenCloud/Queues/Resource/Claim.php b/lib/OpenCloud/Queues/Resource/Claim.php index fbc338467..fcb6ad9c1 100644 --- a/lib/OpenCloud/Queues/Resource/Claim.php +++ b/lib/OpenCloud/Queues/Resource/Claim.php @@ -25,7 +25,6 @@ */ class Claim extends PersistentObject { - const LIMIT_DEFAULT = 10; const GRACE_DEFAULT = 43200; const TTL_DEFAULT = 43200; diff --git a/lib/OpenCloud/Queues/Resource/Message.php b/lib/OpenCloud/Queues/Resource/Message.php index 38ea3e775..f1164717f 100644 --- a/lib/OpenCloud/Queues/Resource/Message.php +++ b/lib/OpenCloud/Queues/Resource/Message.php @@ -28,7 +28,6 @@ */ class Message extends PersistentObject { - /** * @var string */ diff --git a/lib/OpenCloud/Queues/Resource/Queue.php b/lib/OpenCloud/Queues/Resource/Queue.php index ea367e682..0a65723e9 100644 --- a/lib/OpenCloud/Queues/Resource/Queue.php +++ b/lib/OpenCloud/Queues/Resource/Queue.php @@ -211,7 +211,6 @@ public function createMessages(array $messages) ->send(); if (null !== ($location = $response->getHeader('Location'))) { - $parts = array_merge($this->getUrl()->getParts(), parse_url($location)); $url = Url::factory(Url::buildUrl($parts)); diff --git a/lib/OpenCloud/Rackspace.php b/lib/OpenCloud/Rackspace.php index d657f37b1..8480a0d71 100644 --- a/lib/OpenCloud/Rackspace.php +++ b/lib/OpenCloud/Rackspace.php @@ -54,7 +54,6 @@ public function getCredentials() $secret = $this->getSecret(); if (!empty($secret['username']) && !empty($secret['apiKey'])) { - $credentials = array('auth' => array( 'RAX-KSKEY:apiKeyCredentials' => array( 'username' => $secret['username'], diff --git a/samples/Compute/attach_volume.php b/samples/Compute/attach_volume.php index 7353e3aa6..cd7625150 100644 --- a/samples/Compute/attach_volume.php +++ b/samples/Compute/attach_volume.php @@ -57,4 +57,4 @@ // Specifying null will auto-assign the block. You also can be specific - '/dev/xvdb' $mountPoint = null; -$myServer->attachVolume($myVolume, $mountPoint); \ No newline at end of file +$myServer->attachVolume($myVolume, $mountPoint); diff --git a/samples/Compute/create_network.php b/samples/Compute/create_network.php index ea19005b7..002023486 100644 --- a/samples/Compute/create_network.php +++ b/samples/Compute/create_network.php @@ -45,4 +45,4 @@ $network->create(array( 'label' => 'Backend Network', 'cidr' => '192.168.0.0/16' -)); \ No newline at end of file +)); diff --git a/samples/Compute/create_new_keypair.php b/samples/Compute/create_new_keypair.php index 83f14a5a1..9b8fa62f1 100644 --- a/samples/Compute/create_new_keypair.php +++ b/samples/Compute/create_new_keypair.php @@ -49,4 +49,4 @@ $sshPrivateKeyFilename = 'new_keypair_private'; $privateKey = $keypair->getPrivateKey(); file_put_contents($sshPrivateKeyFilename, $privateKey); -chmod($sshPrivateKeyFilename, 0600); \ No newline at end of file +chmod($sshPrivateKeyFilename, 0600); diff --git a/samples/Compute/create_server.php b/samples/Compute/create_server.php index 9dd118b2a..682a2478f 100644 --- a/samples/Compute/create_server.php +++ b/samples/Compute/create_server.php @@ -81,4 +81,4 @@ $body = json_decode($response->getBody(true)); // THIS IS YOUR ROOT PASSWORD - DO NOT LOSE! -$password = $body->server->adminPass; \ No newline at end of file +$password = $body->server->adminPass; diff --git a/samples/Compute/create_server_from_bootable_volume.php b/samples/Compute/create_server_from_bootable_volume.php index 65d822086..73ab9251e 100644 --- a/samples/Compute/create_server_from_bootable_volume.php +++ b/samples/Compute/create_server_from_bootable_volume.php @@ -66,4 +66,4 @@ // No! Something failed. Let's find out: echo $e->getRequest() . PHP_EOL . PHP_EOL; echo $e->getResponse(); -} \ No newline at end of file +} diff --git a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php index 923b4339d..f8fd85bbf 100644 --- a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php +++ b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php @@ -68,4 +68,4 @@ // No! Something failed. Let's find out: echo $e->getRequest() . PHP_EOL . PHP_EOL; echo $e->getResponse(); -} \ No newline at end of file +} diff --git a/samples/Compute/create_server_with_keypair.php b/samples/Compute/create_server_with_keypair.php index a6a4dc285..65898e657 100644 --- a/samples/Compute/create_server_with_keypair.php +++ b/samples/Compute/create_server_with_keypair.php @@ -80,4 +80,4 @@ $badResponse->getBody(true), implode(', ', $response->getHeaderLines()) ); -} \ No newline at end of file +} diff --git a/samples/Compute/create_volume.php b/samples/Compute/create_volume.php index ffd334928..d4e827388 100644 --- a/samples/Compute/create_volume.php +++ b/samples/Compute/create_volume.php @@ -60,4 +60,4 @@ 'volume_type' => $volumeType, 'display_name' => 'My Volume', 'display_description' => 'Used for large object storage' -)); \ No newline at end of file +)); diff --git a/samples/Compute/delete_server.php b/samples/Compute/delete_server.php index 6e49f8971..ba78d4524 100644 --- a/samples/Compute/delete_server.php +++ b/samples/Compute/delete_server.php @@ -46,4 +46,4 @@ $server = $service->server($serverId); // 4. Delete it -$server->delete(); \ No newline at end of file +$server->delete(); diff --git a/samples/Compute/detach_volume.php b/samples/Compute/detach_volume.php index c69a5e4e7..0edb057f3 100644 --- a/samples/Compute/detach_volume.php +++ b/samples/Compute/detach_volume.php @@ -54,4 +54,4 @@ $myServer = $computeService->server($serverId); // 5. Detach -$myServer->detachVolume($myVolume); \ No newline at end of file +$myServer->detachVolume($myVolume); diff --git a/samples/Compute/update_server.php b/samples/Compute/update_server.php index 9c7785d94..558ec979b 100644 --- a/samples/Compute/update_server.php +++ b/samples/Compute/update_server.php @@ -49,4 +49,4 @@ // `accessIPv4' and `accessIPv6' attributes. $server->update(array( 'name' => 'new_awesome_name' -)); \ No newline at end of file +)); diff --git a/samples/Compute/upload_existing_keypair.php b/samples/Compute/upload_existing_keypair.php index 36d3385d1..d2886680d 100644 --- a/samples/Compute/upload_existing_keypair.php +++ b/samples/Compute/upload_existing_keypair.php @@ -46,4 +46,4 @@ $keypair->create(array( 'name' => 'new_public_key', 'publicKey' => $payload -)); \ No newline at end of file +)); diff --git a/samples/Database/delete-configuration.php b/samples/Database/delete-configuration.php index 43186a580..4fc2d7460 100644 --- a/samples/Database/delete-configuration.php +++ b/samples/Database/delete-configuration.php @@ -41,4 +41,4 @@ $configuration = $databaseService->configuration(getenv('OS_DB_CONFIGURATION_ID')); // 4. Delete it. -$configuration->delete(); \ No newline at end of file +$configuration->delete(); diff --git a/samples/ObjectStore/delete-container-recursive.php b/samples/ObjectStore/delete-container-recursive.php index 54adac208..fa0724583 100644 --- a/samples/ObjectStore/delete-container-recursive.php +++ b/samples/ObjectStore/delete-container-recursive.php @@ -41,4 +41,4 @@ $container = $objectStoreService->getContainer('logos'); // 4. Delete container. -$container->delete(true); \ No newline at end of file +$container->delete(true); diff --git a/samples/ObjectStore/delete-container.php b/samples/ObjectStore/delete-container.php index ec8d68287..2f120d44d 100644 --- a/samples/ObjectStore/delete-container.php +++ b/samples/ObjectStore/delete-container.php @@ -42,4 +42,4 @@ $container = $objectStoreService->getContainer('logos'); // 4. Delete container. -$container->delete(); \ No newline at end of file +$container->delete(); diff --git a/samples/ObjectStore/delete-object.php b/samples/ObjectStore/delete-object.php index bd5f92ef8..7d3383d3e 100644 --- a/samples/ObjectStore/delete-object.php +++ b/samples/ObjectStore/delete-object.php @@ -47,4 +47,4 @@ $object = $container->getObject($objectName); // 5. Delete object. -$object->delete(); \ No newline at end of file +$object->delete(); diff --git a/samples/ObjectStore/get-account-bytes-used.php b/samples/ObjectStore/get-account-bytes-used.php index cefccf89c..62a728b9e 100644 --- a/samples/ObjectStore/get-account-bytes-used.php +++ b/samples/ObjectStore/get-account-bytes-used.php @@ -40,4 +40,4 @@ // 4. Get the space (in bytes) used by the account. $accountSizeInBytes = $account->getBytesUsed(); -printf("Space (in bytes) used by this account: %d\n", $accountSizeInBytes); \ No newline at end of file +printf("Space (in bytes) used by this account: %d\n", $accountSizeInBytes); diff --git a/samples/ObjectStore/get-account-container-count.php b/samples/ObjectStore/get-account-container-count.php index dfc3a574b..3f43adf1c 100644 --- a/samples/ObjectStore/get-account-container-count.php +++ b/samples/ObjectStore/get-account-container-count.php @@ -40,4 +40,4 @@ // 4. Get the number of containers in the account. $accountContainerCount = $account->getContainerCount(); -printf("# of containers in account: %d\n", $accountContainerCount); \ No newline at end of file +printf("# of containers in account: %d\n", $accountContainerCount); diff --git a/samples/ObjectStore/get-account-object-count.php b/samples/ObjectStore/get-account-object-count.php index 4092006cc..a8529c5bd 100644 --- a/samples/ObjectStore/get-account-object-count.php +++ b/samples/ObjectStore/get-account-object-count.php @@ -40,4 +40,4 @@ // 4. Get the number of objects in the account. $accountObjectCount = $account->getObjectCount(); -printf("# of objects in account: %d\n", $accountObjectCount); \ No newline at end of file +printf("# of objects in account: %d\n", $accountObjectCount); diff --git a/samples/ObjectStore/get-account-temp-url-secret.php b/samples/ObjectStore/get-account-temp-url-secret.php index 1e69608a5..027222c45 100644 --- a/samples/ObjectStore/get-account-temp-url-secret.php +++ b/samples/ObjectStore/get-account-temp-url-secret.php @@ -41,4 +41,4 @@ // 4. Get the temporary URL secret. $tempUrlSecret = $account->getTempUrlSecret(); -printf("Account temporary URL secret: %s\n", $tempUrlSecret); \ No newline at end of file +printf("Account temporary URL secret: %s\n", $tempUrlSecret); diff --git a/samples/ObjectStore/get-container-bytes-quota.php b/samples/ObjectStore/get-container-bytes-quota.php index 960f2f43d..dd9cc1c45 100644 --- a/samples/ObjectStore/get-container-bytes-quota.php +++ b/samples/ObjectStore/get-container-bytes-quota.php @@ -42,4 +42,4 @@ // 4. Get the quota for total size of objects in container. $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); -printf("Total size of objects allowed in container: %d\n", $maximumTotalSizeOfObjectsAllowedInContainer); \ No newline at end of file +printf("Total size of objects allowed in container: %d\n", $maximumTotalSizeOfObjectsAllowedInContainer); diff --git a/samples/ObjectStore/get-container-bytes-used.php b/samples/ObjectStore/get-container-bytes-used.php index 5e6345e53..c82b92dbd 100644 --- a/samples/ObjectStore/get-container-bytes-used.php +++ b/samples/ObjectStore/get-container-bytes-used.php @@ -42,4 +42,4 @@ // 4. Get the space (in bytes) used by this container. $containerSizeInBytes = $container->getBytesUsed(); -printf("Space (in bytes) used by this container: %d\n", $containerSizeInBytes); \ No newline at end of file +printf("Space (in bytes) used by this container: %d\n", $containerSizeInBytes); diff --git a/samples/ObjectStore/get-container-count-quota.php b/samples/ObjectStore/get-container-count-quota.php index 8f93b954c..8485f0639 100644 --- a/samples/ObjectStore/get-container-count-quota.php +++ b/samples/ObjectStore/get-container-count-quota.php @@ -42,4 +42,4 @@ // 4. Get the quota for number of objects in container. $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); -printf("Number of objects allowed in container: %d\n", $maximumNumberOfObjectsAllowedInContainer); \ No newline at end of file +printf("Number of objects allowed in container: %d\n", $maximumNumberOfObjectsAllowedInContainer); diff --git a/samples/ObjectStore/get-container-object-count.php b/samples/ObjectStore/get-container-object-count.php index b4ce66039..46d797c7c 100644 --- a/samples/ObjectStore/get-container-object-count.php +++ b/samples/ObjectStore/get-container-object-count.php @@ -42,4 +42,4 @@ // 4. Get the number of objects in this container. $containerObjectCount = $container->getObjectCount(); -printf("# of objects in container: %d\n", $containerObjectCount); \ No newline at end of file +printf("# of objects in container: %d\n", $containerObjectCount); diff --git a/samples/ObjectStore/get-object.php b/samples/ObjectStore/get-object.php index 23004dbf2..666f7ad16 100644 --- a/samples/ObjectStore/get-object.php +++ b/samples/ObjectStore/get-object.php @@ -59,4 +59,4 @@ $localFilename = tempnam("/tmp", 'php-opencloud-'); file_put_contents($localFilename, $stream); -printf("Your object has been written to %s\n", $localFilename); \ No newline at end of file +printf("Your object has been written to %s\n", $localFilename); diff --git a/samples/ObjectStore/list-containers.php b/samples/ObjectStore/list-containers.php index 0c31e16b1..9c6f4af1e 100644 --- a/samples/ObjectStore/list-containers.php +++ b/samples/ObjectStore/list-containers.php @@ -40,4 +40,4 @@ foreach ($containers as $container) { /** @var $container OpenCloud\ObjectStore\Resource\Container **/ printf("Container name: %s\n", $container->getName()); -} \ No newline at end of file +} diff --git a/samples/ObjectStore/quickstart.php b/samples/ObjectStore/quickstart.php index ef5147bca..0536e9c90 100644 --- a/samples/ObjectStore/quickstart.php +++ b/samples/ObjectStore/quickstart.php @@ -43,4 +43,4 @@ $remoteFileName = 'php-elephant.jpg'; $fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); \ No newline at end of file +$container->uploadObject($remoteFileName, $fileData); diff --git a/samples/ObjectStore/set-container-count-quota.php b/samples/ObjectStore/set-container-count-quota.php index cb07d316b..1867eb7d2 100644 --- a/samples/ObjectStore/set-container-count-quota.php +++ b/samples/ObjectStore/set-container-count-quota.php @@ -42,4 +42,4 @@ // 4. Set quota for number of objects in container. $maximumNumberOfObjectsAllowedInContainer = 25; -$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); \ No newline at end of file +$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); diff --git a/samples/ObjectStore/set-container-metadata.php b/samples/ObjectStore/set-container-metadata.php index c91baa8a8..ac6e6669e 100644 --- a/samples/ObjectStore/set-container-metadata.php +++ b/samples/ObjectStore/set-container-metadata.php @@ -43,4 +43,4 @@ // 4. Set container metadata. $container->saveMetadata(array( 'author' => 'John Doe' -)); \ No newline at end of file +)); diff --git a/samples/ObjectStore/upload-large-object.php b/samples/ObjectStore/upload-large-object.php index a236f3e29..69e24d66c 100644 --- a/samples/ObjectStore/upload-large-object.php +++ b/samples/ObjectStore/upload-large-object.php @@ -47,4 +47,4 @@ 'path' => getenv('LARGE_FILE_PATH') ); $objectTransfer = $container->setupObjectTransfer($options); -$objectTransfer->upload(); \ No newline at end of file +$objectTransfer->upload(); diff --git a/samples/ObjectStore/upload-multiple-objects.php b/samples/ObjectStore/upload-multiple-objects.php index ff44ea287..08787bc16 100644 --- a/samples/ObjectStore/upload-multiple-objects.php +++ b/samples/ObjectStore/upload-multiple-objects.php @@ -52,4 +52,4 @@ ) ); -$container->uploadObjects($objects); \ No newline at end of file +$container->uploadObjects($objects); diff --git a/samples/Volume/list-snapshots.php b/samples/Volume/list-snapshots.php index fe4d1a7d4..9e2db32fc 100644 --- a/samples/Volume/list-snapshots.php +++ b/samples/Volume/list-snapshots.php @@ -20,7 +20,7 @@ // * Prior to running this script, you must setup the following environment variables: // * RAX_USERNAME: Your Rackspace Cloud Account Username, and // * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * RAX_VOLUME_ID: ID of the volume whose snapshots you want to list. Run +// * RAX_VOLUME_ID: ID of the volume whose snapshots you want to list. Run // create-volume.php if you need to create one first. // diff --git a/samples/Volume/list-volumes.php b/samples/Volume/list-volumes.php index 43c88d8d6..5a18b6ca5 100644 --- a/samples/Volume/list-volumes.php +++ b/samples/Volume/list-volumes.php @@ -40,4 +40,4 @@ foreach ($volumes as $volume) { /** @var $volume OpenCloud\Volume\Resource\Volume **/ echo "ID: " . $volume->id() . " | " . "Name: " . $volume->name() . PHP_EOL; -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Smoke/Enum.php b/tests/OpenCloud/Smoke/Enum.php index d464051f0..a25b1386c 100644 --- a/tests/OpenCloud/Smoke/Enum.php +++ b/tests/OpenCloud/Smoke/Enum.php @@ -28,12 +28,11 @@ /** * Description of Enum - * - * @link + * + * @link */ class Enum { - const USER_AGENT = 'PHP OpenCloud SMOKETEST'; const CREDS_FILENAME = '.smoketestCredentials'; @@ -55,5 +54,4 @@ class Enum const DISPLAY_ITER_LIMIT = 10; const DIVIDER = '-------------'; - } diff --git a/tests/OpenCloud/Smoke/Logger.php b/tests/OpenCloud/Smoke/Logger.php index b43053ce8..f1a884b38 100644 --- a/tests/OpenCloud/Smoke/Logger.php +++ b/tests/OpenCloud/Smoke/Logger.php @@ -22,11 +22,9 @@ class Logger extends AbstractLogger { - public function log($level, $message, array $context = array()) { $logger = new CommonLogger(); return $logger->log($level, $message, $context); } - } diff --git a/tests/OpenCloud/Smoke/Runner.php b/tests/OpenCloud/Smoke/Runner.php index a387c6c99..073e1c376 100644 --- a/tests/OpenCloud/Smoke/Runner.php +++ b/tests/OpenCloud/Smoke/Runner.php @@ -30,8 +30,8 @@ class Runner { /** * These are the individual tests, or units, that can execute. - * - * @var array + * + * @var array */ private $units = array( 'Autoscale', @@ -90,7 +90,7 @@ private function formatDuration($duration) } private function handleArguments() - { + { $options = getopt('D::H::E::I::A', array( 'debug::', 'help::', @@ -174,7 +174,6 @@ public function executeTemplate() $client = $this->createClient(); foreach ($this->included as $unit) { - $class = __NAMESPACE__ . '\\Unit\\' . $unit; if (!class_exists($class)) { @@ -197,10 +196,10 @@ public function executeTemplate() private function createClient() { - Utils::log('Authenticate'); + Utils::log('Authenticate'); $secret = array( - 'username' => Utils::getEnvVar(Enum::ENV_USERNAME), + 'username' => Utils::getEnvVar(Enum::ENV_USERNAME), 'apiKey' => Utils::getEnvVar(Enum::ENV_API_KEY) ); @@ -223,7 +222,6 @@ private function createClient() return $client; } - } require __DIR__ . '/../../bootstrap.php'; diff --git a/tests/OpenCloud/Smoke/SmokeException.php b/tests/OpenCloud/Smoke/SmokeException.php index 107af2318..ed7953d90 100644 --- a/tests/OpenCloud/Smoke/SmokeException.php +++ b/tests/OpenCloud/Smoke/SmokeException.php @@ -30,8 +30,8 @@ /** * Description of SmokeException - * - * @link + * + * @link */ class SmokeException extends Exception { diff --git a/tests/OpenCloud/Smoke/Step.php b/tests/OpenCloud/Smoke/Step.php index 8744c19b8..9009e5026 100644 --- a/tests/OpenCloud/Smoke/Step.php +++ b/tests/OpenCloud/Smoke/Step.php @@ -28,8 +28,8 @@ /** * Description of Step - * - * @link + * + * @link */ class Step { @@ -43,7 +43,7 @@ class Step public $message; /** - * @var string Either default, `spacer` or `dotter`. + * @var string Either default, `spacer` or `dotter`. */ public $outputType; @@ -58,7 +58,7 @@ class Step public $depth = 0; /** - * @var int The count for this step. + * @var int The count for this step. */ public $count = 1; @@ -136,7 +136,7 @@ public function getOutput() switch ($this->getOutputType()) { default: $leadingLine = true; - $outputString = sprintf('%d. %s', $this->getCount(), $this->getMessage()); + $outputString = sprintf('%d. %s', $this->getCount(), $this->getMessage()); break; case self::TYPE_DOTTER: $outputString = sprintf('... %s', $this->getMessage()); @@ -146,9 +146,9 @@ public function getOutput() break; } - return ((isset($leadingLine)) ? PHP_EOL : '') + return ((isset($leadingLine)) ? PHP_EOL : '') . $this->computeSpacePrefix() . $outputString; - } + } private function computeSpacePrefix() { @@ -198,5 +198,4 @@ public function createSubStep($string, $outputType = null) ->setCount($this->getCount() + 1) ->output(); } - } diff --git a/tests/OpenCloud/Smoke/Unit/AbstractUnit.php b/tests/OpenCloud/Smoke/Unit/AbstractUnit.php index aa2ac945f..bdf9d2e58 100644 --- a/tests/OpenCloud/Smoke/Unit/AbstractUnit.php +++ b/tests/OpenCloud/Smoke/Unit/AbstractUnit.php @@ -25,29 +25,29 @@ /** * Description of AbstractUnit - * - * @link + * + * @link */ abstract class AbstractUnit { /** * The credentials cache filename. - * - * @var string + * + * @var string */ private $credentialsCacheFile; /** * The connection object which everything routes through. - * + * * @var OpenCloud\OpenStack */ protected $connection; /** * The particular service that each unit uses. - * - * @var OpenCloud\Common\Service + * + * @var OpenCloud\Common\Service */ protected $service; @@ -57,9 +57,9 @@ abstract class AbstractUnit protected $includedUnits; /** - * Factory method for instantiating the unit object, and executing its + * Factory method for instantiating the unit object, and executing its * main algorithm. - * + * * @return UnitInterface */ public static function factory(OpenStack $connection, array $includedUnits) @@ -118,9 +118,10 @@ public function getIncludedUnits() public function getWaiterCallback() { - return function($object) { + return function ($object) { if (!empty($object->error)) { - var_dump($object->error); die; + var_dump($object->error); + die; } else { $this->stepInfoDotter( "Waiting on %s/%-12s %4s%%", diff --git a/tests/OpenCloud/Smoke/Unit/Autoscale.php b/tests/OpenCloud/Smoke/Unit/Autoscale.php index f470437aa..10e44e7ad 100644 --- a/tests/OpenCloud/Smoke/Unit/Autoscale.php +++ b/tests/OpenCloud/Smoke/Unit/Autoscale.php @@ -19,13 +19,11 @@ class Autoscale extends AbstractUnit implements UnitInterface { - /** * {@inheritDoc} */ public function setupService() { - } /** @@ -33,7 +31,6 @@ public function setupService() */ public function main() { - } /** @@ -41,7 +38,5 @@ public function main() */ public function teardown() { - } - } diff --git a/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php b/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php index 2bf609d70..9f08cc6e0 100644 --- a/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php +++ b/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php @@ -21,8 +21,8 @@ /** * Description of CloudMonitoring - * - * @link + * + * @link */ class CloudMonitoring extends AbstractUnit implements UnitInterface { diff --git a/tests/OpenCloud/Smoke/Unit/DNS.php b/tests/OpenCloud/Smoke/Unit/DNS.php index 0cb22b219..bfc4b95ec 100644 --- a/tests/OpenCloud/Smoke/Unit/DNS.php +++ b/tests/OpenCloud/Smoke/Unit/DNS.php @@ -28,9 +28,10 @@ public function setupService() public function getWaiterCallback() { - return function($object) { + return function ($object) { if (!empty($object->error)) { - var_dump($object->error); die; + var_dump($object->error); + die; } else { $this->stepInfoDotter("Waiting..."); } @@ -61,8 +62,8 @@ public function main() if ($asyncResponse->Status() == 'ERROR') { $this->stepInfo( 'Error: [%d] %s - %s', - $asyncResponse->error->code, - $asyncResponse->error->message, + $asyncResponse->error->code, + $asyncResponse->error->message, $asyncResponse->error->details ); } @@ -75,8 +76,8 @@ public function main() $record = $domain->record(); $asyncResponse = $record->create(array( - 'type' => 'CNAME', - 'ttl' => 600, + 'type' => 'CNAME', + 'ttl' => 600, 'name' => 'www.'. $domainName, 'data' => 'developer.rackspace.com' )); @@ -84,8 +85,8 @@ public function main() if ($asyncResponse->status() == 'ERROR') { $this->stepInfo( - 'Error: [%d] $s - %s', - $asyncResponse->error->code, + 'Error: [%d] $s - %s', + $asyncResponse->error->code, $asyncResponse->error->message, $asyncResponse->error->details ); @@ -103,7 +104,6 @@ public function main() $domains->setOption('limit.total', Enum::DISPLAY_ITER_LIMIT); foreach ($domains as $domain) { - $this->stepInfo('%s [%s]', $domain->name(), $domain->emailAddress); $step = $this->stepInfo('Domain Records:'); @@ -111,9 +111,9 @@ public function main() foreach ($records as $record) { $step->stepInfo( '- %s %d %s %s', - $record->type, - $record->ttl, - $record->name(), + $record->type, + $record->ttl, + $record->name(), $record->data ); } diff --git a/tests/OpenCloud/Smoke/Unit/Database.php b/tests/OpenCloud/Smoke/Unit/Database.php index 3900660b6..c5f76899e 100644 --- a/tests/OpenCloud/Smoke/Unit/Database.php +++ b/tests/OpenCloud/Smoke/Unit/Database.php @@ -94,7 +94,6 @@ public function teardown() $instances = $this->getService()->instanceList(); foreach ($instances as $instance) { - // Users $users = $instance->userList(); foreach ($users as $user) { @@ -113,11 +112,11 @@ public function teardown() } } - // Instance + // Instance if ($this->shouldDelete($instance->name)) { $this->stepInfo('Deleting instance: %s', $instance->id); $instance->delete(); - } + } } } } diff --git a/tests/OpenCloud/Smoke/Unit/Identity.php b/tests/OpenCloud/Smoke/Unit/Identity.php index 4f8a44175..97bd0f571 100644 --- a/tests/OpenCloud/Smoke/Unit/Identity.php +++ b/tests/OpenCloud/Smoke/Unit/Identity.php @@ -128,4 +128,4 @@ public function executeTenants() public function teardown() { } -} +} diff --git a/tests/OpenCloud/Smoke/Unit/LoadBalancer.php b/tests/OpenCloud/Smoke/Unit/LoadBalancer.php index 9d408037f..e7a451c05 100644 --- a/tests/OpenCloud/Smoke/Unit/LoadBalancer.php +++ b/tests/OpenCloud/Smoke/Unit/LoadBalancer.php @@ -68,8 +68,8 @@ public function main() $protocolList = $this->getService()->protocolList(); foreach ($protocolList as $protocol) { $this->stepInfo( - '%s %4d', - substr($protocol->name() . '..................', 0, 20), + '%s %4d', + substr($protocol->name() . '..................', 0, 20), $protocol->port ); } @@ -87,15 +87,13 @@ public function main() $loadBalancers->setOption('limit.total', Enum::DISPLAY_ITER_LIMIT); if ($loadBalancers->count()) { - $i = 1; $total = $loadBalancers->count() > 10 ? 10 : $loadBalancers->count(); foreach ($loadBalancers as $loadBalancer) { - $step = $this->stepInfo('Load balancer (%d/%d)', $i, $total); $step->stepInfo( - 'ID [%s], Name [%s], Status [%s]', + 'ID [%s], Name [%s], Status [%s]', $loadBalancer->id, $loadBalancer->name(), $loadBalancer->status() @@ -109,10 +107,10 @@ public function main() } else { foreach ($nodeList as $node) { $step1->stepInfo('Node: [%s] %s:%d %s/%s', - $node->id(), - $node->address, + $node->id(), + $node->address, $node->port, - $node->condition, + $node->condition, $node->status ); } @@ -126,7 +124,7 @@ public function main() } else { foreach ($nodeEvents as $event) { $step2->stepInfo('Event: %s (%s)', - $event->detailedMessage, + $event->detailedMessage, $event->author ); } @@ -145,8 +143,8 @@ public function main() $metadataList = $loadBalancer->metadataList(); foreach ($metadataList as $metadataItem) { $step3->stepInfo('[Metadata #%s] %s=%s', - $metadataItem->Id(), - $metadataItem->key, + $metadataItem->Id(), + $metadataItem->key, $metadataItem->value ); } @@ -164,7 +162,7 @@ public function main() $this->step('Billable Load Balancers from %s to %s', $start, $end); $list = $this->getService()->billableLoadBalancerList(array( - 'startTime' => $start, + 'startTime' => $start, 'endTime' => $end )); diff --git a/tests/OpenCloud/Smoke/Unit/ObjectStore.php b/tests/OpenCloud/Smoke/Unit/ObjectStore.php index 5ab4a3b0c..74f9ec346 100644 --- a/tests/OpenCloud/Smoke/Unit/ObjectStore.php +++ b/tests/OpenCloud/Smoke/Unit/ObjectStore.php @@ -26,7 +26,6 @@ class ObjectStore extends AbstractUnit implements UnitInterface { - const OBJECT_NAME = 'TestObject'; const UPLOAD_COUNT = 50; const MASSIVE_FILE_PATH = '/tmp/massive.txt'; @@ -132,7 +131,6 @@ public function main() $containers = $this->getService()->listContainers(); foreach ($containers as $container) { - $step = $this->stepInfo('Container: %s', $container->getName()); // List this container's objects @@ -140,7 +138,7 @@ public function main() foreach ($containers as $container) { $step->stepInfo('Object: %s', $object->getName()); } - } + } } public function teardown() @@ -156,7 +154,8 @@ public function teardown() $this->stepInfo('Disable Container CDN'); try { $container->disableCDN(); - } catch (CdnNotAvailableError $e) {} + } catch (CdnNotAvailableError $e) { + } $step = $this->stepInfo('Delete objects'); $objects = $container->objectList(); diff --git a/tests/OpenCloud/Smoke/Unit/Orchestration.php b/tests/OpenCloud/Smoke/Unit/Orchestration.php index 57ee8223b..0b9baa340 100644 --- a/tests/OpenCloud/Smoke/Unit/Orchestration.php +++ b/tests/OpenCloud/Smoke/Unit/Orchestration.php @@ -21,12 +21,11 @@ /** * Description of Orchestration - * - * @link + * + * @link */ class Orchestration extends AbstractUnit implements UnitInterface { - protected $cleanupStackIds = array(); public function setupService() @@ -36,7 +35,6 @@ public function setupService() public function main() { - $this->step('Validate template from a file'); $this->getService()->validateTemplate(array( 'template' => file_get_contents($this->getResourceDir() . '/lamp.yaml') @@ -157,7 +155,7 @@ public function main() $this->step('List resource types'); $resourceTypes = $this->getService()->listResourceTypes(); $this->stepInfo('Resource type'); - $this->stepInfo(str_repeat('-', 40)); + $this->stepInfo(str_repeat('-', 40)); foreach ($resourceTypes as $resourceType) { $this->stepInfo($resourceType->getResourceType()); $lastResourceType = $resourceType->getResourceType(); @@ -176,7 +174,7 @@ public function main() $this->stepInfo('Engine revision: ' . $buildInfo->getEngine()->revision); $this->step('Update stack from template file'); - $stack->waitFor('CREATE_COMPLETE', null, function($s) { + $stack->waitFor('CREATE_COMPLETE', null, function ($s) { $this->stepInfo('Stack is still being created. Waiting...'); }); $stack->update(array( @@ -191,7 +189,7 @@ public function main() $this->step('Abandon stack'); $stack = $this->getService()->getStack('simple-lamp-setup-from-template-url'); - $stack->waitFor('CREATE_COMPLETE', null, function($s) { + $stack->waitFor('CREATE_COMPLETE', null, function ($s) { $this->stepInfo('Stack is still being created. Waiting...'); }); $abandonedStackData = $stack->abandon(); diff --git a/tests/OpenCloud/Smoke/Unit/UnitInterface.php b/tests/OpenCloud/Smoke/Unit/UnitInterface.php index 3e321c67c..214d6c883 100644 --- a/tests/OpenCloud/Smoke/Unit/UnitInterface.php +++ b/tests/OpenCloud/Smoke/Unit/UnitInterface.php @@ -22,7 +22,6 @@ */ interface UnitInterface { - /** * Method for executing the main algorithm of the test. */ @@ -34,9 +33,8 @@ public function main(); public function setupService(); /** - * Allows for the deletion of any persistent resources created during + * Allows for the deletion of any persistent resources created during * execution. */ public function teardown(); - } diff --git a/tests/OpenCloud/Smoke/Unit/Volume.php b/tests/OpenCloud/Smoke/Unit/Volume.php index 020dea9cc..1085f14e9 100644 --- a/tests/OpenCloud/Smoke/Unit/Volume.php +++ b/tests/OpenCloud/Smoke/Unit/Volume.php @@ -21,11 +21,11 @@ /** * Description of Volume - * - * @link + * + * @link */ class Volume extends AbstractUnit implements UnitInterface -{ +{ /** * {@inheritDoc} */ diff --git a/tests/OpenCloud/Smoke/Utils.php b/tests/OpenCloud/Smoke/Utils.php index 2e3394bba..a643d0d86 100644 --- a/tests/OpenCloud/Smoke/Utils.php +++ b/tests/OpenCloud/Smoke/Utils.php @@ -28,15 +28,14 @@ /** * Description of Utils - * - * @link + * + * @link */ class Utils { - /** * Basic logging function. - * + * * @param string $string */ public static function log($string) @@ -53,7 +52,7 @@ public static function convertArgsToString(array $args) } /** - * A logging function similar to sprintf(). Accepts a format string as a + * A logging function similar to sprintf(). Accepts a format string as a * first argument, and an array as a second argument to stock the format. */ public static function logf() @@ -120,5 +119,4 @@ public static function getIdentityEndpoint() return \OpenCloud\Rackspace::US_IDENTITY_ENDPOINT; } } - } diff --git a/tests/OpenCloud/Tests/Autoscale/Resource/GroupTest.php b/tests/OpenCloud/Tests/Autoscale/Resource/GroupTest.php index ddc9e4386..76f898b9c 100644 --- a/tests/OpenCloud/Tests/Autoscale/Resource/GroupTest.php +++ b/tests/OpenCloud/Tests/Autoscale/Resource/GroupTest.php @@ -129,4 +129,4 @@ public function testCreatingScalingPolicies() $this->isResponse($response); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Autoscale/Resource/ScalingPolicyTest.php b/tests/OpenCloud/Tests/Autoscale/Resource/ScalingPolicyTest.php index d9ca24258..d1d16d1e2 100644 --- a/tests/OpenCloud/Tests/Autoscale/Resource/ScalingPolicyTest.php +++ b/tests/OpenCloud/Tests/Autoscale/Resource/ScalingPolicyTest.php @@ -22,7 +22,6 @@ class ScalingPolicyTest extends AutoscaleTestCase { - public function setupObjects() { parent::setupObjects(); diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTest.php index c173c6eab..b38de3db8 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTest.php @@ -21,7 +21,6 @@ class AgentTest extends CloudMonitoringTestCase { - const AGENT_ID = '00-agent.example.com'; const CONNECTION_ID = 'cntl4qsIbA'; diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTokenTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTokenTest.php index c6729aa87..c25636773 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTokenTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/AgentTokenTest.php @@ -21,7 +21,6 @@ class AgentTokenTest extends OpenCloudTestCase { - const TOKEN_ID = 'someId'; public function setupObjects() diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/AlarmTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/AlarmTest.php index 44a71f207..a2a6c72dd 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/AlarmTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/AlarmTest.php @@ -21,7 +21,6 @@ class AlarmTest extends CloudMonitoringTestCase { - const ENTITY_ID = 'en5hw56rAh'; const ALARM_ID = 'alAAAA'; diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/ChangelogTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/ChangelogTest.php index 7d40ec752..c07621e22 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/ChangelogTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/ChangelogTest.php @@ -19,7 +19,6 @@ class ChangelogTest extends CloudMonitoringTestCase { - const NT_ID = 'webhook'; public function setupObjects() diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/CheckTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/CheckTest.php index 812c657d9..f2f9ada0b 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/CheckTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/CheckTest.php @@ -22,7 +22,6 @@ class CheckTest extends CloudMonitoringTestCase { - public function setupObjects() { parent::setupObjects(); diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/EntityTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/EntityTest.php index b9c36b70f..b1ea5e34f 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/EntityTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/EntityTest.php @@ -21,7 +21,6 @@ class EntityTest extends CloudMonitoringTestCase { - public function testResourceClass() { $this->assertInstanceOf('OpenCloud\CloudMonitoring\Resource\Entity', $this->entity); diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/MetricTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/MetricTest.php index bc0b7a4f5..06a75a608 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/MetricTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/MetricTest.php @@ -21,7 +21,6 @@ class MetricTest extends CloudMonitoringTestCase { - const CHECK_ID = 'chAAAA'; const METRIC_NAME = 'mzdfw.available'; diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationHistoryTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationHistoryTest.php index cdff81645..f5bdef5d4 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationHistoryTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationHistoryTest.php @@ -21,7 +21,6 @@ class NotificationHistoryTest extends CloudMonitoringTestCase { - const ENTITY_ID = 'enAAAAA'; const ALARM_ID = 'alAAAA'; const CHECK_ID = 'chAAAA'; diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationPlanTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationPlanTest.php index 0619e6372..c3f39b5d3 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationPlanTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationPlanTest.php @@ -22,7 +22,6 @@ class NotificationPlanTest extends CloudMonitoringTestCase { - const NP_ID = 'npAAAA'; public function setupObjects() diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTest.php index 1d207d986..05701e232 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTest.php @@ -22,7 +22,6 @@ class NotificationTest extends CloudMonitoringTestCase { - const NOTIFICATION_ID = 'ntAAAA'; public function setupObjects() diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTypeTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTypeTest.php index eb5f8bc39..ab1d39c18 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTypeTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/NotificationTypeTest.php @@ -22,7 +22,6 @@ class NotificationTypeTest extends CloudMonitoringTestCase { - const NT_ID = 'webhook'; public function setupObjects() diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/ViewTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/ViewTest.php index 3a5c61624..40eb6abe0 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/ViewTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/ViewTest.php @@ -21,7 +21,6 @@ class ViewTest extends CloudMonitoringTestCase { - public function setupObjects() { $this->service = $this->getClient()->cloudMonitoringService(); diff --git a/tests/OpenCloud/Tests/CloudMonitoring/Resource/ZoneTest.php b/tests/OpenCloud/Tests/CloudMonitoring/Resource/ZoneTest.php index f292a2519..20ef87a53 100644 --- a/tests/OpenCloud/Tests/CloudMonitoring/Resource/ZoneTest.php +++ b/tests/OpenCloud/Tests/CloudMonitoring/Resource/ZoneTest.php @@ -22,7 +22,6 @@ class ZoneTest extends CloudMonitoringTestCase { - public function setupObjects() { $this->service = $this->getClient()->cloudMonitoringService(); diff --git a/tests/OpenCloud/Tests/Common/BaseTest.php b/tests/OpenCloud/Tests/Common/BaseTest.php index dce924db6..c0a1c44ea 100644 --- a/tests/OpenCloud/Tests/Common/BaseTest.php +++ b/tests/OpenCloud/Tests/Common/BaseTest.php @@ -22,7 +22,6 @@ class MyBase extends Base { - public $foo; protected $bar; private $baz; @@ -41,7 +40,6 @@ public function getBar() class BaseTest extends \OpenCloud\Tests\OpenCloudTestCase { - private $my; public function setupObjects() diff --git a/tests/OpenCloud/Tests/Common/Collection/ArrayCollectionTest.php b/tests/OpenCloud/Tests/Common/Collection/ArrayCollectionTest.php index a50722dd1..150bc961b 100644 --- a/tests/OpenCloud/Tests/Common/Collection/ArrayCollectionTest.php +++ b/tests/OpenCloud/Tests/Common/Collection/ArrayCollectionTest.php @@ -21,7 +21,6 @@ class ArrayCollectionTest extends OpenCloudTestCase { - public function test_Basic_Operations() { $iterator = $this->getMockForAbstractClass('OpenCloud\Common\Collection\ArrayCollection'); diff --git a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php index 0cdbdcc40..da06da6d0 100644 --- a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php +++ b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php @@ -22,7 +22,6 @@ class LoggerTest extends PHPUnit_Framework_TestCase { - public function __construct() { $this->logger = new Logger; diff --git a/tests/OpenCloud/Tests/Common/MetadataTest.php b/tests/OpenCloud/Tests/Common/MetadataTest.php index 655933c3e..d759ccd8a 100644 --- a/tests/OpenCloud/Tests/Common/MetadataTest.php +++ b/tests/OpenCloud/Tests/Common/MetadataTest.php @@ -31,7 +31,6 @@ class MetadataTest extends \OpenCloud\Tests\OpenCloudTestCase { - private $metadata; public function __construct() diff --git a/tests/OpenCloud/Tests/Common/PersistentObjectTest.php b/tests/OpenCloud/Tests/Common/PersistentObjectTest.php index 0f41b7d3c..4f5c886d4 100644 --- a/tests/OpenCloud/Tests/Common/PersistentObjectTest.php +++ b/tests/OpenCloud/Tests/Common/PersistentObjectTest.php @@ -93,7 +93,6 @@ class NamelessObject extends NovaResource class PersistentObjectTest extends \OpenCloud\Tests\OpenCloudTestCase { - private $service; private $instance; diff --git a/tests/OpenCloud/Tests/Common/ServiceTest.php b/tests/OpenCloud/Tests/Common/ServiceTest.php index 6ff99ccd1..1e4e25230 100644 --- a/tests/OpenCloud/Tests/Common/ServiceTest.php +++ b/tests/OpenCloud/Tests/Common/ServiceTest.php @@ -32,7 +32,6 @@ class ServiceTest extends \OpenCloud\Tests\OpenCloudTestCase { - private $service; public function setupObjects() diff --git a/tests/OpenCloud/Tests/Compute/Resource/ImageTest.php b/tests/OpenCloud/Tests/Compute/Resource/ImageTest.php index aa4123b6c..c388e8c73 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ImageTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ImageTest.php @@ -22,7 +22,6 @@ class ImageTest extends ComputeTestCase { - public function test_good_image() { $image = new Image($this->service); diff --git a/tests/OpenCloud/Tests/Compute/Resource/KeyPairTest.php b/tests/OpenCloud/Tests/Compute/Resource/KeyPairTest.php index 609fa825e..493f5a9b0 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/KeyPairTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/KeyPairTest.php @@ -30,7 +30,6 @@ class KeyPairTest extends ComputeTestCase { - public function test_Service_Methods() { $this->assertInstanceOf( diff --git a/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php b/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php index 719a8073e..06ef7e0dc 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php @@ -35,7 +35,6 @@ class NetworkTest extends ComputeTestCase { - public function setupObjects() { parent::setupObjects(); diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerMetadataTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerMetadataTest.php index 5a1073112..f0d6a6a23 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerMetadataTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerMetadataTest.php @@ -21,7 +21,6 @@ class ServerMetadataTest extends ComputeTestCase { - private $metadata; public function setupObjects() diff --git a/tests/OpenCloud/Tests/Compute/Resource/VolumeAttachmentTest.php b/tests/OpenCloud/Tests/Compute/Resource/VolumeAttachmentTest.php index 9f71ea71e..396439045 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/VolumeAttachmentTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/VolumeAttachmentTest.php @@ -31,7 +31,6 @@ class VolumeAttachmentTest extends ComputeTestCase { - private $attachment; public function setupObjects() diff --git a/tests/OpenCloud/Tests/Compute/ServiceTest.php b/tests/OpenCloud/Tests/Compute/ServiceTest.php index 538206b27..90537ff22 100644 --- a/tests/OpenCloud/Tests/Compute/ServiceTest.php +++ b/tests/OpenCloud/Tests/Compute/ServiceTest.php @@ -29,7 +29,6 @@ class ServiceTest extends ComputeTestCase { - public function test__construct() { $this->assertInstanceOf( @@ -95,5 +94,5 @@ public function testV1Dot1IsSupported() $computeService = $this->getClient()->computeService(null, 'DFW'); $this->assertStringStartsWith('/v1.1', $computeService->getUrl()->getPath()); - } + } } diff --git a/tests/OpenCloud/Tests/DNS/Resource/DomainTest.php b/tests/OpenCloud/Tests/DNS/Resource/DomainTest.php index c097abe47..a2cba23fb 100644 --- a/tests/OpenCloud/Tests/DNS/Resource/DomainTest.php +++ b/tests/OpenCloud/Tests/DNS/Resource/DomainTest.php @@ -31,7 +31,6 @@ class DomainTest extends DnsTestCase { - public function test__construct() { $this->assertInstanceOf('OpenCloud\DNS\Resource\Domain', $this->domain); diff --git a/tests/OpenCloud/Tests/DNS/Resource/PtrRecordTest.php b/tests/OpenCloud/Tests/DNS/Resource/PtrRecordTest.php index afe42e352..b53335a7d 100644 --- a/tests/OpenCloud/Tests/DNS/Resource/PtrRecordTest.php +++ b/tests/OpenCloud/Tests/DNS/Resource/PtrRecordTest.php @@ -89,4 +89,4 @@ public function test_Load_Balancer_Can_Have_PtrRecords() $this->record->getDeviceParent() ); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/DNS/Resource/RecordTest.php b/tests/OpenCloud/Tests/DNS/Resource/RecordTest.php index 4eb34666b..be73cdda4 100644 --- a/tests/OpenCloud/Tests/DNS/Resource/RecordTest.php +++ b/tests/OpenCloud/Tests/DNS/Resource/RecordTest.php @@ -21,7 +21,6 @@ class RecordTest extends DnsTestCase { - public function test__construct() { $record = $this->domain->record(array( diff --git a/tests/OpenCloud/Tests/DNS/ServiceTest.php b/tests/OpenCloud/Tests/DNS/ServiceTest.php index a8e217bae..73da7403e 100644 --- a/tests/OpenCloud/Tests/DNS/ServiceTest.php +++ b/tests/OpenCloud/Tests/DNS/ServiceTest.php @@ -22,7 +22,6 @@ class ServiceTest extends DnsTestCase { - public function test__construct() { $this->assertInstanceOf('OpenCloud\DNS\Service', $this->service); diff --git a/tests/OpenCloud/Tests/Database/Resource/ConfigurationTest.php b/tests/OpenCloud/Tests/Database/Resource/ConfigurationTest.php index 38c96f5e7..12c0eb5da 100644 --- a/tests/OpenCloud/Tests/Database/Resource/ConfigurationTest.php +++ b/tests/OpenCloud/Tests/Database/Resource/ConfigurationTest.php @@ -21,7 +21,6 @@ class ConfigurationTest extends DatabaseTestCase { - public function test_Class() { $this->assertInstanceOf('OpenCloud\Database\Resource\Configuration', $this->configuration); @@ -51,6 +50,4 @@ public function testInstanceList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->configuration->instanceList()); } - - -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Database/Resource/DatastoreTest.php b/tests/OpenCloud/Tests/Database/Resource/DatastoreTest.php index bf248d120..ade80b778 100644 --- a/tests/OpenCloud/Tests/Database/Resource/DatastoreTest.php +++ b/tests/OpenCloud/Tests/Database/Resource/DatastoreTest.php @@ -21,7 +21,6 @@ class DatastoreTest extends DatabaseTestCase { - public function test_Class() { $this->assertInstanceOf('OpenCloud\Database\Resource\Datastore', $this->datastore); @@ -47,4 +46,4 @@ public function testVersionList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->datastore->versionList()); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Database/Resource/DatastoreVersionTest.php b/tests/OpenCloud/Tests/Database/Resource/DatastoreVersionTest.php index 618be4d53..a5cb6ec57 100644 --- a/tests/OpenCloud/Tests/Database/Resource/DatastoreVersionTest.php +++ b/tests/OpenCloud/Tests/Database/Resource/DatastoreVersionTest.php @@ -21,7 +21,6 @@ class DatastoreVersionTest extends DatabaseTestCase { - public function test_Class() { $this->assertInstanceOf('OpenCloud\Database\Resource\DatastoreVersion', $this->datastoreVersion); @@ -42,4 +41,4 @@ public function testDelete() { $this->datastore->delete(); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php b/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php index 72f2371da..53b0498fb 100644 --- a/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php +++ b/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php @@ -21,7 +21,6 @@ class InstanceTest extends DatabaseTestCase { - public function test_Class() { $this->assertInstanceOf('OpenCloud\Database\Resource\Instance', $this->instance); diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index dcd7a7107..8212fb61a 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -29,7 +29,6 @@ class ServiceTest extends DatabaseTestCase { - public function test__construct() { $this->assertInstanceOf( diff --git a/tests/OpenCloud/Tests/Identity/Resource/RoleTest.php b/tests/OpenCloud/Tests/Identity/Resource/RoleTest.php index d83105289..bb8bf0198 100644 --- a/tests/OpenCloud/Tests/Identity/Resource/RoleTest.php +++ b/tests/OpenCloud/Tests/Identity/Resource/RoleTest.php @@ -31,4 +31,4 @@ public function test_Methods() $role->setDescription('bar'); $this->assertEquals('bar', $role->getDescription()); } -} +} diff --git a/tests/OpenCloud/Tests/Identity/Resource/TokenTest.php b/tests/OpenCloud/Tests/Identity/Resource/TokenTest.php index ea491f7e2..30fe6ce13 100644 --- a/tests/OpenCloud/Tests/Identity/Resource/TokenTest.php +++ b/tests/OpenCloud/Tests/Identity/Resource/TokenTest.php @@ -35,4 +35,4 @@ public function test_Methods() $tenant->setEnabled(false); $this->assertFalse($tenant->isEnabled()); } -} +} diff --git a/tests/OpenCloud/Tests/Identity/Resource/UserTest.php b/tests/OpenCloud/Tests/Identity/Resource/UserTest.php index 925a625d6..d2c594117 100644 --- a/tests/OpenCloud/Tests/Identity/Resource/UserTest.php +++ b/tests/OpenCloud/Tests/Identity/Resource/UserTest.php @@ -21,7 +21,6 @@ class UserTest extends IdentityTestCase { - public function setupObjects() { parent::setupObjects(); diff --git a/tests/OpenCloud/Tests/Image/Resource/ImageTest.php b/tests/OpenCloud/Tests/Image/Resource/ImageTest.php index 0d8255312..5b9fc03b4 100644 --- a/tests/OpenCloud/Tests/Image/Resource/ImageTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/ImageTest.php @@ -139,4 +139,4 @@ public function test_Delete_Tag() $this->assertInstanceOf('Guzzle\Http\Message\Response', $this->image->deleteTag(12345)); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/Resource/JsonPatch/DocumentTest.php b/tests/OpenCloud/Tests/Image/Resource/JsonPatch/DocumentTest.php index cf0084f6b..1b27aa5dc 100644 --- a/tests/OpenCloud/Tests/Image/Resource/JsonPatch/DocumentTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/JsonPatch/DocumentTest.php @@ -50,4 +50,4 @@ public function test_To_String() $this->assertEquals('[{"op": "replace", "path": "/foo", "value": "bar"}]', $document->toString()); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/Resource/JsonPatch/EncoderTest.php b/tests/OpenCloud/Tests/Image/Resource/JsonPatch/EncoderTest.php index 2912d34bd..bc4dea8f8 100644 --- a/tests/OpenCloud/Tests/Image/Resource/JsonPatch/EncoderTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/JsonPatch/EncoderTest.php @@ -60,4 +60,4 @@ public function test_Transform() $this->assertEquals('A', Encoder::transform('A')); $this->assertEquals('!!!!', Encoder::transform('!!!!')); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/Resource/JsonPatch/OperationTest.php b/tests/OpenCloud/Tests/Image/Resource/JsonPatch/OperationTest.php index d31007c6e..ae1915d9e 100644 --- a/tests/OpenCloud/Tests/Image/Resource/JsonPatch/OperationTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/JsonPatch/OperationTest.php @@ -65,4 +65,4 @@ public function test_Validate_Fails() $operation->setType('foo'); $operation->validate(); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/Resource/MemberTest.php b/tests/OpenCloud/Tests/Image/Resource/MemberTest.php index 0937ed74f..d4b106db7 100644 --- a/tests/OpenCloud/Tests/Image/Resource/MemberTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/MemberTest.php @@ -84,4 +84,4 @@ public function test_Delete() $this->assertInstanceOf('Guzzle\Http\Message\Response', $response); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/Resource/Schema/PropertyTest.php b/tests/OpenCloud/Tests/Image/Resource/Schema/PropertyTest.php index c4b11e5b0..7eccbef72 100644 --- a/tests/OpenCloud/Tests/Image/Resource/Schema/PropertyTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/Schema/PropertyTest.php @@ -126,4 +126,4 @@ public function test_Get_Path() $this->assertEquals('/id', $property->getPath()); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/Resource/Schema/SchemaTest.php b/tests/OpenCloud/Tests/Image/Resource/Schema/SchemaTest.php index 66d16b3fe..9d9b70f0a 100644 --- a/tests/OpenCloud/Tests/Image/Resource/Schema/SchemaTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/Schema/SchemaTest.php @@ -73,4 +73,4 @@ public function test_Deciding_Operation_Type() $property2 = Property::factory(array('type' => 'string', 'name' => 'username')); $this->assertEquals(OperationType::ADD, $schema1->decideOperationType($property2)); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Image/ServiceTest.php b/tests/OpenCloud/Tests/Image/ServiceTest.php index 8b3cfa940..e9fac1023 100644 --- a/tests/OpenCloud/Tests/Image/ServiceTest.php +++ b/tests/OpenCloud/Tests/Image/ServiceTest.php @@ -59,4 +59,4 @@ public function test_Member_Schema() { $this->assertInstanceOf('OpenCloud\Image\Resource\Schema\Schema', $this->service->getMemberSchema()); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 247525033..6bd047064 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -29,7 +29,6 @@ class ServiceTest extends \OpenCloud\Tests\OpenCloudTestCase { - private $service; public function setupObjects() diff --git a/tests/OpenCloud/Tests/MockSubscriber.php b/tests/OpenCloud/Tests/MockSubscriber.php index 7cb85acf0..d55d1e438 100644 --- a/tests/OpenCloud/Tests/MockSubscriber.php +++ b/tests/OpenCloud/Tests/MockSubscriber.php @@ -64,4 +64,4 @@ public function onRequestBeforeSend(Event $event) $event->stopPropagation(); } } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php b/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php index 9640777cc..985f7830c 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php +++ b/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php @@ -56,4 +56,4 @@ public function setupObjects() $this->container = $this->service->getContainer('foo'); } -} +} diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 343a3ae4c..8193c267d 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -22,7 +22,6 @@ class DataObjectTest extends ObjectStoreTestCase { - public function test_Pseudo_Dirs() { $this->addMockSubscriber($this->makeResponse('[{"subdir": "foo"}]')); diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/TransferTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/TransferTest.php index 14349ab75..cf08b8aea 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/TransferTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/TransferTest.php @@ -23,7 +23,6 @@ class TransferTest extends ObjectStoreTestCase { - public function test_Consecutive_Transfer() { $options = array('objectName' => 'NEW_OBJECT'); diff --git a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php index 232391bcc..e5a898712 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php @@ -30,7 +30,6 @@ class ServiceTest extends ObjectStoreTestCase { - public function test__construct() { $service = $this->getClient()->objectStoreService('cloudFiles', 'DFW'); diff --git a/tests/OpenCloud/Tests/OpenStackTest.php b/tests/OpenCloud/Tests/OpenStackTest.php index a2e6f9bf3..f38f3777b 100644 --- a/tests/OpenCloud/Tests/OpenStackTest.php +++ b/tests/OpenCloud/Tests/OpenStackTest.php @@ -184,4 +184,4 @@ public function test_Import_Credentials_Numeric_Tenant() $this->assertEquals('{expiration}', $this->client->getExpiration()); $this->assertEquals($randomNumericTenant, $this->client->getTenant()); } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php index 66e2fb9bf..6bca04448 100644 --- a/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php +++ b/tests/OpenCloud/Tests/Orchestration/OrchestrationTestCase.php @@ -45,27 +45,33 @@ public function setupObjects() $this->resource = $this->stack->getResource('MySqlCloudDatabaseServer'); } - protected function assertIsService($object) { + protected function assertIsService($object) + { $this->assertInstanceOf('OpenCloud\Orchestration\Service', $object); } - protected function assertIsBuildInfo($object) { + protected function assertIsBuildInfo($object) + { $this->assertInstanceOf('OpenCloud\Orchestration\Resource\BuildInfo', $object); } - protected function assertIsEvent($object) { + protected function assertIsEvent($object) + { $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Event', $object); } - protected function assertIsResource($object) { + protected function assertIsResource($object) + { $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Resource', $object); } - protected function assertIsResourceType($object) { + protected function assertIsResourceType($object) + { $this->assertInstanceOf('OpenCloud\Orchestration\Resource\ResourceType', $object); } - protected function assertIsStack($object) { + protected function assertIsStack($object) + { $this->assertInstanceOf('OpenCloud\Orchestration\Resource\Stack', $object); } } diff --git a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php index 4d5c1032d..b0f4ef95e 100644 --- a/tests/OpenCloud/Tests/Orchestration/ServiceTest.php +++ b/tests/OpenCloud/Tests/Orchestration/ServiceTest.php @@ -22,7 +22,6 @@ class ServiceTest extends OrchestrationTestCase { - public function test__construct() { $service = $this->getClient()->orchestrationService(null, 'DFW'); diff --git a/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php b/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php index ada7f0b26..5237d48a9 100644 --- a/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php +++ b/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php @@ -31,7 +31,6 @@ class QueueTest extends QueuesTestCase { - public function test_Create() { $this->addMockSubscriber($this->makeResponse(null, 201)); @@ -166,7 +165,6 @@ public function test_Getting_Claim() public function test_List_Messages_Boolean_Casting() { - // Test true (boolean) casts to "true" (string) $messages = $this->queue->listMessages(array('echo' => true)); $options = $messages->getOptions(); @@ -186,6 +184,5 @@ public function test_List_Messages_Boolean_Casting() $messages = $this->queue->listMessages(array('echo' => 'false')); $options = $messages->getOptions(); $this->assertEquals('false', $options['baseUrl']->getQuery()->get('echo')); - - } + } } diff --git a/tests/OpenCloud/Tests/Queues/ServiceTest.php b/tests/OpenCloud/Tests/Queues/ServiceTest.php index b7f5e3e2c..7864e6770 100644 --- a/tests/OpenCloud/Tests/Queues/ServiceTest.php +++ b/tests/OpenCloud/Tests/Queues/ServiceTest.php @@ -19,7 +19,6 @@ class ServiceTest extends QueuesTestCase { - public function test_ClientId() { $rand = sha1(rand(1, 9999)); diff --git a/tests/OpenCloud/Tests/RackspaceTest.php b/tests/OpenCloud/Tests/RackspaceTest.php index b24f082f6..790d54645 100644 --- a/tests/OpenCloud/Tests/RackspaceTest.php +++ b/tests/OpenCloud/Tests/RackspaceTest.php @@ -17,7 +17,6 @@ namespace OpenCloud\Tests; - class RackspaceTest extends OpenCloudTestCase { const CREDENTIALS = <<assertNotNull(Version::getGuzzleVersion()); } -} +} diff --git a/tests/OpenCloud/Tests/Volume/Resource/SnapshotTest.php b/tests/OpenCloud/Tests/Volume/Resource/SnapshotTest.php index 8ea4b45b4..8fc0b39ab 100644 --- a/tests/OpenCloud/Tests/Volume/Resource/SnapshotTest.php +++ b/tests/OpenCloud/Tests/Volume/Resource/SnapshotTest.php @@ -21,7 +21,6 @@ class SnapshotTest extends VolumeTestCase { - private $snapshot; public function setupObjects() @@ -65,8 +64,8 @@ public function testResourceName() */ public function testUpdateDisallowedProperties() { - $this->volume->rename(array( + $this->volume->rename(array( 'volume_id' => 'abcd-ef12' - )); + )); } } diff --git a/tests/OpenCloud/Tests/Volume/Resource/VolumeTest.php b/tests/OpenCloud/Tests/Volume/Resource/VolumeTest.php index db182db20..3b2667c67 100644 --- a/tests/OpenCloud/Tests/Volume/Resource/VolumeTest.php +++ b/tests/OpenCloud/Tests/Volume/Resource/VolumeTest.php @@ -66,8 +66,8 @@ public function testResourceName() */ public function testUpdateDisallowedProperties() { - $this->volume->rename(array( + $this->volume->rename(array( 'size' => 314 - )); + )); } } diff --git a/tests/OpenCloud/Tests/Volume/Resource/VolumeTypeTest.php b/tests/OpenCloud/Tests/Volume/Resource/VolumeTypeTest.php index 0b8eae1f0..a8730c8ad 100644 --- a/tests/OpenCloud/Tests/Volume/Resource/VolumeTypeTest.php +++ b/tests/OpenCloud/Tests/Volume/Resource/VolumeTypeTest.php @@ -21,7 +21,6 @@ class VolumeTypeTest extends VolumeTestCase { - private $volumeType; public function setupObjects() diff --git a/tests/OpenCloud/Tests/Volume/ServiceTest.php b/tests/OpenCloud/Tests/Volume/ServiceTest.php index dd6ebb179..d2569cbb2 100644 --- a/tests/OpenCloud/Tests/Volume/ServiceTest.php +++ b/tests/OpenCloud/Tests/Volume/ServiceTest.php @@ -19,7 +19,6 @@ class ServiceTest extends VolumeTestCase { - public function test__construct() { $this->assertInstanceOf( diff --git a/tests/OpenCloud/Tests/Volume/VolumeTestCase.php b/tests/OpenCloud/Tests/Volume/VolumeTestCase.php index 657f46755..e956631f7 100644 --- a/tests/OpenCloud/Tests/Volume/VolumeTestCase.php +++ b/tests/OpenCloud/Tests/Volume/VolumeTestCase.php @@ -33,4 +33,4 @@ public function setupObjects() $this->addMockSubscriber($this->getTestFilePath('Volume')); $this->volume = $this->service->volume('foo'); } -} +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index eca206a70..08888c4d1 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -17,4 +17,4 @@ require __DIR__ . '/../vendor/autoload.php'; -define('ROOT_TEST_DIR', __DIR__ . '/OpenCloud/Tests/'); \ No newline at end of file +define('ROOT_TEST_DIR', __DIR__ . '/OpenCloud/Tests/'); From 715c1d2c354a88372b5490ca93e233689d04c70e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 06:16:48 -0700 Subject: [PATCH 137/835] Checking for PSR-2 coding style compliance. --- .travis.yml | 1 + CONTRIBUTING.md | 10 +++++++++- composer.json | 3 ++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fad14d88c..8afd2f8cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ branches: before_script: - composer install --prefer-source + - vendor/bin/php-cs-fixer fix --level psr2 . after_script: - php vendor/bin/coveralls -v diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a500b696d..381746f2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ efforts. Here are a few general guidelines to follow: will need to write new test cases; if you're updating existing code, you will need to make sure the methods you're updating are still completely covered. -3. Please abide by PSR code styling. +3. Please abide by [PSR-2 code styling](#ensuring-psr-2-coding-style-compliance). 4. Explaining your pull requests is appreciated. Unless you're fixing a minor typographical error, create a description which explains your changes @@ -44,3 +44,11 @@ phpunit * Methods that create a new resource, say `Foo`, should be named `createFoo`. For example, [`createEntity`](/lib/OpenCloud/CloudMonitoring/Service.php#L105). * When validating arguments to a method, please throw `\InvalidArgumentException` when an invalid argument is found. For example, see [here](/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php#L212-L215). + +## Ensuring PSR-2 coding style compliance + +The code in this library is compliant with the [PSR-2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md). To ensure that any code you contribute is also PSR-2 compliant, please run the following command from the base directory of this project _before_ submitting your contribution: + + $ vendor/bin/php-cs-fixer fix --level psr2 . + +Running this command will _change_ your code to become PSR-2 compliant. You will need to _commit_ these changes and make them part of your pull request. diff --git a/composer.json b/composer.json index 5bf3d0d36..9c478266c 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ }, "require-dev" : { "guzzle/guzzle": "~3.8", - "satooshi/php-coveralls": "0.6.*@dev" + "satooshi/php-coveralls": "0.6.*@dev", + "fabpot/php-cs-fixer": "1.0.*@dev" } } From 02c4263f3ff9ddba97d1a92d982165c5ee7a41ff Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 06:23:51 -0700 Subject: [PATCH 138/835] Adding --dry-run (not that it really matters). --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 39cce1026..09407a4a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ branches: before_script: - composer install --prefer-source - vendor/bin/parallel-lint --exclude vendor . - - vendor/bin/php-cs-fixer fix --level psr2 . + - vendor/bin/php-cs-fixer fix --dry-run --level psr2 . after_script: - php vendor/bin/coveralls -v From e9a5d802e13cc51e07f47ca19c869e349f68f109 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 06:34:33 -0700 Subject: [PATCH 139/835] Running php-cs-fixer with level=psr2. --- tests/OpenCloud/Smoke/Unit/Compute.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Compute.php b/tests/OpenCloud/Smoke/Unit/Compute.php index a288d68e3..78083392a 100644 --- a/tests/OpenCloud/Smoke/Unit/Compute.php +++ b/tests/OpenCloud/Smoke/Unit/Compute.php @@ -24,8 +24,8 @@ /** * Description of Compute - * - * @link + * + * @link */ class Compute extends AbstractUnit implements UnitInterface { @@ -66,7 +66,7 @@ public function main() $network = $this->getService()->network(); try { $network->create(array( - 'label' => $this->prepend(self::NETWORK_NAME), + 'label' => $this->prepend(self::NETWORK_NAME), 'cidr' => '192.168.0.0/24' )); } catch (ClientErrorResponseException $e) { @@ -223,7 +223,6 @@ public function teardown() // Delete servers foreach ($servers as $server) { - $attachments = $server->volumeAttachmentList(); foreach ($attachments as $volumeAttachment) { From 6d82b0b44f102f5517d965795a1a81382ec8b8be Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 24 Oct 2014 12:40:09 -0700 Subject: [PATCH 140/835] Fixing doc to match code. --- docs/userguide/LoadBalancer/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 8efa98ffe..0c15f0b29 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -126,7 +126,7 @@ You can list the nodes attached to a load balancer. An instance of `OpenCloud\Co is returned. ```php -$nodes = $loadBalancer->listNodes(); +$nodes = $loadBalancer->nodeList(); foreach ($nodes as $node) { /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ } From 7cedfe56fed582528d201a387f1cf7a5559f0c82 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 27 Oct 2014 10:45:08 -0700 Subject: [PATCH 141/835] Adding ability to update a CDN-enabled container's TTL. --- docs/userguide/ObjectStore/USERGUIDE.md | 9 ++++ .../ObjectStore/Resource/CDNContainer.php | 13 +++++ samples/ObjectStore/set-cdn-container-ttl.php | 47 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 samples/ObjectStore/set-cdn-container-ttl.php diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index cea15821f..b1f0f1b0d 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -550,6 +550,15 @@ $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-ios-streaming-url.php) ] +### Update CDN Container TTL +You can update the TTL of a CDN-enabled container. + +```php +$cdnContainer = $container->getCdn(); +$cdnContainer->setTtl(); +``` +[ [Get the executable PHP script for this example](/samples/ObjectStore/set-cdn-container-ttl.php) ] + ### Disable CDN Container If you no longer need CDN capabilities for a container, you can disable them. diff --git a/lib/OpenCloud/ObjectStore/Resource/CDNContainer.php b/lib/OpenCloud/ObjectStore/Resource/CDNContainer.php index 44422bf00..80e547349 100644 --- a/lib/OpenCloud/ObjectStore/Resource/CDNContainer.php +++ b/lib/OpenCloud/ObjectStore/Resource/CDNContainer.php @@ -106,4 +106,17 @@ public function isCdnEnabled() { return $this->metadata->getProperty(HeaderConst::ENABLED) == 'True'; } + + /** + * Set the TTL. + * + * @param $ttl The time-to-live in seconds. + * @return \Guzzle\Http\Message\Response + */ + public function setTtl($ttl) + { + $headers = array('X-Ttl' => $ttl); + + return $this->getClient()->post($this->getUrl(), $headers)->send(); + } } diff --git a/samples/ObjectStore/set-cdn-container-ttl.php b/samples/ObjectStore/set-cdn-container-ttl.php new file mode 100644 index 000000000..0ac60674d --- /dev/null +++ b/samples/ObjectStore/set-cdn-container-ttl.php @@ -0,0 +1,47 @@ + getenv('RAX_USERNAME'), + 'apiKey' => getenv('RAX_API_KEY') +)); + +// 2. Obtain an Object Store service object from the client. +$region = 'DFW'; +$objectStoreService = $client->objectStoreService(null, $region); + +// 3. Get CDN-enabled container. +$container = $objectStoreService->getContainer('logos'); +$cdnContainer = $container->getCdn(); + +// 4. Set container metadata. +$cdnContainer->setTtl(4 * 24 * 60 * 60); // 4 days From e47b387d91eaf26834d95b74e00a455820a2af5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Pr=C3=A9vot?= Date: Mon, 27 Oct 2014 19:44:58 -0400 Subject: [PATCH 142/835] Delete empty file --- docs/userguide/DNS/Subdomains.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/userguide/DNS/Subdomains.md diff --git a/docs/userguide/DNS/Subdomains.md b/docs/userguide/DNS/Subdomains.md deleted file mode 100644 index e69de29bb..000000000 From 9e5c2aba2090aa63a559489b590c125360660fca Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 28 Oct 2014 14:04:34 +0100 Subject: [PATCH 143/835] Ensure that different JSON attribute is sent to Rackspace API --- lib/OpenCloud/Identity/Resource/User.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Identity/Resource/User.php b/lib/OpenCloud/Identity/Resource/User.php index 404496652..46613a785 100644 --- a/lib/OpenCloud/Identity/Resource/User.php +++ b/lib/OpenCloud/Identity/Resource/User.php @@ -20,6 +20,7 @@ use OpenCloud\Common\Collection\PaginatedIterator; use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\PersistentObject; +use OpenCloud\Rackspace; /** * User class which encapsulates functionality for a user. @@ -59,7 +60,7 @@ class User extends PersistentObject protected $updateKeys = array('username', 'email', 'enabled', 'RAX-AUTH:defaultRegion', 'RAX-AUTH:domainId', 'id'); protected $aliases = array( - 'name' => 'username', + 'name' => 'username', 'RAX-AUTH:defaultRegion' => 'defaultRegion', 'RAX-AUTH:domainId' => 'domainId', 'OS-KSADM:password' => 'password' @@ -68,6 +69,18 @@ class User extends PersistentObject protected static $url_resource = 'users'; protected static $json_name = 'user'; + public function createJson() + { + $json = parent::createJson(); + + if ($this->getClient() instanceof Rackspace) { + $json->user->username = $json->user->name; + unset($json->user->name); + } + + return $json; + } + /** * @param $region Set the default region */ From 53c25c3f9df1b33deb0fa1ed588b7053d2bffbb9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 28 Oct 2014 14:07:17 +0100 Subject: [PATCH 144/835] Fix indent --- lib/OpenCloud/Identity/Resource/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Identity/Resource/User.php b/lib/OpenCloud/Identity/Resource/User.php index 46613a785..736fbd09f 100644 --- a/lib/OpenCloud/Identity/Resource/User.php +++ b/lib/OpenCloud/Identity/Resource/User.php @@ -60,7 +60,7 @@ class User extends PersistentObject protected $updateKeys = array('username', 'email', 'enabled', 'RAX-AUTH:defaultRegion', 'RAX-AUTH:domainId', 'id'); protected $aliases = array( - 'name' => 'username', + 'name' => 'username', 'RAX-AUTH:defaultRegion' => 'defaultRegion', 'RAX-AUTH:domainId' => 'domainId', 'OS-KSADM:password' => 'password' From 91c0a431645c74a2c990b6cd10b7cebf3b515755 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 30 Oct 2014 05:28:22 -0700 Subject: [PATCH 145/835] Renaming performance1-1 flavor to general1-1. --- docs/userguide/Orchestration/USERGUIDE.md | 4 ++-- .../OpenCloud/Tests/Orchestration/Resource/StackTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index c98ceadc9..a6d8af0f9 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -169,7 +169,7 @@ This operation takes one parameter, an associative array, with the following key | `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters | Yes | - | `simple-lamp-setup` | | `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | | `templateUrl` | URL of the template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters. For example, see the parameters in [this template section](https://github.com/rackspace-orchestration-templates/lamp/blob/master/lamp.yaml#L22) | Associative array | No | `null` | `array('flavor_id' => 'performance1_1')` | +| `parameters` | Arguments to the template, based on the template's parameters. For example, see the parameters in [this template section](https://github.com/rackspace-orchestration-templates/lamp/blob/master/lamp.yaml#L22) | Associative array | No | `null` | `array('flavor_id' => 'general1-1')` | #### Preview a stack from a template file @@ -300,7 +300,7 @@ This operation takes one parameter, an associative array, with the following key | ---- | ----------- | --------- | --------- | ------------- | ------------- | | `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | | `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null`| `array('flavor_id' => 'performance1_1')` | +| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null`| `array('flavor_id' => 'general1-1')` | | `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | #### Update a stack from a template file diff --git a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php index 35c982d50..3c05e7735 100644 --- a/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php +++ b/tests/OpenCloud/Tests/Orchestration/Resource/StackTest.php @@ -41,7 +41,7 @@ public function testCreateJson() 'name' => 'foobar', 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( - 'flavor_id' => 'performance1_1', + 'flavor_id' => 'general1-1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' ), @@ -56,7 +56,7 @@ public function testCreateJson() 'stack_name' => 'foobar', 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( - 'flavor_id' => 'performance1_1', + 'flavor_id' => 'general1-1', 'db_name' => 'drupaldb', 'db_user' => 'drupaldbuser' ), @@ -71,7 +71,7 @@ public function testUpdateJson() $updateParams = array( 'templateUrl' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( - 'flavor_id' => 'performance1_1', + 'flavor_id' => 'general1-1', 'db_name' => 'drupaldb', 'db_user' => 'drupalwebuser' ), @@ -85,7 +85,7 @@ public function testUpdateJson() $expectedObj = (object) array( 'template_url' => 'https://github.com/ycombinator/drupal-multi/template.yml', 'parameters' => array( - 'flavor_id' => 'performance1_1', + 'flavor_id' => 'general1-1', 'db_name' => 'drupaldb', 'db_user' => 'drupalwebuser' ), From cfc126b86400f6853a3eb255d03cb885c7df3569 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 1 Nov 2014 10:38:16 -0700 Subject: [PATCH 146/835] Adding initial version of code samples for networking. --- samples/Networking/create-network.php | 44 +++++++++++++++++++ samples/Networking/create-networks.php | 52 ++++++++++++++++++++++ samples/Networking/create-port.php | 48 +++++++++++++++++++++ samples/Networking/create-ports.php | 56 ++++++++++++++++++++++++ samples/Networking/create-subnet.php | 50 +++++++++++++++++++++ samples/Networking/create-subnets.php | 60 ++++++++++++++++++++++++++ samples/Networking/delete-network.php | 45 +++++++++++++++++++ samples/Networking/delete-port.php | 45 +++++++++++++++++++ samples/Networking/delete-subnet.php | 45 +++++++++++++++++++ samples/Networking/get-network.php | 43 ++++++++++++++++++ samples/Networking/get-port.php | 43 ++++++++++++++++++ samples/Networking/get-subnet.php | 43 ++++++++++++++++++ samples/Networking/list-networks.php | 44 +++++++++++++++++++ samples/Networking/list-ports.php | 44 +++++++++++++++++++ samples/Networking/list-subnets.php | 44 +++++++++++++++++++ samples/Networking/update-network.php | 47 ++++++++++++++++++++ samples/Networking/update-port.php | 47 ++++++++++++++++++++ samples/Networking/update-subnet.php | 47 ++++++++++++++++++++ 18 files changed, 847 insertions(+) create mode 100644 samples/Networking/create-network.php create mode 100644 samples/Networking/create-networks.php create mode 100644 samples/Networking/create-port.php create mode 100644 samples/Networking/create-ports.php create mode 100644 samples/Networking/create-subnet.php create mode 100644 samples/Networking/create-subnets.php create mode 100644 samples/Networking/delete-network.php create mode 100644 samples/Networking/delete-port.php create mode 100644 samples/Networking/delete-subnet.php create mode 100644 samples/Networking/get-network.php create mode 100644 samples/Networking/get-port.php create mode 100644 samples/Networking/get-subnet.php create mode 100644 samples/Networking/list-networks.php create mode 100644 samples/Networking/list-ports.php create mode 100644 samples/Networking/list-subnets.php create mode 100644 samples/Networking/update-network.php create mode 100644 samples/Networking/update-port.php create mode 100644 samples/Networking/update-subnet.php diff --git a/samples/Networking/create-network.php b/samples/Networking/create-network.php new file mode 100644 index 000000000..b5dc0a931 --- /dev/null +++ b/samples/Networking/create-network.php @@ -0,0 +1,44 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Create a network. +$network = $networkingService->createNetwork(array( + 'name' => 'My virtual private network' +)); +/** @var $network OpenCloud\Networking\Resource\Network **/ diff --git a/samples/Networking/create-networks.php b/samples/Networking/create-networks.php new file mode 100644 index 000000000..f452db2ee --- /dev/null +++ b/samples/Networking/create-networks.php @@ -0,0 +1,52 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Create multiple networks. +$networks = $networkingService->createNetworks(array( + array( + 'name' => 'My virtual private network #1' + ), + array( + 'name' => 'My virtual private network #2' + ) +)); + +foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ +} diff --git a/samples/Networking/create-port.php b/samples/Networking/create-port.php new file mode 100644 index 000000000..97ee8bc2c --- /dev/null +++ b/samples/Networking/create-port.php @@ -0,0 +1,48 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); + +// 4. Create a port. +$port = $networkingService->createPort(array( + 'name' => 'My port' +)); +/** @var $port OpenCloud\Networking\Resource\Port **/ diff --git a/samples/Networking/create-ports.php b/samples/Networking/create-ports.php new file mode 100644 index 000000000..4f6165830 --- /dev/null +++ b/samples/Networking/create-ports.php @@ -0,0 +1,56 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); + +// 4. Create multiple ports. +$ports = $network->createPorts(array( + array( + 'name' => 'My port #1' + ), + array( + 'name' => 'My port #2' + ) +)); + +foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ +} diff --git a/samples/Networking/create-subnet.php b/samples/Networking/create-subnet.php new file mode 100644 index 000000000..ac8fba9b3 --- /dev/null +++ b/samples/Networking/create-subnet.php @@ -0,0 +1,50 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); + +// 4. Create a subnet. +$subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'ip_version' => 4, + 'cidr' => '192.168.199.0/24' +)); +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/create-subnets.php b/samples/Networking/create-subnets.php new file mode 100644 index 000000000..eeb5c962d --- /dev/null +++ b/samples/Networking/create-subnets.php @@ -0,0 +1,60 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); + +// 4. Create multiple subnets. +$subnets = $network->createSubnets(array( + array( + 'name' => 'My subnet #1', + 'ip_version' => 4, + 'cidr' => '192.168.199.0/24' + ), + array( + 'name' => 'My subnet #2', + 'ip_version' => 4, + 'cidr' => '10.56.4.0/22' + ) +)); + +foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ +} diff --git a/samples/Networking/delete-network.php b/samples/Networking/delete-network.php new file mode 100644 index 000000000..88b7b8a38 --- /dev/null +++ b/samples/Networking/delete-network.php @@ -0,0 +1,45 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); + +// 4. Delete network. +$network->delete(); diff --git a/samples/Networking/delete-port.php b/samples/Networking/delete-port.php new file mode 100644 index 000000000..1f840b02e --- /dev/null +++ b/samples/Networking/delete-port.php @@ -0,0 +1,45 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get port. +$port = $networkingService->getPort(getenv('PORT_ID')); + +// 4. Delete port. +$port->delete(); diff --git a/samples/Networking/delete-subnet.php b/samples/Networking/delete-subnet.php new file mode 100644 index 000000000..0e8dfa5dc --- /dev/null +++ b/samples/Networking/delete-subnet.php @@ -0,0 +1,45 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get subnet. +$subnet = $networkingService->getSubnet(getenv('SUBNET_ID')); + +// 4. Delete subnet. +$subnet->delete(); diff --git a/samples/Networking/get-network.php b/samples/Networking/get-network.php new file mode 100644 index 000000000..f9cb456ce --- /dev/null +++ b/samples/Networking/get-network.php @@ -0,0 +1,43 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); +/** @var $network OpenCloud\Networking\Resource\Network **/ diff --git a/samples/Networking/get-port.php b/samples/Networking/get-port.php new file mode 100644 index 000000000..33749a2be --- /dev/null +++ b/samples/Networking/get-port.php @@ -0,0 +1,43 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get port. +$port = $networkingService->getPort(getenv('PORT_ID')); +/** @var $port OpenCloud\Networking\Resource\Port **/ diff --git a/samples/Networking/get-subnet.php b/samples/Networking/get-subnet.php new file mode 100644 index 000000000..7397d9226 --- /dev/null +++ b/samples/Networking/get-subnet.php @@ -0,0 +1,43 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get subnet. +$subnet = $networkingService->getSubnet(getenv('SUBNET_ID')); +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/list-networks.php b/samples/Networking/list-networks.php new file mode 100644 index 000000000..e1d3abb87 --- /dev/null +++ b/samples/Networking/list-networks.php @@ -0,0 +1,44 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. List networks +$networks = $networkingService->listNetworks(); +foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ +} diff --git a/samples/Networking/list-ports.php b/samples/Networking/list-ports.php new file mode 100644 index 000000000..5ddbe7e4f --- /dev/null +++ b/samples/Networking/list-ports.php @@ -0,0 +1,44 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. List ports +$ports = $networkingService->listPorts(); +foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ +} diff --git a/samples/Networking/list-subnets.php b/samples/Networking/list-subnets.php new file mode 100644 index 000000000..c926d5e71 --- /dev/null +++ b/samples/Networking/list-subnets.php @@ -0,0 +1,44 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. List subnets +$subnets = $networkingService->listSubnets(); +foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ +} diff --git a/samples/Networking/update-network.php b/samples/Networking/update-network.php new file mode 100644 index 000000000..7650be384 --- /dev/null +++ b/samples/Networking/update-network.php @@ -0,0 +1,47 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get network. +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); + +// 4. Update network. +$network->update(array( + 'name' => 'My updated virtual private network' +)); diff --git a/samples/Networking/update-port.php b/samples/Networking/update-port.php new file mode 100644 index 000000000..b4c4db186 --- /dev/null +++ b/samples/Networking/update-port.php @@ -0,0 +1,47 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get port. +$port = $networkingService->getPort(getenv('PORT_ID')); + +// 4. Update port. +$port->update(array( + 'name' => 'My updated port' +)); diff --git a/samples/Networking/update-subnet.php b/samples/Networking/update-subnet.php new file mode 100644 index 000000000..46f8b5d1a --- /dev/null +++ b/samples/Networking/update-subnet.php @@ -0,0 +1,47 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Get subnet. +$subnet = $networkingService->getSubnet(getenv('SUBNET_ID')); + +// 4. Update subnet. +$subnet->update(array( + 'name' => 'My updated subnet' +)); From f23a3f593278b3704bbc269c371cdf382e86d480 Mon Sep 17 00:00:00 2001 From: Shawn Iwinski Date: Sun, 2 Nov 2014 14:24:07 -0500 Subject: [PATCH 147/835] Remove executable bit from documentation files --- docs/userguide/Autoscale/Config.md | 0 docs/userguide/Autoscale/Groups.md | 0 docs/userguide/Autoscale/Policies.md | 0 docs/userguide/Autoscale/Webhooks.md | 0 docs/userguide/CloudMonitoring/Agents.md | 0 docs/userguide/CloudMonitoring/Alarms.md | 0 docs/userguide/CloudMonitoring/Changelogs.md | 0 docs/userguide/CloudMonitoring/Checks.md | 0 docs/userguide/CloudMonitoring/Entities.md | 0 docs/userguide/CloudMonitoring/Metrics.md | 0 docs/userguide/CloudMonitoring/Notifications.md | 0 docs/userguide/CloudMonitoring/Service.md | 0 docs/userguide/CloudMonitoring/Views.md | 0 docs/userguide/CloudMonitoring/Zones.md | 0 docs/userguide/ObjectStore/Access.md | 0 docs/userguide/ObjectStore/Account.md | 0 docs/userguide/ObjectStore/CDN/Container.md | 0 docs/userguide/ObjectStore/CDN/Object.md | 0 docs/userguide/ObjectStore/Storage/Container.md | 0 docs/userguide/ObjectStore/Storage/Object.md | 0 docs/userguide/Queues/Claim.md | 0 docs/userguide/Queues/Message.md | 0 docs/userguide/Queues/Queue.md | 0 docs/userguide/accessip.md | 0 docs/userguide/dbaas.md | 0 docs/userguide/flavors.md | 0 docs/userguide/networks.md | 0 docs/userguide/servers.md | 0 docs/userguide/volumes.md | 0 29 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 docs/userguide/Autoscale/Config.md mode change 100755 => 100644 docs/userguide/Autoscale/Groups.md mode change 100755 => 100644 docs/userguide/Autoscale/Policies.md mode change 100755 => 100644 docs/userguide/Autoscale/Webhooks.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Agents.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Alarms.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Changelogs.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Checks.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Entities.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Metrics.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Notifications.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Service.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Views.md mode change 100755 => 100644 docs/userguide/CloudMonitoring/Zones.md mode change 100755 => 100644 docs/userguide/ObjectStore/Access.md mode change 100755 => 100644 docs/userguide/ObjectStore/Account.md mode change 100755 => 100644 docs/userguide/ObjectStore/CDN/Container.md mode change 100755 => 100644 docs/userguide/ObjectStore/CDN/Object.md mode change 100755 => 100644 docs/userguide/ObjectStore/Storage/Container.md mode change 100755 => 100644 docs/userguide/ObjectStore/Storage/Object.md mode change 100755 => 100644 docs/userguide/Queues/Claim.md mode change 100755 => 100644 docs/userguide/Queues/Message.md mode change 100755 => 100644 docs/userguide/Queues/Queue.md mode change 100755 => 100644 docs/userguide/accessip.md mode change 100755 => 100644 docs/userguide/dbaas.md mode change 100755 => 100644 docs/userguide/flavors.md mode change 100755 => 100644 docs/userguide/networks.md mode change 100755 => 100644 docs/userguide/servers.md mode change 100755 => 100644 docs/userguide/volumes.md diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md old mode 100755 new mode 100644 diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md old mode 100755 new mode 100644 diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md old mode 100755 new mode 100644 diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md old mode 100755 new mode 100644 diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md old mode 100755 new mode 100644 diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md old mode 100755 new mode 100644 diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md old mode 100755 new mode 100644 diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md old mode 100755 new mode 100644 diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md old mode 100755 new mode 100644 diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md old mode 100755 new mode 100644 diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md old mode 100755 new mode 100644 diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md old mode 100755 new mode 100644 diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md old mode 100755 new mode 100644 diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md old mode 100755 new mode 100644 diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md old mode 100755 new mode 100644 diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md old mode 100755 new mode 100644 diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md old mode 100755 new mode 100644 diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md old mode 100755 new mode 100644 diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md old mode 100755 new mode 100644 diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md old mode 100755 new mode 100644 From 20af3c60359abed71f4eeb072f6cb1abe4ee614d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 2 Nov 2014 16:03:32 -0800 Subject: [PATCH 148/835] Trivial whitespace lint fixes. --- tests/OpenCloud/Smoke/Unit/CloudMonitoring.php | 2 +- tests/OpenCloud/Smoke/Unit/ObjectStore.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php b/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php index 9f08cc6e0..b01543f70 100644 --- a/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php +++ b/tests/OpenCloud/Smoke/Unit/CloudMonitoring.php @@ -318,7 +318,7 @@ public function doMonitoringZonesBlock() $zone = $this->getService()->getMonitoringZone($zoneId); // perform traceroute - $trace = $zone->traceroute(array('target' => self::TRACEROUTE_TARGET,'target_resolver' => 'IPv4')); + $trace = $zone->traceroute(array('target' => self::TRACEROUTE_TARGET, 'target_resolver' => 'IPv4')); $this->stepInfo('Traceroute: %s', print_r($trace, true)); } diff --git a/tests/OpenCloud/Smoke/Unit/ObjectStore.php b/tests/OpenCloud/Smoke/Unit/ObjectStore.php index 74f9ec346..dccb74f1b 100644 --- a/tests/OpenCloud/Smoke/Unit/ObjectStore.php +++ b/tests/OpenCloud/Smoke/Unit/ObjectStore.php @@ -59,7 +59,7 @@ public function main() { // Container $this->step('Create Container'); - $container = $this->getService()->createContainer($this->prepend(rand(1,99999))); + $container = $this->getService()->createContainer($this->prepend(rand(1, 99999))); // Upload normal file $this->step('Upload 1 file'); From d77430ad287e2253f445193d596cf008af14c250 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 3 Nov 2014 15:40:40 -0800 Subject: [PATCH 149/835] Create ports and subnets from service object. --- samples/Networking/create-port.php | 8 +++----- samples/Networking/create-ports.php | 18 +++++++++--------- samples/Networking/create-subnet.php | 8 +++----- samples/Networking/create-subnets.php | 18 +++++++++--------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/samples/Networking/create-port.php b/samples/Networking/create-port.php index 97ee8bc2c..753211bde 100644 --- a/samples/Networking/create-port.php +++ b/samples/Networking/create-port.php @@ -38,11 +38,9 @@ $region = getenv('OS_REGION_NAME'); $networkingService = $client->networkingService(null, $region); -// 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); - -// 4. Create a port. +// 3. Create a port. $port = $networkingService->createPort(array( - 'name' => 'My port' + 'name' => 'My port', + 'networkId' => getenv('NETWORK_ID') )); /** @var $port OpenCloud\Networking\Resource\Port **/ diff --git a/samples/Networking/create-ports.php b/samples/Networking/create-ports.php index 4f6165830..00ec0228a 100644 --- a/samples/Networking/create-ports.php +++ b/samples/Networking/create-ports.php @@ -21,8 +21,9 @@ // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network in which to create ports +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, +// * NETWORK_ID_1: ID of network in which to create port 1, and +// * NETWORK_ID_2: ID of network in which to create port 2 // require __DIR__ . '/../../vendor/autoload.php'; @@ -38,16 +39,15 @@ $region = getenv('OS_REGION_NAME'); $networkingService = $client->networkingService(null, $region); -// 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); - -// 4. Create multiple ports. -$ports = $network->createPorts(array( +// 3. Create multiple ports. +$ports = $networkingService->createPorts(array( array( - 'name' => 'My port #1' + 'name' => 'My port #1', + 'networkId' => getenv('NETWORK_ID_1') ), array( - 'name' => 'My port #2' + 'name' => 'My port #2', + 'networkId' => getenv('NETWORK_ID_2') ) )); diff --git a/samples/Networking/create-subnet.php b/samples/Networking/create-subnet.php index ac8fba9b3..2bd94c561 100644 --- a/samples/Networking/create-subnet.php +++ b/samples/Networking/create-subnet.php @@ -38,13 +38,11 @@ $region = getenv('OS_REGION_NAME'); $networkingService = $client->networkingService(null, $region); -// 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); - -// 4. Create a subnet. +// 3. Create a subnet. $subnet = $networkingService->createSubnet(array( 'name' => 'My subnet', - 'ip_version' => 4, + 'networkId' => getenv('NETWORK_ID'), + 'ipVersion' => 4, 'cidr' => '192.168.199.0/24' )); /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/create-subnets.php b/samples/Networking/create-subnets.php index eeb5c962d..0c74167de 100644 --- a/samples/Networking/create-subnets.php +++ b/samples/Networking/create-subnets.php @@ -21,8 +21,9 @@ // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network in which to create subnets +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, +// * NETWORK_ID_1: ID of network in which to create port 1, and +// * NETWORK_ID_2: ID of network in which to create port 2 // require __DIR__ . '/../../vendor/autoload.php'; @@ -38,19 +39,18 @@ $region = getenv('OS_REGION_NAME'); $networkingService = $client->networkingService(null, $region); -// 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); - -// 4. Create multiple subnets. -$subnets = $network->createSubnets(array( +// 3. Create multiple subnets. +$subnets = $networkingService->createSubnets(array( array( 'name' => 'My subnet #1', - 'ip_version' => 4, + 'networkId' => getenv('NETWORK_ID_1'), + 'ipVersion' => 4, 'cidr' => '192.168.199.0/24' ), array( 'name' => 'My subnet #2', - 'ip_version' => 4, + 'networkId' => getenv('NETWORK_ID_2'), + 'ipVersion' => 4, 'cidr' => '10.56.4.0/22' ) )); From d77a8bf7ac794ff1c15c46b00418625922092dfd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 4 Nov 2014 15:24:33 -0800 Subject: [PATCH 150/835] First cut at unit tests and implementation. --- lib/OpenCloud/Networking/Resource/Network.php | 60 ++++++ lib/OpenCloud/Networking/Resource/Port.php | 80 ++++++++ lib/OpenCloud/Networking/Resource/Subnet.php | 80 ++++++++ lib/OpenCloud/Networking/Service.php | 194 ++++++++++++++++++ lib/OpenCloud/OpenStack.php | 19 ++ .../Tests/Networking/NetworkingTestCase.php | 51 +++++ .../Tests/Networking/ServiceTest.php | 99 +++++++++ tests/OpenCloud/Tests/_response/Auth.resp | 16 ++ 8 files changed, 599 insertions(+) create mode 100644 lib/OpenCloud/Networking/Resource/Network.php create mode 100644 lib/OpenCloud/Networking/Resource/Port.php create mode 100644 lib/OpenCloud/Networking/Resource/Subnet.php create mode 100644 lib/OpenCloud/Networking/Service.php create mode 100644 tests/OpenCloud/Tests/Networking/NetworkingTestCase.php create mode 100644 tests/OpenCloud/Tests/Networking/ServiceTest.php diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php new file mode 100644 index 000000000..fd9f621f8 --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -0,0 +1,60 @@ + 'adminStateUp', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'adminStateUp', + 'name', + 'shared', + 'tenantId' + ); + + protected $updateKeys = array( + 'adminStateUp', + 'name', + 'shared', + 'tenantId' + ); +} diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php new file mode 100644 index 000000000..aaf14fd87 --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -0,0 +1,80 @@ + 'adminStateUp', + 'network_id' => 'networkId', + 'device_id' => 'deviceId', + 'device_owner' => 'deviceOwner', + 'fixed_ips' => 'fixedIps', + 'mac_address' => 'macAddress', + 'security_groups' => 'securityGroups', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'name', + 'enableDhcp', + 'networkId', + 'allocationPools', + 'hostRoutes', + 'ipVersion', + 'gatewayIp', + 'cidr', + 'tenantId' + ); + + protected $updateKeys = array( + 'name', + 'enableDhcp', + 'networkId', + 'allocationPools', + 'hostRoutes', + 'ipVersion', + 'gatewayIp', + 'cidr', + 'tenantId' + ); +} diff --git a/lib/OpenCloud/Networking/Resource/Subnet.php b/lib/OpenCloud/Networking/Resource/Subnet.php new file mode 100644 index 000000000..ab8c313e4 --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/Subnet.php @@ -0,0 +1,80 @@ + 'enableDhcp', + 'network_id' => 'networkId', + 'dns_nameservers' => 'dnsNameservers', + 'allocation_pools' => 'allocationPools', + 'host_routes' => 'hostRoutes', + 'ip_version' => 'ipVersion', + 'gateway_ip' => 'gatewayIp', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'name', + 'enableDhcp', + 'networkId', + 'allocationPools', + 'hostRoutes', + 'ipVersion', + 'gatewayIp', + 'cidr', + 'tenantId' + ); + + protected $updateKeys = array( + 'name', + 'enableDhcp', + 'networkId', + 'allocationPools', + 'hostRoutes', + 'ipVersion', + 'gatewayIp', + 'cidr', + 'tenantId' + ); +} diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php new file mode 100644 index 000000000..f18c4d0f4 --- /dev/null +++ b/lib/OpenCloud/Networking/Service.php @@ -0,0 +1,194 @@ +resource('Network', $id); + } + + /** + * Creates a new Network and returns it. + * + * @param array $params Network creation parameters + * @return Network Object representing created network + */ + public function createNetwork($params = array()) + { + $network = $this->network(); + $network->create($params); + return $network; + } + + /** + * Returns a Network object associated with this Networking service + * + * @param string $id ID of network to retrieve + * @return Network object + */ + public function getNetwork($id) + { + return $this->network($id); + } + + /** + * Returns a list of networks you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listNetworks(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Network::resourceName())->setQuery($params); + + return $this->resourceList('Network', $url); + } + + /** + * Returns a Subnet object associated with this Networking service + * + * @param string $id ID of subnet to retrieve + * @return Subnet object + */ + public function subnet($id = null) + { + return $this->resource('Subnet', $id); + } + + /** + * Creates a new Subnet and returns it. + * + * @param array $params Subnet creation parameters + * @return Subnet Object representing created subnet + */ + public function createSubnet($params = array()) + { + $subnet = $this->subnet(); + $subnet->create($params); + return $subnet; + } + + /** + * Returns a Subnet object associated with this Networking service + * + * @param string $id ID of subnet to retrieve + * @return Subnet object + */ + public function getSubnet($id) + { + return $this->subnet($id); + } + + /** + * Returns a list of subnets you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSubnets(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Subnet::resourceName())->setQuery($params); + + return $this->resourceList('Subnet', $url); + } + + /** + * Returns a Port object associated with this Networking service + * + * @param string $id ID of port to retrieve + * @return Port object + */ + public function port($id = null) + { + return $this->resource('Port', $id); + } + + /** + * Creates a new Port and returns it. + * + * @param array $params Port creation parameters + * @return Port Object representing created port + */ + public function createPort($params = array()) + { + $port = $this->port(); + $port->create($params); + return $port; + } + + /** + * Returns a Port object associated with this Networking service + * + * @param string $id ID of port to retrieve + * @return Port object + */ + public function getPort($id) + { + return $this->port($id); + } + + /** + * Returns a list of ports you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listPorts(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Port::resourceName())->setQuery($params); + + return $this->resourceList('Port', $url); + } + + /** + * Return namespaces. + * + * @return array + */ + public function namespaces() + { + return array(); + } +} diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 0ab054eef..9d4dd3dff 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -545,4 +545,23 @@ public function imageService($name = null, $region = null, $urltype = null) 'urlType' => $urltype )); } + + /** + * Creates a new Networking (Neutron) service object + * + * @param string $name The name of the service as it appears in the Catalog + * @param string $region The region (DFW, IAD, ORD, LON, SYD) + * @param string $urltype The URL type ("publicURL" or "internalURL") + * @return \OpenCloud\Networking\Service + * @codeCoverageIgnore + */ + public function networkingService($name = null, $region = null, $urltype = null) + { + return ServiceBuilder::factory($this, 'OpenCloud\Networking\Service', array( + 'name' => $name, + 'region' => $region, + 'urlType' => $urltype + )); + } + } diff --git a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php new file mode 100644 index 000000000..be6b304c4 --- /dev/null +++ b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php @@ -0,0 +1,51 @@ +service = $this->getClient()->networkingService(); + } + + protected function assertIsService($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Service', $object); + } + + protected function assertIsNetwork($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\Network', $object); + } + + protected function assertIsSubnet($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\Subnet', $object); + } + + protected function assertIsPort($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\Port', $object); + } +} diff --git a/tests/OpenCloud/Tests/Networking/ServiceTest.php b/tests/OpenCloud/Tests/Networking/ServiceTest.php new file mode 100644 index 000000000..85092cb2e --- /dev/null +++ b/tests/OpenCloud/Tests/Networking/ServiceTest.php @@ -0,0 +1,99 @@ +getClient()->networkingService(null, 'IAD'); + $this->assertIsService($service); + } + + public function testCreateNetwork() + { + $this->assertIsNetwork($this->service->createNetwork()); + } + + public function testListNetworks() + { + $this->addMockSubscriber($this->makeResponse('{"networks":[{"admin_state_up":true,"id":"00000000-0000-0000-0000-000000000000","name":"public","shared":true,"status":"ACTIVE","subnets":[],"tenant_id":"rackspace"},{"admin_state_up":true,"id":"11111111-1111-1111-1111-111111111111","name":"private","shared":true,"status":"ACTIVE","subnets":[],"tenant_id":"rackspace"},{"admin_state_up":true,"id":"2993e407-5531-4ca8-9d2a-0d13b5cac904","name":"RackNet","shared":false,"status":"ACTIVE","subnets":["017d8997-70ec-4448-91d9-a8097d6d60f3"],"tenant_id":"123456"}]}')); + + $networks = $this->service->listNetworks(); + $this->isCollection($networks); + $this->assertIsNetwork($networks->getElement(0)); + } + + public function testGetNetwork() + { + $this->addMockSubscriber($this->makeResponse('{"network":{"admin_state_up":true,"id":"4d4e772a-98e7-4409-8a3c-4fed4324da26","name":"sameer-3","shared":false,"status":"ACTIVE","subnets":[],"tenant_id":"546428"}}')); + + $network = $this->service->getNetwork('4d4e772a-98e7-4409-8a3c-4fed4324da26'); + $this->assertIsNetwork($network); + $this->assertEquals('sameer-3', $network->getName()); + } + + public function testCreateSubnet() + { + $this->assertIsSubnet($this->service->createSubnet()); + } + + public function testListSubnets() + { + $this->addMockSubscriber($this->makeResponse('{"subnets":[{"allocation_pools":[{"end":"192.168.9.254","start":"192.168.9.1"}],"cidr":"192.168.9.0/24","dns_nameservers":[],"enable_dhcp":false,"gateway_ip":null,"host_routes":[],"id":"f975defc-637d-4e2a-858b-c6cc4cec3951","ip_version":4,"name":"","network_id":"0ebf6a10-5fc1-4f13-aca9-be0a2a00b1ac","tenant_id":"123456"}]}')); + + $subnets = $this->service->listSubnets(); + $this->isCollection($subnets); + $this->assertIsSubnet($subnets->getElement(0)); + } + + public function testGetSubnet() + { + $this->addMockSubscriber($this->makeResponse('{"subnet":{"name":"my_subnet","enable_dhcp":false,"network_id":"d32019d3-bc6e-4319-9c1d-6722fc136a22","tenant_id":"4fd44f30292945e481c7b8a0c8908869","dns_nameservers":[],"allocation_pools":[{"start":"192.0.0.2","end":"192.255.255.254"}],"host_routes":[],"ip_version":4,"gateway_ip":"192.0.0.1","cidr":"192.0.0.0/8","id":"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}}')); + + $subnet = $this->service->getSubnet('54d6f61d-db07-451c-9ab3-b9609b6b6f0b'); + $this->assertIsSubnet($subnet); + $this->assertEquals('my_subnet', $subnet->getName()); + } + + public function testCreatePort() + { + $this->assertIsPort($this->service->createPort()); + } + + public function testListPorts() + { + $this->addMockSubscriber($this->makeResponse('{"ports":[{"admin_state_up":true,"device_id":"","device_owner":null,"fixed_ips":[{"ip_address":"192.168.3.11","subnet_id":"739ecc58-f9a0-4145-8a06-cd415e6e5c8d"}],"id":"10ba23f5-bb70-4fd7-a118-83f89b62e340","mac_address":"BE:CB:FE:00:00:EE","name":"port1","network_id":"6406ed30-193a-4958-aae5-7c05268d332b","security_groups":[],"status":"ACTIVE","tenant_id":"123456"}]}')); + + $ports = $this->service->listPorts(); + $this->isCollection($ports); + $this->assertIsPort($ports->getElement(0)); + } + + public function testGetPort() + { + $this->addMockSubscriber($this->makeResponse('{"port":{"admin_state_up":true,"device_id":"","device_owner":null,"fixed_ips":[{"ip_address":"192.168.3.11","subnet_id":"739ecc58-f9a0-4145-8a06-cd415e6e5c8d"}],"id":"10ba23f5-bb70-4fd7-a118-83f89b62e340","mac_address":"BE:CB:FE:00:00:EE","name":"port1","network_id":"6406ed30-193a-4958-aae5-7c05268d332b","security_groups":[],"status":"ACTIVE","tenant_id":"123456"}}')); + + $port = $this->service->getPort('10ba23f5-bb70-4fd7-a118-83f89b62e340'); + $this->assertIsPort($port); + $this->assertEquals('port1', $port->getName()); + } +} diff --git a/tests/OpenCloud/Tests/_response/Auth.resp b/tests/OpenCloud/Tests/_response/Auth.resp index ce1ea5efc..c5c1c146f 100644 --- a/tests/OpenCloud/Tests/_response/Auth.resp +++ b/tests/OpenCloud/Tests/_response/Auth.resp @@ -348,6 +348,22 @@ Front-End-Https: on ], "name": "cloudOrchestration", "type": "orchestration" + }, + { + "name": "cloudNetworks", + "endpoints": [ + { + "region": "IAD", + "tenantId": "123456", + "publicURL": "https://iad.networks.api.rackspacecloud.com/v2.0" + }, + { + "region": "DFW", + "tenantId": "123456", + "publicURL": "https://dfw.networks.api.rackspacecloud.com/v2.0" + } + ], + "type": "networks" } ], "token": { From 9db5e4e4e7dfd8034688f8e9a06c875a7da5ccc3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 4 Nov 2014 15:31:03 -0800 Subject: [PATCH 151/835] Trivial whitespace lint fixes. --- lib/OpenCloud/OpenStack.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 9d4dd3dff..495f31115 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -563,5 +563,4 @@ public function networkingService($name = null, $region = null, $urltype = null) 'urlType' => $urltype )); } - } From 3b9d2d0045b2a701d2d3a328243263189f2642b0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 06:18:32 -0800 Subject: [PATCH 152/835] Adding unit tests and implementation for bulk creation operations. --- lib/OpenCloud/Networking/Resource/Network.php | 10 ++ lib/OpenCloud/Networking/Resource/Port.php | 10 ++ lib/OpenCloud/Networking/Resource/Subnet.php | 10 ++ lib/OpenCloud/Networking/Service.php | 109 ++++++++++++++++++ .../Tests/Networking/ServiceTest.php | 52 ++++++++- 5 files changed, 190 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index fd9f621f8..934f151bd 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -57,4 +57,14 @@ class Network extends PersistentResource 'shared', 'tenantId' ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as OpenCloud\Networking\Service. + */ + public function createJson() + { + return parent::createJson(); + } } diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index aaf14fd87..77919131d 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -77,4 +77,14 @@ class Port extends PersistentResource 'cidr', 'tenantId' ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as OpenCloud\Networking\Service. + */ + public function createJson() + { + return parent::createJson(); + } } diff --git a/lib/OpenCloud/Networking/Resource/Subnet.php b/lib/OpenCloud/Networking/Resource/Subnet.php index ab8c313e4..cbbbc9e2e 100644 --- a/lib/OpenCloud/Networking/Resource/Subnet.php +++ b/lib/OpenCloud/Networking/Resource/Subnet.php @@ -77,4 +77,14 @@ class Subnet extends PersistentResource 'cidr', 'tenantId' ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as OpenCloud\Networking\Service. + */ + public function createJson() + { + return parent::createJson(); + } } diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index f18c4d0f4..cac03e122 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -18,6 +18,7 @@ namespace OpenCloud\Networking; use OpenCloud\Common\Service\CatalogService; +use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Networking\Resource\Network; use OpenCloud\Networking\Resource\Subnet; use OpenCloud\Networking\Resource\Port; @@ -59,6 +60,42 @@ public function createNetwork($params = array()) return $network; } + /** + * Creates multiple new Networks and returns their list. + * + * @param array $networksParams Array of network creation parameters' arrays + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function createNetworks($networksParams = array()) + { + // Form URL + $url = clone $this->getUrl(); + $url->addPath(Network::resourceName()); + + // Form JSON + $singleNetworkJsonName = Network::jsonName(); + $networksJsonCollectionName = Network::jsonCollectionName(); + $networks = array(); + foreach ($networksParams as $networkParams) { + $network = $this->network(); + $network->populate($networkParams); + $networks[] = $network->createJson()->$singleNetworkJsonName; + } + $json = json_encode(array( + $networksJsonCollectionName => $networks + )); + + // Call the API + $response = $this->getClient()->post($url, self::getJsonHeader(), $json)->send(); + + // Parse the response into a collection of created networks + $responseJson = Formatter::decode($response); + $createdNetworksJson = $responseJson->$networksJsonCollectionName; + + // Return collection of created networks + return $this->collection('Network', $url, $this, $createdNetworksJson); + } + /** * Returns a Network object associated with this Networking service * @@ -108,6 +145,42 @@ public function createSubnet($params = array()) return $subnet; } + /** + * Creates multiple new Subnets and returns their list. + * + * @param array $subnetsParams Array of subnet creation parameters' arrays + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function createSubnets($subnetsParams = array()) + { + // Form URL + $url = clone $this->getUrl(); + $url->addPath(Subnet::resourceName()); + + // Form JSON + $singleSubnetJsonName = Subnet::jsonName(); + $subnetsJsonCollectionName = Subnet::jsonCollectionName(); + $subnets = array(); + foreach ($subnetsParams as $subnetParams) { + $subnet = $this->subnet(); + $subnet->populate($subnetParams); + $subnets[] = $subnet->createJson()->$singleSubnetJsonName; + } + $json = json_encode(array( + $subnetsJsonCollectionName => $subnets + )); + + // Call the API + $response = $this->getClient()->post($url, self::getJsonHeader(), $json)->send(); + + // Parse the response into a collection of created subnets + $responseJson = Formatter::decode($response); + $createdSubnetsJson = $responseJson->$subnetsJsonCollectionName; + + // Return collection of created subnets + return $this->collection('Subnet', $url, $this, $createdSubnetsJson); + } + /** * Returns a Subnet object associated with this Networking service * @@ -157,6 +230,42 @@ public function createPort($params = array()) return $port; } + /** + * Creates multiple new Ports and returns their list. + * + * @param array $portsParams Array of port creation parameters' arrays + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function createPorts($portsParams = array()) + { + // Form URL + $url = clone $this->getUrl(); + $url->addPath(Port::resourceName()); + + // Form JSON + $singlePortJsonName = Port::jsonName(); + $portsJsonCollectionName = Port::jsonCollectionName(); + $ports = array(); + foreach ($portsParams as $portParams) { + $port = $this->port(); + $port->populate($portParams); + $ports[] = $port->createJson()->$singlePortJsonName; + } + $json = json_encode(array( + $portsJsonCollectionName => $ports + )); + + // Call the API + $response = $this->getClient()->post($url, self::getJsonHeader(), $json)->send(); + + // Parse the response into a collection of created ports + $responseJson = Formatter::decode($response); + $createdPortsJson = $responseJson->$portsJsonCollectionName; + + // Return collection of created ports + return $this->collection('Port', $url, $this, $createdPortsJson); + } + /** * Returns a Port object associated with this Networking service * diff --git a/tests/OpenCloud/Tests/Networking/ServiceTest.php b/tests/OpenCloud/Tests/Networking/ServiceTest.php index 85092cb2e..92b793e19 100644 --- a/tests/OpenCloud/Tests/Networking/ServiceTest.php +++ b/tests/OpenCloud/Tests/Networking/ServiceTest.php @@ -30,7 +30,25 @@ public function test__construct() public function testCreateNetwork() { - $this->assertIsNetwork($this->service->createNetwork()); + $this->assertIsNetwork($this->service->createNetwork(array( + 'name' => 'network1' + ))); + } + + public function testCreateNetworks() + { + $this->addMockSubscriber($this->makeResponse('{"networks":[{"admin_state_up":true,"id":"e6031bc2-901a-4c66-82da-f4c32ed89406","name":"sample_network_1","shared":false,"status":"ACTIVE","subnets":[],"tenant_id":"d19231fc08ec4bc4829b668040d34512"},{"admin_state_up":false,"id":"64239a54-dcc4-4b39-920b-b37c2144effa","name":"sample_network_2","shared":false,"status":"ACTIVE","subnets":[],"tenant_id":"d19231fc08ec4bc4829b668040d34512"}]}')); + + $createdNetworks = $this->service->createNetworks(array( + array( + 'name' => 'sample_network_1' + ), + array( + 'name' => 'sample_network_2' + ) + )); + $this->isCollection($createdNetworks); + $this->assertIsNetwork($createdNetworks->getElement(0)); } public function testListNetworks() @@ -56,6 +74,22 @@ public function testCreateSubnet() $this->assertIsSubnet($this->service->createSubnet()); } + public function testCreateSubnets() + { + $this->addMockSubscriber($this->makeResponse('{"subnets":[{"cidr":"192.168.199.0/24","ip_version":4,"network_id":"e6031bc2-901a-4c66-82da-f4c32ed89406"},{"cidr":"10.56.4.0/22","ip_version":4,"network_id":"64239a54-dcc4-4b39-920b-b37c2144effa"}]}')); + + $createdSubnets = $this->service->createSubnets(array( + array( + 'cidr' => '192.168.199.0/24' + ), + array( + 'cidr' => '10.56.4.0/22' + ) + )); + $this->isCollection($createdSubnets); + $this->assertIsSubnet($createdSubnets->getElement(0)); + } + public function testListSubnets() { $this->addMockSubscriber($this->makeResponse('{"subnets":[{"allocation_pools":[{"end":"192.168.9.254","start":"192.168.9.1"}],"cidr":"192.168.9.0/24","dns_nameservers":[],"enable_dhcp":false,"gateway_ip":null,"host_routes":[],"id":"f975defc-637d-4e2a-858b-c6cc4cec3951","ip_version":4,"name":"","network_id":"0ebf6a10-5fc1-4f13-aca9-be0a2a00b1ac","tenant_id":"123456"}]}')); @@ -79,6 +113,22 @@ public function testCreatePort() $this->assertIsPort($this->service->createPort()); } + public function testCreatePorts() + { + $this->addMockSubscriber($this->makeResponse('{"ports":[{"admin_state_up":true,"device_id":"24df1d04-d5cb-41e1-8de5-61ed77c558df","name":"port1","network_id":"64239a54-dcc4-4b39-920b-b37c2144effa","security_groups":["dbc107f4-afcd-4d5a-9352-f68f82241d5b"]},{"admin_state_up":false,"name":"port2","network_id":"e6031bc2-901a-4c66-82da-f4c32ed89406","security_groups":["8bf3f7cc-8471-40b1-815f-9da47e79775b","dbc107f4-afcd-4d5a-9352-f68f82241d5b"]}]}')); + + $createdPorts = $this->service->createPorts(array( + array( + 'name' => 'port1' + ), + array( + 'name' => 'port2' + ) + )); + $this->isCollection($createdPorts); + $this->assertIsPort($createdPorts->getElement(0)); + } + public function testListPorts() { $this->addMockSubscriber($this->makeResponse('{"ports":[{"admin_state_up":true,"device_id":"","device_owner":null,"fixed_ips":[{"ip_address":"192.168.3.11","subnet_id":"739ecc58-f9a0-4145-8a06-cd415e6e5c8d"}],"id":"10ba23f5-bb70-4fd7-a118-83f89b62e340","mac_address":"BE:CB:FE:00:00:EE","name":"port1","network_id":"6406ed30-193a-4958-aae5-7c05268d332b","security_groups":[],"status":"ACTIVE","tenant_id":"123456"}]}')); From eb30f3810e16e4dcf429def332d789eff36511cd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 16:17:28 -0800 Subject: [PATCH 153/835] Adding smoke tests. --- tests/OpenCloud/Smoke/Runner.php | 1 + tests/OpenCloud/Smoke/Unit/Networking.php | 273 ++++++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 tests/OpenCloud/Smoke/Unit/Networking.php diff --git a/tests/OpenCloud/Smoke/Runner.php b/tests/OpenCloud/Smoke/Runner.php index 073e1c376..d1925ba83 100644 --- a/tests/OpenCloud/Smoke/Runner.php +++ b/tests/OpenCloud/Smoke/Runner.php @@ -41,6 +41,7 @@ class Runner 'Database', 'Identity', 'LoadBalancer', + 'Networking', 'ObjectStore', 'Orchestration', 'Queues', diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php new file mode 100644 index 000000000..c2dda80c3 --- /dev/null +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -0,0 +1,273 @@ +getConnection()->networkingService('cloudNetworks', Utils::getRegion()); + return $this->getConnection()->networkingService('cloudNetworksPreprod', Utils::getRegion()); + } + + public function main() + { + $this->testNetworkOperations(); + $this->testSubnetOperations(); + $this->testPortOperations(); + } + + protected function testNetworkOperations() + { + $this->step('Create network'); + $createdNetwork = $this->getService()->createNetwork(array( + 'name' => 'test_network' + )); + $this->stepInfo('Network ID: ' . $createdNetwork->getId()); + $this->stepInfo('Network Name: ' . $createdNetwork->getName()); + $this->cleanupNetworkIds[] = $createdNetwork->getId(); + + // The next operation is commented out (for now) because the Rackspace + // Networking API does not support bulk operations (for now). When that + // changes in the future, please uncomment this operation. + // $this->step('Create networks'); + // $createdNetworks = $this->getService()->createNetworks(array( + // array( 'name' => 'test_network_1' ), + // array( 'name' => 'test_network_2' ), + // array( 'name' => 'test_network_3' ), + // )); + // $this->stepInfo('%-40s | %s', 'Network ID', 'Network name'); + // $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + // foreach ($createdNetworks as $network) { + // $this->stepInfo('%-40s | %s', $network->getId(), $network->getName()); + // $this->cleanupNetworkIds[] = $network->getId(); + // } + + $this->step('List networks'); + $networks = $this->getService()->listNetworks(); + $this->stepInfo('%-40s | %s', 'Network ID', 'Network name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($networks as $network) { + $this->stepInfo('%-40s | %s', $network->getId(), $network->getName()); + } + + $this->step('Get network'); + $network = $this->getService()->getNetwork($createdNetwork->getId()); + $this->stepInfo('Network ID: ' . $network->getId()); + $this->stepInfo('Network Name: ' . $network->getName()); + + $this->step('Update network'); + $network->update(array( + 'name' => 'updated_test_network' + )); + } + + protected function testSubnetOperations() + { + $network1 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_subnet' + )); + $this->cleanupNetworkIds[] = $network1->getId(); + + $this->step('Create subnet'); + $subnet = $this->getService()->createSubnet(array( + 'cidr' => '192.168.199.0/24', + 'networkId' => $network1->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet' + )); + $this->stepInfo('Subnet ID: ' . $subnet->getId()); + $this->stepInfo('Subnet Name: ' . $subnet->getName()); + $this->cleanupSubnetIds[] = $subnet->getId(); + + $network2 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_subnet_w_gateway' + )); + $this->cleanupNetworkIds[] = $network2->getId(); + + $this->step('Create subnet with gateway IP'); + $subnet = $this->getService()->createSubnet(array( + 'cidr' => '192.168.62.0/25', + 'networkId' => $network2->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet_with_gateway_ip', + 'gatewayIp' => '192.168.62.128' + )); + $this->stepInfo('Subnet ID: ' . $subnet->getId()); + $this->stepInfo('Subnet Name: ' . $subnet->getName()); + $this->cleanupSubnetIds[] = $subnet->getId(); + + $network3 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_subnet_w_host_rt' + )); + $this->cleanupNetworkIds[] = $network3->getId(); + + $this->step('Create subnet with host routes'); + $subnet = $this->getService()->createSubnet(array( + 'cidr' => '192.168.62.0/25', + 'networkId' => $network3->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet_with_host_routes', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.19.20' + ) + ) + )); + $this->stepInfo('Subnet ID: ' . $subnet->getId()); + $this->stepInfo('Subnet Name: ' . $subnet->getName()); + $this->cleanupSubnetIds[] = $subnet->getId(); + + // The next operation is commented out (for now) because the Rackspace + // Networking API does not support bulk operations (for now). When that + // changes in the future, please uncomment this operation. + // $this->step('Create subnets'); + // $subnets = $this->getService()->createSubnets(array( + // array( + // 'cidr' => '192.168.199.0/24', + // 'networkId' => $network->getId(), + // 'ipVersion' => 4, + // 'name' => 'test_subnet_1' + // ) + // )); + // $this->stepInfo('%-40s | %s', 'Subnet ID', 'Subnet name'); + // $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + // foreach ($subnets as $subnet) { + // $this->stepInfo('%-40s | %s', $subnet->getId(), $subnet->getName()); + // $this->cleanupSubnetIds[] = $subnet->getId(); + // } + + $this->step('List subnets'); + $subnets = $this->getService()->listSubnets(); + $this->stepInfo('%-40s | %s', 'Subnet ID', 'Subnet name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($subnets as $subnet) { + $this->stepInfo('%-40s | %s', $subnet->getId(), $subnet->getName()); + } + + $this->step('Get subnet'); + $subnet = $this->getService()->getSubnet($subnet->getId()); + $this->stepInfo('Subnet ID: ' . $subnet->getId()); + $this->stepInfo('Subnet Name: ' . $subnet->getName()); + + $this->step('Update subnet'); + $subnet->update(array( + 'name' => 'updated_test_subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + } + + protected function testPortOperations() + { + $network1 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_port' + )); + $this->cleanupNetworkIds[] = $network1->getId(); + + $subnet1 = $this->getService()->createSubnet(array( + 'cidr' => '192.168.62.0/25', + 'networkId' => $network1->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet_for_test_port' + )); + $this->cleanupSubnetIds[] = $subnet1->getId(); + + $this->step('Create port'); + $port = $this->getService()->createPort(array( + 'networkId' => $network1->getId(), + 'name' => 'test_port' + )); + $this->stepInfo('Port ID: ' . $port->getId()); + $this->stepInfo('Port Name: ' . $port->getName()); + $this->cleanupPortIds[] = $port->getId(); + + // The next operation is commented out (for now) because the Rackspace + // Networking API does not support bulk operations (for now). When that + // changes in the future, please uncomment this operation. + // $this->step('Create ports'); + // $ports = $this->getService()->createPorts(array( + // )); + // $this->stepInfo('%-40s | %s', 'Port ID', 'Port name'); + // $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + // foreach ($ports as $port) { + // $this->stepInfo('%-40s | %s', $port->getId(), $port->getName()); + // $this->cleanupPortIds[] = $port->getId(); + // } + + $this->step('List ports'); + $ports = $this->getService()->listPorts(); + $this->stepInfo('%-40s | %s', 'Port ID', 'Port name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($ports as $port) { + $this->stepInfo('%-40s | %s', $port->getId(), $port->getName()); + } + + $this->step('Get port'); + $port = $this->getService()->getPort($port->getId()); + $this->stepInfo('Port ID: ' . $port->getId()); + $this->stepInfo('Port Name: ' . $port->getName()); + + $this->step('Update port'); + $port->update(array( + 'name' => 'updated_test_port', + 'fixedIps' => array( + array( + 'subnet_id' => $subnet1->getId(), + 'ip_address' => '192.168.62.17' + ) + ) + )); + } + + public function teardown() + { + foreach ($this->cleanupPortIds as $portId) { + $port = $this->getService()->getPort($portId); + $port->delete(); + } + + foreach ($this->cleanupSubnetIds as $subnetId) { + $subnet = $this->getService()->getSubnet($subnetId); + $subnet->delete(); + } + + foreach ($this->cleanupNetworkIds as $networkId) { + $network = $this->getService()->getNetwork($networkId); + $network->delete(); + } + + } +} From bc2c93acac28de4889029cbb51681035b8ff8ace Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 16:17:38 -0800 Subject: [PATCH 154/835] Fixing update-able attributes based on empirical evidence. --- lib/OpenCloud/Networking/Resource/Network.php | 3 +-- lib/OpenCloud/Networking/Resource/Port.php | 25 ++++++++----------- lib/OpenCloud/Networking/Resource/Subnet.php | 7 +----- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 934f151bd..2f75a2592 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -54,8 +54,7 @@ class Network extends PersistentResource protected $updateKeys = array( 'adminStateUp', 'name', - 'shared', - 'tenantId' + 'shared' ); /** diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 77919131d..63a92dc2c 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -56,26 +56,23 @@ class Port extends PersistentResource protected $createKeys = array( 'name', - 'enableDhcp', + 'adminStateUp', 'networkId', - 'allocationPools', - 'hostRoutes', - 'ipVersion', - 'gatewayIp', - 'cidr', + 'deviceId', + 'deviceOwner', + 'fixedIps', + 'macAddress', + 'securityGroups', 'tenantId' ); protected $updateKeys = array( 'name', - 'enableDhcp', - 'networkId', - 'allocationPools', - 'hostRoutes', - 'ipVersion', - 'gatewayIp', - 'cidr', - 'tenantId' + 'adminStateUp', + 'deviceId', + 'deviceOwner', + 'fixedIps', + 'securityGroups' ); /** diff --git a/lib/OpenCloud/Networking/Resource/Subnet.php b/lib/OpenCloud/Networking/Resource/Subnet.php index cbbbc9e2e..b51727d0b 100644 --- a/lib/OpenCloud/Networking/Resource/Subnet.php +++ b/lib/OpenCloud/Networking/Resource/Subnet.php @@ -69,13 +69,8 @@ class Subnet extends PersistentResource protected $updateKeys = array( 'name', 'enableDhcp', - 'networkId', - 'allocationPools', 'hostRoutes', - 'ipVersion', - 'gatewayIp', - 'cidr', - 'tenantId' + 'gatewayIp' ); /** From ee8a9a335eb5b483f9e792f3ff684510ed746f8c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 17:52:46 -0800 Subject: [PATCH 155/835] Recursively alias properties. --- .../Common/Resource/PersistentResource.php | 28 ++++- .../Resource/PersistentResourceTest.php | 109 ++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index e87d46323..76977758a 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -217,7 +217,7 @@ protected function createJson() foreach ($this->createKeys as $key) { if (null !== ($property = $this->getProperty($key))) { - $element->{$this->getAlias($key)} = $property; + $element->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($property); } } @@ -244,6 +244,30 @@ protected function getAlias($key) return $key; } + protected function recursivelyAliasPropertyValue($propertyValue) + { + if (is_array($propertyValue)) { + foreach ($propertyValue as $key => $subValue) { + $aliasedSubValue = $this->recursivelyAliasPropertyValue($subValue); + if (is_numeric($key)) { + $propertyValue[$key] = $aliasedSubValue; + } else { + unset($propertyValue[$key]); + $propertyValue[$this->getAlias($key)] = $aliasedSubValue; + } + } + } + + elseif (is_object($propertyValue) && ($propertyValue instanceOf \stdClass)) { + foreach ($propertyValue as $key => $subValue) { + unset($propertyValue->$key); + $propertyValue->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($subValue); + } + } + + return $propertyValue; + } + /** * Provides JSON for update request body */ @@ -260,7 +284,7 @@ protected function updateJson($params = array()) foreach ($this->updateKeys as $key) { if (null !== ($property = $this->getProperty($key))) { - $element->{$this->getAlias($key)} = $property; + $element->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($property); } } diff --git a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php new file mode 100644 index 000000000..cd81b93ab --- /dev/null +++ b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php @@ -0,0 +1,109 @@ + 'fooBar' + ); + + public function recursivelyAliasPropertyValue($propertyValue) + { + return parent::recursivelyAliasPropertyValue($propertyValue); + } +} + +class PersistentResourceTest extends OpenCloudTestCase +{ + private $persistentResource; + + public function setupObjects() + { + $service = IdentityService::factory($this->client); + $this->persistentResource = new PublicPersistentResource($service); + } + + public function testRecursivelyAliasPropertyValueWithScalars() + { + $this->assertEquals(11, + $this->persistentResource->recursivelyAliasPropertyValue(11)); + $this->assertEquals("foobar", + $this->persistentResource->recursivelyAliasPropertyValue("foobar")); + $this->assertEquals("fooBar", + $this->persistentResource->recursivelyAliasPropertyValue("fooBar")); + $this->assertEquals(false, + $this->persistentResource->recursivelyAliasPropertyValue(false)); + } + + public function testRecursivelyAliasPropertyValueWithIndexedArrays() + { + $this->assertEquals(array(18), + $this->persistentResource->recursivelyAliasPropertyValue(array(18))); + $this->assertEquals(array("foobar"), + $this->persistentResource->recursivelyAliasPropertyValue(array("foobar"))); + $this->assertEquals(array("fooBar"), + $this->persistentResource->recursivelyAliasPropertyValue(array("fooBar"))); + } + + public function testRecursivelyAliasPropertyValueWithAssociativeArrays() + { + $this->assertEquals(array("foobar" => "baz"), + $this->persistentResource->recursivelyAliasPropertyValue(array("foobar" => "baz"))); + $this->assertEquals(array("foo_bar" => "baz"), + $this->persistentResource->recursivelyAliasPropertyValue(array("fooBar" => "baz"))); + $this->assertEquals(array("qux" => array("foo_bar" => "baz")), + $this->persistentResource->recursivelyAliasPropertyValue(array("qux" => array("fooBar" => "baz")))); + } + + public function testRecursivelyAliasPropertyValueWithObjects() + { + $obj1 = new \stdClass(); + $obj1->foobar = "baz"; + + $obj1Expected = new \stdClass(); + $obj1Expected->foobar = "baz"; + + $this->assertEquals($obj1Expected, + $this->persistentResource->recursivelyAliasPropertyValue($obj1)); + + $obj2 = new \stdClass(); + $obj2->fooBar = "baz"; + + $obj2Expected = new \stdClass(); + $obj2Expected->foo_bar = "baz"; + + $this->assertEquals($obj2Expected, + $this->persistentResource->recursivelyAliasPropertyValue($obj2)); + + $obj3 = new \stdClass(); + $obj3->qux = new \stdClass(); + $obj3->qux->fooBar = "baz"; + + $obj3Expected = new \stdClass(); + $obj3Expected->qux = new \stdClass(); + $obj3Expected->qux->foo_bar = "baz"; + + $this->assertEquals($obj3Expected, + $this->persistentResource->recursivelyAliasPropertyValue($obj3)); + } +} From 72e3d10aef11f150e85adbe1e23daab18f7c3158 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 17:53:34 -0800 Subject: [PATCH 156/835] Adding aliases for sub-properties. --- lib/OpenCloud/Networking/Resource/Port.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 63a92dc2c..072edb850 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -51,7 +51,9 @@ class Port extends PersistentResource 'fixed_ips' => 'fixedIps', 'mac_address' => 'macAddress', 'security_groups' => 'securityGroups', - 'tenant_id' => 'tenantId' + 'tenant_id' => 'tenantId', + 'subnet_id' => 'subnetId', + 'ip_address' => 'ipAddress' ); protected $createKeys = array( From 07c045694e2cd7f4c2ae53e706f5ca17f6e2b3ec Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 17:54:06 -0800 Subject: [PATCH 157/835] Using camelCase sub-properties. --- tests/OpenCloud/Smoke/Unit/Networking.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index c2dda80c3..988a37591 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -242,11 +242,11 @@ protected function testPortOperations() $this->step('Update port'); $port->update(array( - 'name' => 'updated_test_port', + 'name' => 'updated_test_port', 'fixedIps' => array( array( - 'subnet_id' => $subnet1->getId(), - 'ip_address' => '192.168.62.17' + 'subnetId' => $subnet1->getId(), + 'ipAddress' => '192.168.62.17' ) ) )); From f436fb93be8a2979b9d5e80d49af4987d655b569 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 17:54:34 -0800 Subject: [PATCH 158/835] Fleshing out update examples. --- samples/Networking/update-port.php | 14 +++++++++++--- samples/Networking/update-subnet.php | 9 ++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/samples/Networking/update-port.php b/samples/Networking/update-port.php index b4c4db186..59e174856 100644 --- a/samples/Networking/update-port.php +++ b/samples/Networking/update-port.php @@ -21,8 +21,10 @@ // * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, // * OS_USERNAME: Your OpenStack Cloud Account Username, // * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * PORT_ID: ID of port +// * OS_REGION_NAME: The OpenStack Cloud region you want to use, +// * PORT_ID: ID of port, +// * SUBNET_ID: ID of subnet for new fixed IP, and +// * IP_ADDRESS: IP address for new fixed IP // require __DIR__ . '/../../vendor/autoload.php'; @@ -43,5 +45,11 @@ // 4. Update port. $port->update(array( - 'name' => 'My updated port' + 'name' => 'My updated port', + 'fixedIps' => array( + array( + 'subnetId' => getenv('SUBNET_ID'), + 'ipAddress' => getenv('IP_ADDRESS') + ) + ) )); diff --git a/samples/Networking/update-subnet.php b/samples/Networking/update-subnet.php index 46f8b5d1a..6f74f9ffa 100644 --- a/samples/Networking/update-subnet.php +++ b/samples/Networking/update-subnet.php @@ -43,5 +43,12 @@ // 4. Update subnet. $subnet->update(array( - 'name' => 'My updated subnet' + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' )); From 6f61eb869620fb39a5a733bba6d0e679b13ee1f3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 17:55:02 -0800 Subject: [PATCH 159/835] Adding more nuanced create subnet samples. --- .../create-subnet-with-gateway-ip.php | 49 +++++++++++++++++ .../create-subnet-with-host-routes.php | 54 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 samples/Networking/create-subnet-with-gateway-ip.php create mode 100644 samples/Networking/create-subnet-with-host-routes.php diff --git a/samples/Networking/create-subnet-with-gateway-ip.php b/samples/Networking/create-subnet-with-gateway-ip.php new file mode 100644 index 000000000..512f49e49 --- /dev/null +++ b/samples/Networking/create-subnet-with-gateway-ip.php @@ -0,0 +1,49 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Create a subnet. +$subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => getenv('NETWORK_ID'), + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25', + 'gatewayIp' => '192.168.199.128' +)); +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/create-subnet-with-host-routes.php b/samples/Networking/create-subnet-with-host-routes.php new file mode 100644 index 000000000..2e20e7b87 --- /dev/null +++ b/samples/Networking/create-subnet-with-host-routes.php @@ -0,0 +1,54 @@ + getenv('OS_USERNAME'), + 'password' => getenv('OS_PASSWORD') +)); + +// 2. Obtain an Networking service object from the client. +$region = getenv('OS_REGION_NAME'); +$networkingService = $client->networkingService(null, $region); + +// 3. Create a subnet. +$subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => getenv('NETWORK_ID'), + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/24', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.19.20' + ) + ) +)); +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ From f3a8f4be4d0d421124850ded5886cba526a41a64 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 5 Nov 2014 18:02:44 -0800 Subject: [PATCH 160/835] Make the PSR-2 checker happy. --- lib/OpenCloud/Common/Resource/PersistentResource.php | 4 +--- tests/OpenCloud/Smoke/Unit/Networking.php | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 76977758a..583eeba41 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -256,9 +256,7 @@ protected function recursivelyAliasPropertyValue($propertyValue) $propertyValue[$this->getAlias($key)] = $aliasedSubValue; } } - } - - elseif (is_object($propertyValue) && ($propertyValue instanceOf \stdClass)) { + } elseif (is_object($propertyValue) && ($propertyValue instanceof \stdClass)) { foreach ($propertyValue as $key => $subValue) { unset($propertyValue->$key); $propertyValue->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($subValue); diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 988a37591..a2753e071 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -25,14 +25,14 @@ * @link */ class Networking extends AbstractUnit implements UnitInterface -{ +{ protected $cleanupNetworkIds = array(); protected $cleanupSubnetIds = array(); protected $cleanupPortIds = array(); public function setupService() { -// return $this->getConnection()->networkingService('cloudNetworks', Utils::getRegion()); + // return $this->getConnection()->networkingService('cloudNetworks', Utils::getRegion()); return $this->getConnection()->networkingService('cloudNetworksPreprod', Utils::getRegion()); } @@ -268,6 +268,5 @@ public function teardown() $network = $this->getService()->getNetwork($networkId); $network->delete(); } - } } From 1f7cfaa9de1bc8063e7c973b97f5eead3259d220 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 06:20:48 -0800 Subject: [PATCH 161/835] Adding previously-MIA header comment. --- lib/OpenCloud/Common/Resource/PersistentResource.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 583eeba41..c012b8e76 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -244,6 +244,14 @@ protected function getAlias($key) return $key; } + /** + * Returns the given property value's alias, if configured; Else, the + * unchanged property value is returned. If the given property value + * is an array or an instance of \stdClass, it is aliases recursively. + * + * @param mixed $propertyValue Array or \stdClass instance to alias + * @return mixed Property value, aliased recursively + */ protected function recursivelyAliasPropertyValue($propertyValue) { if (is_array($propertyValue)) { From 149d5cef8a06c4e5c5a91e914b5a7ee427ae2b18 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 06:28:58 -0800 Subject: [PATCH 162/835] Referencing class using @see annotation. --- lib/OpenCloud/Networking/Resource/Network.php | 2 +- lib/OpenCloud/Networking/Resource/Port.php | 2 +- lib/OpenCloud/Networking/Resource/Subnet.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 2f75a2592..596ea6d83 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -60,7 +60,7 @@ class Network extends PersistentResource /** * This method is inherited. The inherited method has protected scope * but we are widening the scope to public so this method may be called - * from other classes such as OpenCloud\Networking\Service. + * from other classes such as {@see OpenCloud\Networking\Service}. */ public function createJson() { diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 072edb850..6689bbb98 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -80,7 +80,7 @@ class Port extends PersistentResource /** * This method is inherited. The inherited method has protected scope * but we are widening the scope to public so this method may be called - * from other classes such as OpenCloud\Networking\Service. + * from other classes such as {@see OpenCloud\Networking\Service}. */ public function createJson() { diff --git a/lib/OpenCloud/Networking/Resource/Subnet.php b/lib/OpenCloud/Networking/Resource/Subnet.php index b51727d0b..bd454753d 100644 --- a/lib/OpenCloud/Networking/Resource/Subnet.php +++ b/lib/OpenCloud/Networking/Resource/Subnet.php @@ -76,7 +76,7 @@ class Subnet extends PersistentResource /** * This method is inherited. The inherited method has protected scope * but we are widening the scope to public so this method may be called - * from other classes such as OpenCloud\Networking\Service. + * from other classes such as {@see OpenCloud\Networking\Service}. */ public function createJson() { From 96e8b80e0fac9886698cb65cc8d4f10a21adf316 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 06:30:58 -0800 Subject: [PATCH 163/835] Removing unnecessary annotation. --- lib/OpenCloud/Networking/Service.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index cac03e122..596b9fd2b 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -28,8 +28,6 @@ * * Neutron is a service that provides networking between devices managed by other * OpenNetwork services (e.g. Compute). - * - * @codeCoverageIgnore */ class Service extends CatalogService { From 9603d494fafe30672c9ce298a1c5e00ff1b409b6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 06:37:12 -0800 Subject: [PATCH 164/835] Using FQCNs in method docblocks. --- lib/OpenCloud/Networking/Service.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 596b9fd2b..8f160491b 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -38,7 +38,7 @@ class Service extends CatalogService * Returns a Network object associated with this Networking service * * @param string $id ID of network to retrieve - * @return Network object + * @return \OpenCloud\Networking\Resource\Network object */ public function network($id = null) { @@ -49,7 +49,7 @@ public function network($id = null) * Creates a new Network and returns it. * * @param array $params Network creation parameters - * @return Network Object representing created network + * @return \OpenCloud\Networking\Resource\Network Object representing created network */ public function createNetwork($params = array()) { @@ -98,7 +98,7 @@ public function createNetworks($networksParams = array()) * Returns a Network object associated with this Networking service * * @param string $id ID of network to retrieve - * @return Network object + * @return \OpenCloud\Networking\Resource\Network object */ public function getNetwork($id) { @@ -123,7 +123,7 @@ public function listNetworks(array $params = array()) * Returns a Subnet object associated with this Networking service * * @param string $id ID of subnet to retrieve - * @return Subnet object + * @return \OpenCloud\Networking\Resource\Subnet object */ public function subnet($id = null) { @@ -134,7 +134,7 @@ public function subnet($id = null) * Creates a new Subnet and returns it. * * @param array $params Subnet creation parameters - * @return Subnet Object representing created subnet + * @return \OpenCloud\Networking\Resource\Subnet Object representing created subnet */ public function createSubnet($params = array()) { @@ -183,7 +183,7 @@ public function createSubnets($subnetsParams = array()) * Returns a Subnet object associated with this Networking service * * @param string $id ID of subnet to retrieve - * @return Subnet object + * @return \OpenCloud\Networking\Resource\Subnet object */ public function getSubnet($id) { @@ -208,7 +208,7 @@ public function listSubnets(array $params = array()) * Returns a Port object associated with this Networking service * * @param string $id ID of port to retrieve - * @return Port object + * @return \OpenCloud\Networking\Resource\Port object */ public function port($id = null) { @@ -219,7 +219,7 @@ public function port($id = null) * Creates a new Port and returns it. * * @param array $params Port creation parameters - * @return Port Object representing created port + * @return \OpenCloud\Networking\Resource\Port Object representing created port */ public function createPort($params = array()) { @@ -268,7 +268,7 @@ public function createPorts($portsParams = array()) * Returns a Port object associated with this Networking service * * @param string $id ID of port to retrieve - * @return Port object + * @return \OpenCloud\Networking\Resource\Port object */ public function getPort($id) { From d4f4132dd552386a6c3af017b40c829aa6aea3f7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 06:43:04 -0800 Subject: [PATCH 165/835] Type hinting array method parameters. --- lib/OpenCloud/Networking/Service.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 8f160491b..527e75f28 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -51,7 +51,7 @@ public function network($id = null) * @param array $params Network creation parameters * @return \OpenCloud\Networking\Resource\Network Object representing created network */ - public function createNetwork($params = array()) + public function createNetwork(array $params = array()) { $network = $this->network(); $network->create($params); @@ -64,7 +64,7 @@ public function createNetwork($params = array()) * @param array $networksParams Array of network creation parameters' arrays * @return \OpenCloud\Common\Collection\PaginatedIterator */ - public function createNetworks($networksParams = array()) + public function createNetworks(array $networksParams = array()) { // Form URL $url = clone $this->getUrl(); @@ -136,7 +136,7 @@ public function subnet($id = null) * @param array $params Subnet creation parameters * @return \OpenCloud\Networking\Resource\Subnet Object representing created subnet */ - public function createSubnet($params = array()) + public function createSubnet(array $params = array()) { $subnet = $this->subnet(); $subnet->create($params); @@ -149,7 +149,7 @@ public function createSubnet($params = array()) * @param array $subnetsParams Array of subnet creation parameters' arrays * @return \OpenCloud\Common\Collection\PaginatedIterator */ - public function createSubnets($subnetsParams = array()) + public function createSubnets(array $subnetsParams = array()) { // Form URL $url = clone $this->getUrl(); @@ -221,7 +221,7 @@ public function port($id = null) * @param array $params Port creation parameters * @return \OpenCloud\Networking\Resource\Port Object representing created port */ - public function createPort($params = array()) + public function createPort(array $params = array()) { $port = $this->port(); $port->create($params); @@ -234,7 +234,7 @@ public function createPort($params = array()) * @param array $portsParams Array of port creation parameters' arrays * @return \OpenCloud\Common\Collection\PaginatedIterator */ - public function createPorts($portsParams = array()) + public function createPorts(array $portsParams = array()) { // Form URL $url = clone $this->getUrl(); From 52a2b9ecf07ff1da5dd901609f7eff1b4ed2e444 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 06:44:04 -0800 Subject: [PATCH 166/835] Clearly delimiting variable used to determine object property name. --- lib/OpenCloud/Networking/Service.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 527e75f28..ee961af04 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -77,7 +77,7 @@ public function createNetworks(array $networksParams = array()) foreach ($networksParams as $networkParams) { $network = $this->network(); $network->populate($networkParams); - $networks[] = $network->createJson()->$singleNetworkJsonName; + $networks[] = $network->createJson()->{$singleNetworkJsonName}; } $json = json_encode(array( $networksJsonCollectionName => $networks @@ -88,7 +88,7 @@ public function createNetworks(array $networksParams = array()) // Parse the response into a collection of created networks $responseJson = Formatter::decode($response); - $createdNetworksJson = $responseJson->$networksJsonCollectionName; + $createdNetworksJson = $responseJson->{$networksJsonCollectionName}; // Return collection of created networks return $this->collection('Network', $url, $this, $createdNetworksJson); @@ -162,7 +162,7 @@ public function createSubnets(array $subnetsParams = array()) foreach ($subnetsParams as $subnetParams) { $subnet = $this->subnet(); $subnet->populate($subnetParams); - $subnets[] = $subnet->createJson()->$singleSubnetJsonName; + $subnets[] = $subnet->createJson()->{$singleSubnetJsonName}; } $json = json_encode(array( $subnetsJsonCollectionName => $subnets @@ -173,7 +173,7 @@ public function createSubnets(array $subnetsParams = array()) // Parse the response into a collection of created subnets $responseJson = Formatter::decode($response); - $createdSubnetsJson = $responseJson->$subnetsJsonCollectionName; + $createdSubnetsJson = $responseJson->{$subnetsJsonCollectionName}; // Return collection of created subnets return $this->collection('Subnet', $url, $this, $createdSubnetsJson); @@ -247,7 +247,7 @@ public function createPorts(array $portsParams = array()) foreach ($portsParams as $portParams) { $port = $this->port(); $port->populate($portParams); - $ports[] = $port->createJson()->$singlePortJsonName; + $ports[] = $port->createJson()->{$singlePortJsonName}; } $json = json_encode(array( $portsJsonCollectionName => $ports @@ -258,7 +258,7 @@ public function createPorts(array $portsParams = array()) // Parse the response into a collection of created ports $responseJson = Formatter::decode($response); - $createdPortsJson = $responseJson->$portsJsonCollectionName; + $createdPortsJson = $responseJson->{$portsJsonCollectionName}; // Return collection of created ports return $this->collection('Port', $url, $this, $createdPortsJson); From 9c87db0b2a649868857c8aa79aa390215ea628df Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 07:06:40 -0800 Subject: [PATCH 167/835] Fleshing out class header comments for Networking resources. --- lib/OpenCloud/Networking/Resource/Network.php | 7 ++++++- lib/OpenCloud/Networking/Resource/Port.php | 10 +++++++++- lib/OpenCloud/Networking/Resource/Subnet.php | 6 +++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 596ea6d83..4aedc52c1 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -20,7 +20,12 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * Class that represents a network. + * A network is an isolated virtual layer-2 broadcast domain that is typically + * reserved for the tenant who created it unless you configure the network to be + * shared. The network is the main entity in the Networking service. Ports ({@see + * \OpenCloud\Networking\Resource\Port}) and subnets ({@see + * \OpenCloud\Networking\Resource\Subnet}) are always associated with a network. + * * @see http://docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html#Network * * @package OpenCloud\Networking\Resource diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 6689bbb98..c3c1bf243 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -20,7 +20,15 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * Class that represents a port. + * A port represents a virtual switch port on a logical network switch, represented + * by {@see \OpenCloud\Networking\Resource\Network}. Virtual instances (such as + * servers created using the {@see \OpenCloud\Compute\Service}) attach their + * interfaces into ports. The port also defines the MAC address and the IP + * address(es) to be assigned to the interfaces plugged into them. When IP addresses + * are associated to a port, this also implies the port is associated with a {@see + * \OpenCloud\Networking\Resource\Subnet}, as the IP address is taken from the + * allocation pool for a specific subnet. + * * @see http://docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html#Port * * @package OpenCloud\Networking\Resource diff --git a/lib/OpenCloud/Networking/Resource/Subnet.php b/lib/OpenCloud/Networking/Resource/Subnet.php index bd454753d..8e52cc28a 100644 --- a/lib/OpenCloud/Networking/Resource/Subnet.php +++ b/lib/OpenCloud/Networking/Resource/Subnet.php @@ -20,7 +20,11 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * Class that represents a subnet. + * A subnet represents an IP address block that can be used to assign IP + * addresses to virtual instances (such as servers created using the {@see + * \OpenCloud\Compute\Service}. Each subnet must have a CIDR and must be + * associated with a network. + * * @see http://docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html#Subnet * * @package OpenCloud\Networking\Resource From b30e306352006351572ca0e4ffe64bdff018b296 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 14:39:26 -0800 Subject: [PATCH 168/835] Start of Networking user guide. --- docs/userguide/Networking/USERGUIDE.md | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 docs/userguide/Networking/USERGUIDE.md diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md new file mode 100644 index 000000000..1530757bf --- /dev/null +++ b/docs/userguide/Networking/USERGUIDE.md @@ -0,0 +1,105 @@ +# Complete User Guide for the Networking Service + +Networking is a service that you can use to create virtual networks and attach cloud devices +such as servers to these networks. + +This user guide will introduce you the entities in the Networking service — networks, subnets, and ports — as well as show you how to create and manage these entities. + +## Concepts + +To use the Networking service effectively, you should understand the following key concepts: + +* **Network**: A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. + +* **Subnet**: A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. + +* **Port**: A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + +## Prerequisites + +### Client +To use the Networking service, you must first instantiate a `OpenStack` or `Rackspace` client object. + +* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: + + ```php + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + ``` + +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: + + ```php + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +### Networking service + +All Networking operations are done via a _networking service object_. To +instantiate this object, call the `networkingService` method on the `$client` +object. This method takes two arguments: + +| Position | Description | Data type | Required? | Default value | Example value | +| -------- | ----------- | ----------| --------- | ------------- | ------------- | +| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | +| 2 | Cloud region | String | Yes | - | `DFW` | + + +```php +$region = ''; +$networkingService = $client->networkingService(null, $region); +``` + +Any networks, subnets, and ports created with this `$networkingService` instance will +be stored in the cloud region specified by `$region`. + +## Networks + +### Create a network + +### Create multiple networks + +### List networks + +### Get network + +### Update network + +### Delete network + +## Subnets + +### Create a subnet + +### Create multiple subnets + +### List subnets + +### Get subnet + +### Update subnet + +### Delete subnet + +## Ports + +### Create a port + +### Create multiple ports + +### List ports + +### Get port + +### Update port + +### Delete port \ No newline at end of file From 344ba57966fc6f5d01c2db94338517ad37149aaa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 14:42:51 -0800 Subject: [PATCH 169/835] Adding TOC. --- docs/userguide/Networking/USERGUIDE.md | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 1530757bf..489d5471b 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -5,6 +5,33 @@ such as servers to these networks. This user guide will introduce you the entities in the Networking service — networks, subnets, and ports — as well as show you how to create and manage these entities. +## Table of Contents + * [Concepts](#concepts) + * [Prerequisites](#prerequisites) + * [Client](#client) + * [Networking service](#networking-service) + * [Networks](#networks) + * [Create a network](#create-a-network) + * [Create multiple networks](#create-multiple-networks) + * [List networks](#list-networks) + * [Get network](#get-network) + * [Update network](#update-network) + * [Delete network](#delete-network) + * [Subnets](#subnets) + * [Create a subnet](#create-a-subnet) + * [Create multiple subnets](#create-multiple-subnets) + * [List subnets](#list-subnets) + * [Get subnet](#get-subnet) + * [Update subnet](#update-subnet) + * [Delete subnet](#delete-subnet) + * [Ports](#ports) + * [Create a port](#create-a-port) + * [Create multiple ports](#create-multiple-ports) + * [List ports](#list-ports) + * [Get port](#get-port) + * [Update port](#update-port) + * [Delete port](#delete-port) + ## Concepts To use the Networking service effectively, you should understand the following key concepts: From e4b6ec7c05051e9168bfb0e378a4638a2efb49fd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 16:30:29 -0800 Subject: [PATCH 170/835] Fixing network name to avoid confusion with VPN. --- samples/Networking/create-network.php | 2 +- samples/Networking/create-networks.php | 4 ++-- samples/Networking/update-network.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/Networking/create-network.php b/samples/Networking/create-network.php index b5dc0a931..02592bfb0 100644 --- a/samples/Networking/create-network.php +++ b/samples/Networking/create-network.php @@ -39,6 +39,6 @@ // 3. Create a network. $network = $networkingService->createNetwork(array( - 'name' => 'My virtual private network' + 'name' => 'My private backend network' )); /** @var $network OpenCloud\Networking\Resource\Network **/ diff --git a/samples/Networking/create-networks.php b/samples/Networking/create-networks.php index f452db2ee..6db031be0 100644 --- a/samples/Networking/create-networks.php +++ b/samples/Networking/create-networks.php @@ -40,10 +40,10 @@ // 3. Create multiple networks. $networks = $networkingService->createNetworks(array( array( - 'name' => 'My virtual private network #1' + 'name' => 'My private backend network #1' ), array( - 'name' => 'My virtual private network #2' + 'name' => 'My private backend network #2' ) )); diff --git a/samples/Networking/update-network.php b/samples/Networking/update-network.php index 7650be384..ec0a2ee50 100644 --- a/samples/Networking/update-network.php +++ b/samples/Networking/update-network.php @@ -43,5 +43,5 @@ // 4. Update network. $network->update(array( - 'name' => 'My updated virtual private network' + 'name' => 'My updated private backend network' )); From 8682a2c3854725587845a8430fb7cd63f1d6af24 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 17:09:53 -0800 Subject: [PATCH 171/835] Fleshing out user guide for Networks. --- docs/userguide/Networking/USERGUIDE.md | 90 ++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 489d5471b..b55783b56 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -91,18 +91,108 @@ be stored in the cloud region specified by `$region`. ## Networks +A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. + ### Create a network +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `backend_network` | +| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | +| `shared` | Specifies whether the network resource can be accessed by any tenant or not. | Boolean | No | `false` | `false` | +| `tenantId` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | `123456` | + +You can create a network as shown in the following example: + +```php +$network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' +)); +/** @var $network OpenCloud\Networking\Resource\Network **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] + ### Create multiple networks +This operation takes one parameter, an indexed array. Each element of this array must +be an associative array with the keys shown in [the table above](#create-a-network). + +You can create multiple networks as shown in the following example: + +```php +$networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) +)); + +foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-networks.php) ] + ### List networks +You can list all the networks to which you have access as shown in the following example: + +```php +$networks = $networkingService->listNetworks(); +foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-networks.php) ] + ### Get network +You can retrieve a specific network by using that network's ID, as shown in the following example: + +```php +$network = $networkingService->getNetwork(getenv('NETWORK_ID')); +/** @var $network OpenCloud\Networking\Resource\Network **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-network.php) ] + ### Update network +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `backend_network` | +| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | +| `shared` | Specifies whether the network resource can be accessed by any tenant or not. | Boolean | No | `false` | `false` | + +You can update a network as shown in the following example: + +```php +$network->update(array( + 'name' => 'My updated private backend network' +)); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/update-network.php) ] + ### Delete network +You can delete a network as shown in the following example: + +```php +$network->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-network.php) ] + ## Subnets ### Create a subnet From 2c6c25aa6dac41b2843b8cc83bc914501c3badb5 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 17:58:43 -0800 Subject: [PATCH 172/835] Using better sample names and IDs. --- docs/userguide/Networking/USERGUIDE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index b55783b56..8716c3c3e 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -99,7 +99,7 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `backend_network` | +| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `My private backend network` | | `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | | `shared` | Specifies whether the network resource can be accessed by any tenant or not. | Boolean | No | `false` | `false` | | `tenantId` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | `123456` | @@ -157,7 +157,7 @@ foreach ($networks as $network) { You can retrieve a specific network by using that network's ID, as shown in the following example: ```php -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); +$network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); /** @var $network OpenCloud\Networking\Resource\Network **/ ``` @@ -169,7 +169,7 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `backend_network` | +| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `My updated private backend network` | | `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | | `shared` | Specifies whether the network resource can be accessed by any tenant or not. | Boolean | No | `false` | `false` | From 37b24dbb74cfd9fd2b05a807e827ea03103efbbb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 17:59:15 -0800 Subject: [PATCH 173/835] Fleshing out user guide section on Subnets. --- docs/userguide/Networking/USERGUIDE.md | 101 +++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 8716c3c3e..af7d310aa 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -195,18 +195,119 @@ $network->delete(); ## Subnets +A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. + ### Create a subnet +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `networkId` | Network this subnet is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | +| `ipVersion` | IP version | Integer (`4` or `6`) | Yes | - | `4` | +| `cidr` | CIDR representing IP range for this subnet | String (CIDR) | Yes | - | `192.168.199.0/25` | +| `name` | Human-readable name for the subnet. Might not be unique. | String | No | `null` | `My subnet` | +| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | +| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | +| `allocationPools` | Sub-ranges of CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | `array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))` | +| `hostRoutes` | Routes that should be used by devices with IPs from this subnet (not including local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | +| `enableDhcp` | Specifies whether DHCP is enabled for this subnet or not | Boolean | No | `true` | `false` | +| `tenantId` | Owner of subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | `123456` | + +You can create a subnet as shown in the following example: + +```php +$subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' +)); +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-subnet.php) ] + ### Create multiple subnets +This operation takes one parameter, an indexed array. Each element of this array must +be an associative array with the keys shown in [the table above](#create-a-subnet). + +You can create multiple subnets as shown in the following example: + +```php +$subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) +)); + +foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-subnets.php) ] + ### List subnets +You can list all the subnets to which you have access as shown in the following example: + +```php +$subnets = $networkingService->listSubnets(); +foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-subnets.php) ] + ### Get subnet +You can retrieve a specific subnet by using that subnet's ID, as shown in the following example: + +```php +$subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-subnet.php) ] + ### Update subnet +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | Human-readable name for the subnet. Might not be unique. | String | No | `null` | `My subnet` | +| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | +| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | +| `hostRoutes` | Routes that should be used by devices with IPs from this subnet (not including local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | +| `enableDhcp` | Specifies whether DHCP is enabled for this subnet or not | Boolean | No | `true` | `false` | + +You can update a subnet as shown in the following example: + +```php +$subnet->update(array( + 'name' => 'My updated private backend subnet' +)); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/update-subnet.php) ] + ### Delete subnet +You can delete a subnet as shown in the following example: + +```php +$subnet->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-subnet.php) ] + ## Ports ### Create a port From b756939550cf3011747fd21143b5dba6bd0efb6e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 18:04:24 -0800 Subject: [PATCH 174/835] Trivial whitespace lint fixes. --- lib/OpenCloud/Networking/Resource/Network.php | 6 +++--- lib/OpenCloud/Networking/Resource/Port.php | 8 ++++---- lib/OpenCloud/Networking/Resource/Subnet.php | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 4aedc52c1..8363b4d89 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -20,12 +20,12 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * A network is an isolated virtual layer-2 broadcast domain that is typically - * reserved for the tenant who created it unless you configure the network to be + * A network is an isolated virtual layer-2 broadcast domain that is typically + * reserved for the tenant who created it unless you configure the network to be * shared. The network is the main entity in the Networking service. Ports ({@see * \OpenCloud\Networking\Resource\Port}) and subnets ({@see * \OpenCloud\Networking\Resource\Subnet}) are always associated with a network. - * + * * @see http://docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html#Network * * @package OpenCloud\Networking\Resource diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index c3c1bf243..484de675e 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -22,11 +22,11 @@ /** * A port represents a virtual switch port on a logical network switch, represented * by {@see \OpenCloud\Networking\Resource\Network}. Virtual instances (such as - * servers created using the {@see \OpenCloud\Compute\Service}) attach their - * interfaces into ports. The port also defines the MAC address and the IP + * servers created using the {@see \OpenCloud\Compute\Service}) attach their + * interfaces into ports. The port also defines the MAC address and the IP * address(es) to be assigned to the interfaces plugged into them. When IP addresses - * are associated to a port, this also implies the port is associated with a {@see - * \OpenCloud\Networking\Resource\Subnet}, as the IP address is taken from the + * are associated to a port, this also implies the port is associated with a {@see + * \OpenCloud\Networking\Resource\Subnet}, as the IP address is taken from the * allocation pool for a specific subnet. * * @see http://docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html#Port diff --git a/lib/OpenCloud/Networking/Resource/Subnet.php b/lib/OpenCloud/Networking/Resource/Subnet.php index 8e52cc28a..5b15220f9 100644 --- a/lib/OpenCloud/Networking/Resource/Subnet.php +++ b/lib/OpenCloud/Networking/Resource/Subnet.php @@ -20,8 +20,8 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * A subnet represents an IP address block that can be used to assign IP - * addresses to virtual instances (such as servers created using the {@see + * A subnet represents an IP address block that can be used to assign IP + * addresses to virtual instances (such as servers created using the {@see * \OpenCloud\Compute\Service}. Each subnet must have a CIDR and must be * associated with a network. * From cba317a75cdbdc2f6fa652e40a1c9a592e89e8b3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 18:28:25 -0800 Subject: [PATCH 175/835] Making examples values in subnet parameters table consistent with sample code. --- docs/userguide/Networking/USERGUIDE.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index af7d310aa..bd5b8151c 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -282,17 +282,24 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the subnet. Might not be unique. | String | No | `null` | `My subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | +| `name` | Human-readable name for the subnet. Might not be unique. | String | No | `null` | `My updated subnet` | +| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.62.155` | | `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `hostRoutes` | Routes that should be used by devices with IPs from this subnet (not including local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | +| `hostRoutes` | Routes that should be used by devices with IPs from this subnet (not including local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))` | | `enableDhcp` | Specifies whether DHCP is enabled for this subnet or not | Boolean | No | `true` | `false` | You can update a subnet as shown in the following example: ```php $subnet->update(array( - 'name' => 'My updated private backend subnet' + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' )); ``` From b8c4aeec82c8f2186704287049e3302f5125e34e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 18:29:21 -0800 Subject: [PATCH 176/835] Fleshing out user guide section on Ports. --- docs/userguide/Networking/USERGUIDE.md | 108 ++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index bd5b8151c..559eefc5b 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -317,14 +317,120 @@ $subnet->delete(); ## Ports +A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + ### Create a port +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `networkId` | Network this port is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | +| `name` | Human-readable name for the port. Might not be unique. | String | No | `null` | `My port` | +| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | +| `macAddress` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | `0F:5A:6F:70:E9:5C` | +| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))` | +| `deviceId` | Identifies the device (e.g. virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | +| `deviceOwner` | Identifies the entity (e.g. DHCP agent) using this port | String | No | `null` | `network:router_interface` | +| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | +| `tenantId` | Owner of port. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the port | `123456` | + +You can create a port as shown in the following example: + +```php +$port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' +)); +/** @var $port OpenCloud\Networking\Resource\Port **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-port.php) ] + ### Create multiple ports +This operation takes one parameter, an indexed array. Each element of this array must +be an associative array with the keys shown in [the table above](#create-a-port). + +You can create multiple ports as shown in the following example: + +```php +$ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) +)); + +foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-ports.php) ] + ### List ports +You can list all the ports to which you have access as shown in the following example: + +```php +$ports = $networkingService->listPorts(); +foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-ports.php) ] + ### Get port +You can retrieve a specific port by using that port's ID, as shown in the following example: + +```php +$port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); +/** @var $port OpenCloud\Networking\Resource\Port **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-port.php) ] + ### Update port -### Delete port \ No newline at end of file +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | Human-readable name for the port. Might not be unique. | String | No | `null` | `My port` | +| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | +| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))` | +| `deviceId` | Identifies the device (e.g. virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | +| `deviceOwner` | Identifies the entity (e.g. DHCP agent) using this port | String | No | `null` | `network:router_interface` | +| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | + +You can update a port as shown in the following example: + +```php +$port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) +)); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/update-port.php) ] + +### Delete port + +You can delete a port as shown in the following example: + +```php +$port->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] From 8d501424dbbd60887e0f7f299cd814fdfa8a4cdb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 18:33:40 -0800 Subject: [PATCH 177/835] Trivial whitespace fix. --- docs/userguide/Networking/USERGUIDE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 559eefc5b..bea5ea173 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -1,7 +1,6 @@ # Complete User Guide for the Networking Service -Networking is a service that you can use to create virtual networks and attach cloud devices -such as servers to these networks. +Networking is a service that you can use to create virtual networks and attach cloud devices such as servers to these networks. This user guide will introduce you the entities in the Networking service — networks, subnets, and ports — as well as show you how to create and manage these entities. From 915a5794b65d536f15b2a994c950eb71f69a3b30 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 18:33:59 -0800 Subject: [PATCH 178/835] Adding quickstart README. --- docs/userguide/Networking/README.md | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/userguide/Networking/README.md diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md new file mode 100644 index 000000000..ee1de981e --- /dev/null +++ b/docs/userguide/Networking/README.md @@ -0,0 +1,76 @@ +# Networking + +**Networking** is a service that you can use to create virtual networks and attach cloud devices such as servers to these networks. + +## Concepts + +## Concepts + +To use the Networking service effectively, you should understand the following key concepts: + +* **Network**: A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. + +* **Subnet**: A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. + +* **Port**: A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + + +## Getting started + +### 1. Instantiate an OpenStack or Rackspace client. + +To use the Networking service, you must first instantiate a `OpenStack` or `Rackspace` client object. + +* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: + + ```php + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + ``` + +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: + + ```php + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +### 2. Obtain an Networking service object from the client. +All Networking operations are done via an _networking service object_. To +instantiate this object, call the `networkingService` method on the `$client` +object. This method takes two arguments: + +| Position | Description | Data type | Required? | Default value | Example value | +| -------- | ----------- | ----------| --------- | ------------- | ------------- | +| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | +| 2 | Cloud region | String | Yes | - | `DFW` | + + +```php +$region = ''; +$networkingService = $client->networkingService(null, $region); +``` + +Any networks, subnets, and ports created with this `$networkingService` instance will +be stored in the cloud region specified by `$region`. + +### 3. Create a network. +```php +$network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' +)); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] + +## Next steps + +Once you have created a network, there is more you can do with it. See [complete user guide for networking](USERGUIDE.md). From 63d2e7222525f479241cd884bc7c8d88d7a32155 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 6 Nov 2014 18:48:40 -0800 Subject: [PATCH 179/835] Adding links to creation parameters' docs. --- lib/OpenCloud/Networking/Service.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index ee961af04..118ba4bde 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -48,7 +48,7 @@ public function network($id = null) /** * Creates a new Network and returns it. * - * @param array $params Network creation parameters + * @param array $params Network creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network * @return \OpenCloud\Networking\Resource\Network Object representing created network */ public function createNetwork(array $params = array()) @@ -133,7 +133,7 @@ public function subnet($id = null) /** * Creates a new Subnet and returns it. * - * @param array $params Subnet creation parameters + * @param array $params Subnet creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet * @return \OpenCloud\Networking\Resource\Subnet Object representing created subnet */ public function createSubnet(array $params = array()) @@ -218,7 +218,7 @@ public function port($id = null) /** * Creates a new Port and returns it. * - * @param array $params Port creation parameters + * @param array $params Port creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port * @return \OpenCloud\Networking\Resource\Port Object representing created port */ public function createPort(array $params = array()) From eb5580ab64aa04bc0f60e36e85f34237fea9f0d9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 7 Nov 2014 12:01:40 -0800 Subject: [PATCH 180/835] Do not consider currentMarker as Cloud Queues does not expose one per element but provides one in the nextUrl. --- lib/OpenCloud/Common/Collection/PaginatedIterator.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/OpenCloud/Common/Collection/PaginatedIterator.php b/lib/OpenCloud/Common/Collection/PaginatedIterator.php index a0628993d..4e9ba1dbb 100644 --- a/lib/OpenCloud/Common/Collection/PaginatedIterator.php +++ b/lib/OpenCloud/Common/Collection/PaginatedIterator.php @@ -181,10 +181,8 @@ public function valid() protected function shouldAppend() { - return $this->currentMarker && ( - $this->nextUrl || - $this->position % $this->getOption('limit.page') == 0 - ); + return ($this->nextUrl + || ($this->position % $this->getOption('limit.page') == 0)); } /** From e9b8557e858190b6b101f4823b8c3cf85ae1c298 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 8 Nov 2014 04:45:07 -0800 Subject: [PATCH 181/835] Updating performance flavor references to equivalent general flavors. --- .../Compute/create_server_from_bootable_volume.php | 2 +- ..._from_bootable_volume_delete_on_termination.php | 2 +- samples/Orchestration/lamp-updated.yaml | 14 +++++++------- samples/Orchestration/lamp.yaml | 14 +++++++------- .../Smoke/Resource/Orchestration/lamp-updated.yaml | 14 +++++++------- .../Smoke/Resource/Orchestration/lamp.yaml | 14 +++++++------- tests/OpenCloud/Smoke/Unit/Compute.php | 2 +- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/samples/Compute/create_server_from_bootable_volume.php b/samples/Compute/create_server_from_bootable_volume.php index 73ab9251e..3dcb8a811 100644 --- a/samples/Compute/create_server_from_bootable_volume.php +++ b/samples/Compute/create_server_from_bootable_volume.php @@ -24,7 +24,7 @@ * - RAX_API_KEY: Your Rackspace Cloud Account API Key, * - RAX_REGION: Rackspace Cloud region in which to create server; e.g.: DFW, * - RAX_BOOTABLE_VOLUME_ID: ID of bootable volume in Rackspace Cloud region, and - * - RAX_FLAVOR_ID: ID of a compute flavor (performance or higher) in the Rackspace Cloud; e.g.: performance1-1. + * - RAX_FLAVOR_ID: ID of a compute flavor (general or higher) in the Rackspace Cloud; e.g.: general1-1. * * - You have an existing keypair. For this script, it will be called 'my_keypair' * but this will change depending on what you called yours. diff --git a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php index f8fd85bbf..bebb72902 100644 --- a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php +++ b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php @@ -24,7 +24,7 @@ * - RAX_API_KEY: Your Rackspace Cloud Account API Key, * - RAX_REGION: Rackspace Cloud region in which to create server; e.g.: DFW, * - RAX_BOOTABLE_VOLUME_ID: ID of bootable volume in Rackspace Cloud region, and - * - RAX_FLAVOR_ID: ID of a compute flavor (performance or higher) in the Rackspace Cloud; e.g.: performance1-1. + * - RAX_FLAVOR_ID: ID of a compute flavor (general or higher) in the Rackspace Cloud; e.g.: general1-1. * * - You have an existing keypair. For this script, it will be called 'my_keypair' * but this will change depending on what you called yours. diff --git a/samples/Orchestration/lamp-updated.yaml b/samples/Orchestration/lamp-updated.yaml index feebf366c..35e65116c 100644 --- a/samples/Orchestration/lamp-updated.yaml +++ b/samples/Orchestration/lamp-updated.yaml @@ -55,15 +55,15 @@ parameters: Required: Rackspace Cloud Server flavor to use. The size is based on the amount of RAM for the provisioned server. type: string - default: 1 GB Performance + default: 1 GB General Purpose v1 constraints: - allowed_values: - - 1 GB Performance - - 2 GB Performance - - 4 GB Performance - - 8 GB Performance - - 15 GB Performance - - 30 GB Performance + - 1 GB General Purpose v1 + - 2 GB General Purpose v1 + - 4 GB General Purpose v1 + - 8 GB General Purpose v1 + - 15 GB I/O v1 + - 30 GB I/O v1 description: | Must be a valid Rackspace Cloud Server flavor for the region you have selected to deploy into. diff --git a/samples/Orchestration/lamp.yaml b/samples/Orchestration/lamp.yaml index d70b2cc4c..7a0c16b64 100644 --- a/samples/Orchestration/lamp.yaml +++ b/samples/Orchestration/lamp.yaml @@ -55,15 +55,15 @@ parameters: Required: Rackspace Cloud Server flavor to use. The size is based on the amount of RAM for the provisioned server. type: string - default: 1 GB Performance + default: 1 GB General Purpose v1 constraints: - allowed_values: - - 1 GB Performance - - 2 GB Performance - - 4 GB Performance - - 8 GB Performance - - 15 GB Performance - - 30 GB Performance + - 1 GB General Purpose v1 + - 2 GB General Purpose v1 + - 4 GB General Purpose v1 + - 8 GB General Purpose v1 + - 15 GB I/O v1 + - 30 GB I/O v1 description: | Must be a valid Rackspace Cloud Server flavor for the region you have selected to deploy into. diff --git a/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml index feebf366c..35e65116c 100644 --- a/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml +++ b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp-updated.yaml @@ -55,15 +55,15 @@ parameters: Required: Rackspace Cloud Server flavor to use. The size is based on the amount of RAM for the provisioned server. type: string - default: 1 GB Performance + default: 1 GB General Purpose v1 constraints: - allowed_values: - - 1 GB Performance - - 2 GB Performance - - 4 GB Performance - - 8 GB Performance - - 15 GB Performance - - 30 GB Performance + - 1 GB General Purpose v1 + - 2 GB General Purpose v1 + - 4 GB General Purpose v1 + - 8 GB General Purpose v1 + - 15 GB I/O v1 + - 30 GB I/O v1 description: | Must be a valid Rackspace Cloud Server flavor for the region you have selected to deploy into. diff --git a/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml index d70b2cc4c..7a0c16b64 100644 --- a/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml +++ b/tests/OpenCloud/Smoke/Resource/Orchestration/lamp.yaml @@ -55,15 +55,15 @@ parameters: Required: Rackspace Cloud Server flavor to use. The size is based on the amount of RAM for the provisioned server. type: string - default: 1 GB Performance + default: 1 GB General Purpose v1 constraints: - allowed_values: - - 1 GB Performance - - 2 GB Performance - - 4 GB Performance - - 8 GB Performance - - 15 GB Performance - - 30 GB Performance + - 1 GB General Purpose v1 + - 2 GB General Purpose v1 + - 4 GB General Purpose v1 + - 8 GB General Purpose v1 + - 15 GB I/O v1 + - 30 GB I/O v1 description: | Must be a valid Rackspace Cloud Server flavor for the region you have selected to deploy into. diff --git a/tests/OpenCloud/Smoke/Unit/Compute.php b/tests/OpenCloud/Smoke/Unit/Compute.php index 78083392a..f5c465fc5 100644 --- a/tests/OpenCloud/Smoke/Unit/Compute.php +++ b/tests/OpenCloud/Smoke/Unit/Compute.php @@ -35,7 +35,7 @@ class Compute extends AbstractUnit implements UnitInterface const SERVER_NAME = 'FooServer'; const SNAPSHOT_NAME = 'FooSnapshot'; - const FLAVOR = 'performance1-2'; + const FLAVOR = 'general1-2'; const IMAGE = "046832f9-4549-4b38-a903-11acecac8cb9"; public function setupService() From 7123292e642e2e098e162f53f66955304cc904d2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 8 Nov 2014 19:00:42 -0800 Subject: [PATCH 182/835] Removing non-Rackspace operations from smoke tests. --- tests/OpenCloud/Smoke/Unit/Compute.php | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Compute.php b/tests/OpenCloud/Smoke/Unit/Compute.php index f5c465fc5..d7c96fe97 100644 --- a/tests/OpenCloud/Smoke/Unit/Compute.php +++ b/tests/OpenCloud/Smoke/Unit/Compute.php @@ -165,30 +165,6 @@ public function main() return false; } - sleep(3); - - // Suspend - $this->step('Suspend the server'); - $server->suspend(); - $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); - - if ($server->status() == 'ERROR') { - $this->stepInfo("Server suspension failed with ERROR\n"); - return false; - } - - sleep(3); - - // Resume - $this->step('Resume the server'); - $server->resume(); - $server->waitFor('ACTIVE', 120, $this->getWaiterCallback()); - - if ($server->status() == 'ERROR') { - $this->stepInfo("Server resuming failed with ERROR\n"); - return false; - } - sleep(3); // Attach volume From 47f595e5277f6687812199e6d9b14940c94d0670 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 10 Nov 2014 17:26:35 -0800 Subject: [PATCH 183/835] Revert "Do not consider currentMarker as Cloud Queues does not expose one per element but provides one in the nextUrl." This reverts commit eb5580ab64aa04bc0f60e36e85f34237fea9f0d9. --- lib/OpenCloud/Common/Collection/PaginatedIterator.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Common/Collection/PaginatedIterator.php b/lib/OpenCloud/Common/Collection/PaginatedIterator.php index 4e9ba1dbb..a0628993d 100644 --- a/lib/OpenCloud/Common/Collection/PaginatedIterator.php +++ b/lib/OpenCloud/Common/Collection/PaginatedIterator.php @@ -181,8 +181,10 @@ public function valid() protected function shouldAppend() { - return ($this->nextUrl - || ($this->position % $this->getOption('limit.page') == 0)); + return $this->currentMarker && ( + $this->nextUrl || + $this->position % $this->getOption('limit.page') == 0 + ); } /** From 0452fe5fa3b76b3e3cb5a205cb92c500d82e6d11 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 10 Nov 2014 18:45:30 -0800 Subject: [PATCH 184/835] Create and use custom MessageIterator for iterating over queue messages. --- .../Queues/Collection/MessageIterator.php | 29 +++++++++++++ lib/OpenCloud/Queues/Resource/Queue.php | 5 ++- tests/OpenCloud/Smoke/Unit/Queues.php | 41 ++++++++++++------- .../Tests/Queues/Resource/QueueTest.php | 2 +- 4 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 lib/OpenCloud/Queues/Collection/MessageIterator.php diff --git a/lib/OpenCloud/Queues/Collection/MessageIterator.php b/lib/OpenCloud/Queues/Collection/MessageIterator.php new file mode 100644 index 000000000..a970fd54b --- /dev/null +++ b/lib/OpenCloud/Queues/Collection/MessageIterator.php @@ -0,0 +1,29 @@ +nextUrl || + ( $this->position % $this->getOption('limit.page') == 0 ); + } +} diff --git a/lib/OpenCloud/Queues/Resource/Queue.php b/lib/OpenCloud/Queues/Resource/Queue.php index 0a65723e9..b6817e0d6 100644 --- a/lib/OpenCloud/Queues/Resource/Queue.php +++ b/lib/OpenCloud/Queues/Resource/Queue.php @@ -22,6 +22,7 @@ use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Resource\PersistentResource; use OpenCloud\Queues\Exception; +use OpenCloud\Queues\Collection\MessageIterator; use OpenCloud\Common\Metadata; /** @@ -250,7 +251,7 @@ public function createMessages(array $messages) * messages as well as unclaimed messages. If not specified, defaults * to FALSE (i.e. only unclaimed messages are returned). * - * @return Collection + * @return \OpenCloud\Queues\Collection\MessageIterator */ public function listMessages(array $options = array()) { @@ -276,7 +277,7 @@ public function listMessages(array $options = array()) 'limit.page' => 10 ); - return PaginatedIterator::factory($this, $options); + return MessageIterator::factory($this, $options); } /** diff --git a/tests/OpenCloud/Smoke/Unit/Queues.php b/tests/OpenCloud/Smoke/Unit/Queues.php index 5079ed2cc..635f0ba96 100644 --- a/tests/OpenCloud/Smoke/Unit/Queues.php +++ b/tests/OpenCloud/Smoke/Unit/Queues.php @@ -17,6 +17,8 @@ namespace OpenCloud\Smoke\Unit; +use OpenCloud\Queues\Resource\Queue; + class Queues extends AbstractUnit implements UnitInterface { const QUEUE_NAME = 'test_queue'; @@ -71,6 +73,7 @@ public function doMessageBlock() $this->step('Messages'); // post + $numMessagesCreated = 0; $this->stepInfo('Create messages for queue %s', $this->queue->getName()); $this->queue->createMessage(array( 'body' => (object) array( @@ -78,26 +81,36 @@ public function doMessageBlock() ), 'ttl' => 300 )); + ++$numMessagesCreated; + + for ($creationBatch = 0; $creationBatch < 3; ++$creationBatch) { + $messages = array(); + for ($messageIndex = 0; $messageIndex < Queue::MAX_POST_MESSAGES; ++$messageIndex) { + $messages[] = array( + 'body' => (object) array('message_number' => ($creationBatch * Queue::MAX_POST_MESSAGES) + $messageIndex + 1), + 'ttl' => mt_rand(300, 600) + ); + ++$numMessagesCreated; + } + $this->queue->createMessages($messages); + } - $this->queue->createMessages(array( - array( - 'body' => (object) array('foo' => 'bar'), - 'ttl' => 700 - ), - array( - 'body' => (object) array('baz' => 'lol'), - 'ttl' => 600 - ) - )); - - // list + // list ( 'echo' => true is needed to list client's own messages) $step = $this->stepInfo('List messages for queue %s', $this->queue->getName()); - $messages = $this->queue->listMessages(); + $messages = $this->queue->listMessages(array( + 'echo' => true + )); $ids = array(); + $numMessagesListed = 0; + $step->stepInfo("%-30s | %-40s", "Message ID", "Message body"); + $step->stepInfo("%-30s | %-40s", str_repeat("-", 30), str_repeat("-", 40)); foreach ($messages as $message) { - $step->stepInfo($message->getId()); + $step->stepInfo("%-30s | %-40s", $message->getId(), json_encode($message->getBody())); $ids[] = $message->getId(); + ++$numMessagesListed; } + $this->stepInfo("Number of messages to be listed: " . $numMessagesCreated); + $this->stepInfo("Number of messages actually listed: " . $numMessagesListed); array_pop($ids); } diff --git a/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php b/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php index 5237d48a9..5f8e4206d 100644 --- a/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php +++ b/tests/OpenCloud/Tests/Queues/Resource/QueueTest.php @@ -122,7 +122,7 @@ public function test_Get_Message() public function test_List_Message() { $this->assertInstanceOf( - self::COLLECTION_CLASS, + 'OpenCloud\Queues\Collection\MessageIterator', $this->queue->setName('foo')->listMessages(array( 'ids' => array(100, 901, 58) )) From c6e31f5d2a0d03bcbd17a3c454f399cf8f3430b0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 10 Nov 2014 18:55:54 -0800 Subject: [PATCH 185/835] Making the PSR-2 lint checker happy. --- lib/OpenCloud/Queues/Collection/MessageIterator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Queues/Collection/MessageIterator.php b/lib/OpenCloud/Queues/Collection/MessageIterator.php index a970fd54b..0f4140936 100644 --- a/lib/OpenCloud/Queues/Collection/MessageIterator.php +++ b/lib/OpenCloud/Queues/Collection/MessageIterator.php @@ -24,6 +24,6 @@ class MessageIterator extends PaginatedIterator protected function shouldAppend() { return $this->nextUrl || - ( $this->position % $this->getOption('limit.page') == 0 ); + ($this->position % $this->getOption('limit.page') == 0); } } From 2b34bb774b0161039897122157a57e77c533a90a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 11 Nov 2014 04:35:30 -0800 Subject: [PATCH 186/835] Ensuring numMessagesListed == numMessagesCreated using PHPUnit. --- CONTRIBUTING.md | 2 +- composer.json | 1 + tests/OpenCloud/Smoke/Unit/AbstractUnit.php | 2 +- tests/OpenCloud/Smoke/Unit/Queues.php | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 381746f2d..f02a508da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ Test Instructions ### To run unit tests: ```bash -phpunit +vendor/bin/phpunit ``` ### To run the full suite of acceptance tests: diff --git a/composer.json b/composer.json index c149f3f33..eed385e82 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "psr/log": "~1.0" }, "require-dev" : { + "phpunit/phpunit": "4.3.*", "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", diff --git a/tests/OpenCloud/Smoke/Unit/AbstractUnit.php b/tests/OpenCloud/Smoke/Unit/AbstractUnit.php index bdf9d2e58..08896aefc 100644 --- a/tests/OpenCloud/Smoke/Unit/AbstractUnit.php +++ b/tests/OpenCloud/Smoke/Unit/AbstractUnit.php @@ -28,7 +28,7 @@ * * @link */ -abstract class AbstractUnit +abstract class AbstractUnit extends \PHPUnit_Framework_TestCase { /** * The credentials cache filename. diff --git a/tests/OpenCloud/Smoke/Unit/Queues.php b/tests/OpenCloud/Smoke/Unit/Queues.php index 635f0ba96..326e726ae 100644 --- a/tests/OpenCloud/Smoke/Unit/Queues.php +++ b/tests/OpenCloud/Smoke/Unit/Queues.php @@ -111,6 +111,7 @@ public function doMessageBlock() } $this->stepInfo("Number of messages to be listed: " . $numMessagesCreated); $this->stepInfo("Number of messages actually listed: " . $numMessagesListed); + $this->assertEquals($numMessagesCreated, $numMessagesListed); array_pop($ids); } From dda0479892d57c66604b9785c0a8628ee281662f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 13 Nov 2014 14:01:08 +0100 Subject: [PATCH 187/835] Adding TOC for userguide --- docs/userguide/README.md | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 docs/userguide/README.md diff --git a/docs/userguide/README.md b/docs/userguide/README.md new file mode 100644 index 000000000..995412d8c --- /dev/null +++ b/docs/userguide/README.md @@ -0,0 +1,76 @@ +# php-opencloud documentation + +## Table of contents + +- Auto Scale + - [Groups](/docs/userguide/Autoscale/Groups.md) + - [Policies](/docs/userguide/Autoscale/Policies.md) + - [Webhooks](/docs/userguide/Autoscale/Webhooks.md) + +- Cloud Monitoring + - [Agents](/docs/userguide/CloudMonitoring/Agents.md) + - [Alarms](/docs/userguide/CloudMonitoring/Alarms.md) + - [Changelogs](/docs/userguide/CloudMonitoring/Changelogs.md) + - [Checks](/docs/userguide/CloudMonitoring/Checks.md) + - [Entities](/docs/userguide/CloudMonitoring/Entities.md) + - [Metrics](/docs/userguide/CloudMonitoring/Metrics.md) + - [Notifications](/docs/userguide/CloudMonitoring/Notifications.md) + - [Views](/docs/userguide/CloudMonitoring/Views.md) + - [Zones](/docs/userguide/CloudMonitoring/Zones.md) + +- Compute + - [Servers](/docs/userguide/Compute/Servers.md) + - [Images](/docs/userguide/Compute/Images.md) + - [Flavors](/docs/userguide/flavors.md) + - [Keypairs](/docs/userguide/Compute/Keypairs.md) + +- DNS + - [Records](/docs/userguide/DNS/Records.md) + - [Domains](/docs/userguide/DNS/Domains.md) + - [Reverse DNS](/docs/userguide/DNS/Reverse-DNS.md) + +- Databases + - [Getting Started](/docs/userguide/Database/README.md) + +- Identity + - [Users](/docs/userguide/Identity/Users.md) + - [Roles](/docs/userguide/Identity/Roles.md) + - [Tokens](/docs/userguide/Identity/Tokens.md) + - [Tenants](/docs/userguide/Identity/Tenants.md) + +- Images + - [Images](/docs/userguide/Image/Images.md) + - [Sharing](/docs/userguide/Image/Sharing.md) + - [Tagging](/docs/userguide/Image/Tags.md) + +- Load Balancers + - [Getting Started](/docs/userguide/LoadBalancer/README.md) + - [Full userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) + +- Networking + - [Getting Started](/docs/userguide/Networking/README.md) + - [Full userguide](/docs/userguide/Networking/USERGUIDE.md) + +- Object Store + - [Objects](/docs/userguide/ObjectStore/Storage/Object.md) + - [Containers](/docs/userguide/ObjectStore/Storage/Container.md) + - [Migrating containers across regions](/docs/userguide/ObjectStore/Storage/Migrating.md) + - [Access control](/docs/userguide/ObjectStore/Access.md) + - [Accounts](/docs/userguide/ObjectStore/Account.md) + - [CDN-enabled containers](/docs/userguide/ObjectStore/CDN/Container.md) + - [Purging objects from the CDN](/docs/userguide/ObjectStore/CDN/Object.md) + +- Orchestration + - [Getting Started](/docs/userguide/Orchestration/README.md) + - [Full userguide](/docs/userguide/Orchestration/USERGUIDE.md) + +- Queues + - [Queues](/docs/userguide/Queues/Queue.md) + - [Messages](/docs/userguide/Queues/Message.md) + - [Claims](/docs/userguide/Queues/Claim.md) + +- Miscelleneous + - [Client setup](/docs/userguide/Clients.md) + - [Debugging](/docs/userguide/Debugging.md) + - [Iterators](/docs/userguide/Iterators.md) + - [Caching credentials](/docs/userguide/caching-credentials.md) From d0650c08b1a59d878294fb6d022680e0cc24a892 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 13 Nov 2014 14:03:30 +0100 Subject: [PATCH 188/835] Fiddling with layout --- docs/userguide/README.md | 124 +++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 995412d8c..341fcd314 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -1,76 +1,72 @@ -# php-opencloud documentation +## Auto Scale +- [Groups](/docs/userguide/Autoscale/Groups.md) +- [Policies](/docs/userguide/Autoscale/Policies.md) +- [Webhooks](/docs/userguide/Autoscale/Webhooks.md) -## Table of contents +## Cloud Monitoring +- [Agents](/docs/userguide/CloudMonitoring/Agents.md) +- [Alarms](/docs/userguide/CloudMonitoring/Alarms.md) +- [Changelogs](/docs/userguide/CloudMonitoring/Changelogs.md) +- [Checks](/docs/userguide/CloudMonitoring/Checks.md) +- [Entities](/docs/userguide/CloudMonitoring/Entities.md) +- [Metrics](/docs/userguide/CloudMonitoring/Metrics.md) +- [Notifications](/docs/userguide/CloudMonitoring/Notifications.md) +- [Views](/docs/userguide/CloudMonitoring/Views.md) +- [Zones](/docs/userguide/CloudMonitoring/Zones.md) -- Auto Scale - - [Groups](/docs/userguide/Autoscale/Groups.md) - - [Policies](/docs/userguide/Autoscale/Policies.md) - - [Webhooks](/docs/userguide/Autoscale/Webhooks.md) +## Compute +- [Servers](/docs/userguide/Compute/Servers.md) +- [Images](/docs/userguide/Compute/Images.md) +- [Flavors](/docs/userguide/flavors.md) +- [Keypairs](/docs/userguide/Compute/Keypairs.md) -- Cloud Monitoring - - [Agents](/docs/userguide/CloudMonitoring/Agents.md) - - [Alarms](/docs/userguide/CloudMonitoring/Alarms.md) - - [Changelogs](/docs/userguide/CloudMonitoring/Changelogs.md) - - [Checks](/docs/userguide/CloudMonitoring/Checks.md) - - [Entities](/docs/userguide/CloudMonitoring/Entities.md) - - [Metrics](/docs/userguide/CloudMonitoring/Metrics.md) - - [Notifications](/docs/userguide/CloudMonitoring/Notifications.md) - - [Views](/docs/userguide/CloudMonitoring/Views.md) - - [Zones](/docs/userguide/CloudMonitoring/Zones.md) +## DNS +- [Records](/docs/userguide/DNS/Records.md) +- [Domains](/docs/userguide/DNS/Domains.md) +- [Reverse DNS](/docs/userguide/DNS/Reverse-DNS.md) -- Compute - - [Servers](/docs/userguide/Compute/Servers.md) - - [Images](/docs/userguide/Compute/Images.md) - - [Flavors](/docs/userguide/flavors.md) - - [Keypairs](/docs/userguide/Compute/Keypairs.md) +## Databases +- [Getting Started](/docs/userguide/Database/README.md) -- DNS - - [Records](/docs/userguide/DNS/Records.md) - - [Domains](/docs/userguide/DNS/Domains.md) - - [Reverse DNS](/docs/userguide/DNS/Reverse-DNS.md) +## Identity +- [Users](/docs/userguide/Identity/Users.md) +- [Roles](/docs/userguide/Identity/Roles.md) +- [Tokens](/docs/userguide/Identity/Tokens.md) +- [Tenants](/docs/userguide/Identity/Tenants.md) -- Databases - - [Getting Started](/docs/userguide/Database/README.md) +## Images +- [Images](/docs/userguide/Image/Images.md) +- [Sharing](/docs/userguide/Image/Sharing.md) +- [Tagging](/docs/userguide/Image/Tags.md) -- Identity - - [Users](/docs/userguide/Identity/Users.md) - - [Roles](/docs/userguide/Identity/Roles.md) - - [Tokens](/docs/userguide/Identity/Tokens.md) - - [Tenants](/docs/userguide/Identity/Tenants.md) +## Load Balancers +- [Getting Started](/docs/userguide/LoadBalancer/README.md) +- [Full userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) -- Images - - [Images](/docs/userguide/Image/Images.md) - - [Sharing](/docs/userguide/Image/Sharing.md) - - [Tagging](/docs/userguide/Image/Tags.md) +## Networking +- [Getting Started](/docs/userguide/Networking/README.md) +- [Full userguide](/docs/userguide/Networking/USERGUIDE.md) -- Load Balancers - - [Getting Started](/docs/userguide/LoadBalancer/README.md) - - [Full userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) +## Object Store +- [Objects](/docs/userguide/ObjectStore/Storage/Object.md) +- [Containers](/docs/userguide/ObjectStore/Storage/Container.md) +- [Migrating containers across regions](/docs/userguide/ObjectStore/Storage/Migrating.md) +- [Access control](/docs/userguide/ObjectStore/Access.md) +- [Accounts](/docs/userguide/ObjectStore/Account.md) +- [CDN-enabled containers](/docs/userguide/ObjectStore/CDN/Container.md) +- [Purging objects from the CDN](/docs/userguide/ObjectStore/CDN/Object.md) -- Networking - - [Getting Started](/docs/userguide/Networking/README.md) - - [Full userguide](/docs/userguide/Networking/USERGUIDE.md) +## Orchestration +- [Getting Started](/docs/userguide/Orchestration/README.md) +- [Full userguide](/docs/userguide/Orchestration/USERGUIDE.md) -- Object Store - - [Objects](/docs/userguide/ObjectStore/Storage/Object.md) - - [Containers](/docs/userguide/ObjectStore/Storage/Container.md) - - [Migrating containers across regions](/docs/userguide/ObjectStore/Storage/Migrating.md) - - [Access control](/docs/userguide/ObjectStore/Access.md) - - [Accounts](/docs/userguide/ObjectStore/Account.md) - - [CDN-enabled containers](/docs/userguide/ObjectStore/CDN/Container.md) - - [Purging objects from the CDN](/docs/userguide/ObjectStore/CDN/Object.md) +## Queues +- [Queues](/docs/userguide/Queues/Queue.md) +- [Messages](/docs/userguide/Queues/Message.md) +- [Claims](/docs/userguide/Queues/Claim.md) -- Orchestration - - [Getting Started](/docs/userguide/Orchestration/README.md) - - [Full userguide](/docs/userguide/Orchestration/USERGUIDE.md) - -- Queues - - [Queues](/docs/userguide/Queues/Queue.md) - - [Messages](/docs/userguide/Queues/Message.md) - - [Claims](/docs/userguide/Queues/Claim.md) - -- Miscelleneous - - [Client setup](/docs/userguide/Clients.md) - - [Debugging](/docs/userguide/Debugging.md) - - [Iterators](/docs/userguide/Iterators.md) - - [Caching credentials](/docs/userguide/caching-credentials.md) +## Miscelleneous +- [Client setup](/docs/userguide/Clients.md) +- [Debugging](/docs/userguide/Debugging.md) +- [Iterators](/docs/userguide/Iterators.md) +- [Caching credentials](/docs/userguide/caching-credentials.md) From 9cffd004abd090c9d58fe80175fa329e73efcacb Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 13 Nov 2014 14:04:55 +0100 Subject: [PATCH 189/835] Adding h1 --- docs/userguide/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 341fcd314..f0f373cac 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -1,3 +1,5 @@ +# Table of contents + ## Auto Scale - [Groups](/docs/userguide/Autoscale/Groups.md) - [Policies](/docs/userguide/Autoscale/Policies.md) From 1579a8f48a98ec64422dd5088c57347127fa1f0b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 13 Nov 2014 14:20:08 +0100 Subject: [PATCH 190/835] [ci skip] s/Full/Complete/ --- docs/userguide/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 341fcd314..9c9a97752 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -1,3 +1,5 @@ +# Table of contents + ## Auto Scale - [Groups](/docs/userguide/Autoscale/Groups.md) - [Policies](/docs/userguide/Autoscale/Policies.md) @@ -41,11 +43,11 @@ ## Load Balancers - [Getting Started](/docs/userguide/LoadBalancer/README.md) -- [Full userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) +- [Complete userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) ## Networking - [Getting Started](/docs/userguide/Networking/README.md) -- [Full userguide](/docs/userguide/Networking/USERGUIDE.md) +- [Complete userguide](/docs/userguide/Networking/USERGUIDE.md) ## Object Store - [Objects](/docs/userguide/ObjectStore/Storage/Object.md) @@ -58,7 +60,7 @@ ## Orchestration - [Getting Started](/docs/userguide/Orchestration/README.md) -- [Full userguide](/docs/userguide/Orchestration/USERGUIDE.md) +- [Complete userguide](/docs/userguide/Orchestration/USERGUIDE.md) ## Queues - [Queues](/docs/userguide/Queues/Queue.md) From 84cb755f6b73b30fa8c2eec6db785e9de86e023d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 13 Nov 2014 10:25:12 -0800 Subject: [PATCH 191/835] Updating service name to match status quo. --- tests/OpenCloud/Smoke/Unit/Networking.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index a2753e071..74f39fac7 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -32,8 +32,7 @@ class Networking extends AbstractUnit implements UnitInterface public function setupService() { - // return $this->getConnection()->networkingService('cloudNetworks', Utils::getRegion()); - return $this->getConnection()->networkingService('cloudNetworksPreprod', Utils::getRegion()); + return $this->getConnection()->networkingService('cloudNetworks', Utils::getRegion()); } public function main() From 1a5f21823afeb9e7d3c4bc9dd694f8cc59223bfd Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 14 Nov 2014 14:00:42 +0100 Subject: [PATCH 192/835] Adding HTTPS redirect as a LB create option --- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 3c6454cee..69cea1699 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -174,7 +174,8 @@ class LoadBalancer extends PersistentResource implements HasPtrRecordsInterface 'connectionLogging', 'connectionThrottle', 'healthMonitor', - 'sessionPersistence' + 'sessionPersistence', + 'httpsRedirect' ); /** From 0c0d033a718f286e90432fe366d67844ef3bd5fa Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 17 Nov 2014 11:32:27 +0100 Subject: [PATCH 193/835] Fixing import issue - tenant IDs can be strings --- lib/OpenCloud/OpenStack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 0ab054eef..b1cf7dbcf 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -196,7 +196,7 @@ public function setTenant($tenant) { $identity = IdentityService::factory($this); - if (is_numeric($tenant)) { + if (is_numeric($tenant) || is_string($tenant)) { if (!$this->tenant) { $this->setTenantObject($identity->resource('Tenant')); } From 1a0b8bf5cd29351116dbe1e20964327ae4aae534 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 17 Nov 2014 13:14:41 +0100 Subject: [PATCH 194/835] Adding identity samples --- samples/Identity/add_user.php | 45 +++++++++++++++++++++++ samples/Identity/authenticate_token.php | 36 +++++++++++++++++++ samples/Identity/delete_user.php | 47 +++++++++++++++++++++++++ samples/Identity/list_users.php | 42 ++++++++++++++++++++++ samples/Identity/reset_api_key.php | 43 ++++++++++++++++++++++ 5 files changed, 213 insertions(+) create mode 100644 samples/Identity/add_user.php create mode 100644 samples/Identity/authenticate_token.php create mode 100644 samples/Identity/delete_user.php create mode 100644 samples/Identity/list_users.php create mode 100644 samples/Identity/reset_api_key.php diff --git a/samples/Identity/add_user.php b/samples/Identity/add_user.php new file mode 100644 index 000000000..b8738930a --- /dev/null +++ b/samples/Identity/add_user.php @@ -0,0 +1,45 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Create user +$user = $service->createUser(array( + 'username' => '{username}', // replace username + 'email' => '{email}', // replace email address + 'enabled' => true, +)); + +// If you do not provide a "password" key in the createUser operation, the API +// will automatically generate you one. If that's the case, you will need to +// retrieve the new password and save it somewhere. +echo $user->getPassword(); diff --git a/samples/Identity/authenticate_token.php b/samples/Identity/authenticate_token.php new file mode 100644 index 000000000..a56dd83c7 --- /dev/null +++ b/samples/Identity/authenticate_token.php @@ -0,0 +1,36 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Authenticate with the above credentials +$client->authenticate(); + +// Retrieve token ID +echo $client->getToken(); diff --git a/samples/Identity/delete_user.php b/samples/Identity/delete_user.php new file mode 100644 index 000000000..1715aeffc --- /dev/null +++ b/samples/Identity/delete_user.php @@ -0,0 +1,47 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Retrieve existing user, either by name: +$user = $service->getUser('{username}'); + +// by ID: +$user = $service->getUser('{userId}', UserConst::MODE_ID); + +// or by email: +$user = $service->getUser('{email}', UserConst::MODE_EMAIL); + + +// And delete them... +$user->delete(); diff --git a/samples/Identity/list_users.php b/samples/Identity/list_users.php new file mode 100644 index 000000000..64b84d8f7 --- /dev/null +++ b/samples/Identity/list_users.php @@ -0,0 +1,42 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Get a collection of users +$users = $service->getUsers(); + +// Traverse the collection +foreach ($users as $user) { + printf("User ID: %s, Name: %s\n", $user->getId(), $user->getUsername()); +} diff --git a/samples/Identity/reset_api_key.php b/samples/Identity/reset_api_key.php new file mode 100644 index 000000000..73adaf55f --- /dev/null +++ b/samples/Identity/reset_api_key.php @@ -0,0 +1,43 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Get user by their ID +$user = $service->getUser('{userId}'); + +// Reset +$user->resetApiKey(); + +// Show the new API key +echo $user->getApiKey(); From 229ccbe9efec143214d65f56bf212e85cccd1bee Mon Sep 17 00:00:00 2001 From: Glen Campbell Date: Mon, 17 Nov 2014 09:38:35 -0600 Subject: [PATCH 195/835] Fixed documentation (it's "internalURL" not "privateURL") --- lib/OpenCloud/Common/Service/CatalogService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index f7baa6977..8000d1615 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -44,7 +44,7 @@ abstract class CatalogService extends AbstractService private $region; /** - * @var string Either 'publicURL' or 'privateURL'. + * @var string Either 'publicURL' or 'internalURL'. */ private $urlType; @@ -66,7 +66,7 @@ abstract class CatalogService extends AbstractService * @param string $type Service type (e.g. 'compute') * @param string $name Service name (e.g. 'cloudServersOpenStack') * @param string $region Service region (e.g. 'DFW', 'ORD', 'IAD', 'LON', 'SYD' or 'HKG') - * @param string $urlType Either 'publicURL' or 'privateURL' + * @param string $urlType Either 'publicURL' or 'internalURL' */ public function __construct(ClientInterface $client, $type = null, $name = null, $region = null, $urlType = null) { From f5e081e7b02cc3456297252550d7bb8ce237e43b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 11:07:20 +0100 Subject: [PATCH 196/835] Fixing docblock type --- lib/OpenCloud/OpenStack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index b1cf7dbcf..7ced9ced2 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -189,7 +189,7 @@ public function getExpiration() * and sets this property accordingly. For any other data type, it assumes you want to populate the Tenant object. * This ambiguity arises due to backwards compatibility. * - * @param string $tenant + * @param mixed $tenant * @return $this */ public function setTenant($tenant) From 510915399feb65b9c66a607a7c3d4a680a9fa9a9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 11:33:09 +0100 Subject: [PATCH 197/835] Fixing non-PSR2 indent --- samples/Identity/list_users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Identity/list_users.php b/samples/Identity/list_users.php index 64b84d8f7..2cf4fbdbb 100644 --- a/samples/Identity/list_users.php +++ b/samples/Identity/list_users.php @@ -38,5 +38,5 @@ // Traverse the collection foreach ($users as $user) { - printf("User ID: %s, Name: %s\n", $user->getId(), $user->getUsername()); + printf("User ID: %s, Name: %s\n", $user->getId(), $user->getUsername()); } From dba249636098ca7c691dddd25a9e9de4b7593183 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 11:33:48 +0100 Subject: [PATCH 198/835] Show users what they got wrong --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 09407a4a9..d44595c93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ branches: before_script: - composer install --prefer-source - vendor/bin/parallel-lint --exclude vendor . - - vendor/bin/php-cs-fixer fix --dry-run --level psr2 . + - vendor/bin/php-cs-fixer fix --dry-run --diff --level psr2 . after_script: - php vendor/bin/coveralls -v From 13da3dd7de433be3c50270b0e73d383e3c44e03c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 11:36:40 +0100 Subject: [PATCH 199/835] Adding samples for retrieving user --- samples/Identity/get_user_by_email.php | 37 ++++++++++++++++++++++++++ samples/Identity/get_user_by_id.php | 37 ++++++++++++++++++++++++++ samples/Identity/get_user_by_name.php | 37 ++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 samples/Identity/get_user_by_email.php create mode 100644 samples/Identity/get_user_by_id.php create mode 100644 samples/Identity/get_user_by_name.php diff --git a/samples/Identity/get_user_by_email.php b/samples/Identity/get_user_by_email.php new file mode 100644 index 000000000..9bb099d16 --- /dev/null +++ b/samples/Identity/get_user_by_email.php @@ -0,0 +1,37 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Retrieve existing user +$user = $service->getUser('{email}', UserConst::MODE_EMAIL); diff --git a/samples/Identity/get_user_by_id.php b/samples/Identity/get_user_by_id.php new file mode 100644 index 000000000..da698a144 --- /dev/null +++ b/samples/Identity/get_user_by_id.php @@ -0,0 +1,37 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Retrieve existing user +$user = $service->getUser('{userId}', UserConst::MODE_ID); diff --git a/samples/Identity/get_user_by_name.php b/samples/Identity/get_user_by_name.php new file mode 100644 index 000000000..3bba52005 --- /dev/null +++ b/samples/Identity/get_user_by_name.php @@ -0,0 +1,37 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +// Set up Identity service +$service = $client->identityService(); + +// Retrieve existing user +$user = $service->getUser('{username}'); From ff2a8f6c7cfb53f6e074a1f7565ba546200037ca Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 11:36:52 +0100 Subject: [PATCH 200/835] Removing ambiguity --- samples/Identity/delete_user.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/samples/Identity/delete_user.php b/samples/Identity/delete_user.php index 1715aeffc..6f9b0f4a0 100644 --- a/samples/Identity/delete_user.php +++ b/samples/Identity/delete_user.php @@ -33,15 +33,8 @@ // Set up Identity service $service = $client->identityService(); -// Retrieve existing user, either by name: +// Retrieve existing user $user = $service->getUser('{username}'); -// by ID: -$user = $service->getUser('{userId}', UserConst::MODE_ID); - -// or by email: -$user = $service->getUser('{email}', UserConst::MODE_EMAIL); - - // And delete them... $user->delete(); From beadc4f52d7f1afd056071200cade77977562643 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 13:50:41 +0100 Subject: [PATCH 201/835] Update docblock comment --- lib/OpenCloud/OpenStack.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 7ced9ced2..dbd1f9854 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -185,9 +185,9 @@ public function getExpiration() } /** - * Set the tenant. If an integer is passed in, the SDK assumes you want to set the ID of the full Tenant object - * and sets this property accordingly. For any other data type, it assumes you want to populate the Tenant object. - * This ambiguity arises due to backwards compatibility. + * Set the tenant. If an integer or string is passed in, the SDK assumes you want to set the ID of the full + * Tenant object and sets this property accordingly. For any other data type, it assumes you want to populate + * the Tenant object. This ambiguity arises due to backwards compatibility. * * @param mixed $tenant * @return $this From a6e9db1d54f0363d305739eca64204af4ac2f4f6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 18 Nov 2014 16:36:45 +0100 Subject: [PATCH 202/835] Shifting to placeholders --- samples/Identity/add_user.php | 12 ++++-------- samples/Identity/authenticate_token.php | 12 ++++-------- samples/Identity/delete_user.php | 12 ++++-------- samples/Identity/get_user_by_email.php | 12 ++++-------- samples/Identity/get_user_by_id.php | 12 ++++-------- samples/Identity/get_user_by_name.php | 12 ++++-------- samples/Identity/list_users.php | 12 ++++-------- samples/Identity/reset_api_key.php | 12 ++++-------- 8 files changed, 32 insertions(+), 64 deletions(-) diff --git a/samples/Identity/add_user.php b/samples/Identity/add_user.php index b8738930a..e37e490ba 100644 --- a/samples/Identity/add_user.php +++ b/samples/Identity/add_user.php @@ -15,18 +15,14 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service diff --git a/samples/Identity/authenticate_token.php b/samples/Identity/authenticate_token.php index a56dd83c7..e1db2e4aa 100644 --- a/samples/Identity/authenticate_token.php +++ b/samples/Identity/authenticate_token.php @@ -15,18 +15,14 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Authenticate with the above credentials diff --git a/samples/Identity/delete_user.php b/samples/Identity/delete_user.php index 6f9b0f4a0..493e5ff98 100644 --- a/samples/Identity/delete_user.php +++ b/samples/Identity/delete_user.php @@ -15,19 +15,15 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Identity\Constants\User as UserConst; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service diff --git a/samples/Identity/get_user_by_email.php b/samples/Identity/get_user_by_email.php index 9bb099d16..e127dcb26 100644 --- a/samples/Identity/get_user_by_email.php +++ b/samples/Identity/get_user_by_email.php @@ -15,19 +15,15 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Identity\Constants\User as UserConst; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service diff --git a/samples/Identity/get_user_by_id.php b/samples/Identity/get_user_by_id.php index da698a144..0efbeece0 100644 --- a/samples/Identity/get_user_by_id.php +++ b/samples/Identity/get_user_by_id.php @@ -15,19 +15,15 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Identity\Constants\User as UserConst; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service diff --git a/samples/Identity/get_user_by_name.php b/samples/Identity/get_user_by_name.php index 3bba52005..45f1ff34f 100644 --- a/samples/Identity/get_user_by_name.php +++ b/samples/Identity/get_user_by_name.php @@ -15,19 +15,15 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Identity\Constants\User as UserConst; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service diff --git a/samples/Identity/list_users.php b/samples/Identity/list_users.php index 2cf4fbdbb..f5f0be8b5 100644 --- a/samples/Identity/list_users.php +++ b/samples/Identity/list_users.php @@ -15,19 +15,15 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Identity\Constants\User as UserConst; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service diff --git a/samples/Identity/reset_api_key.php b/samples/Identity/reset_api_key.php index 73adaf55f..28efe0878 100644 --- a/samples/Identity/reset_api_key.php +++ b/samples/Identity/reset_api_key.php @@ -15,19 +15,15 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Identity\Constants\User as UserConst; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +// You can replace {authUrl} with Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // Set up Identity service From ee3d5b3fc72670c28c7d135609a4b59ff910c014 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Wed, 19 Nov 2014 04:41:31 -0500 Subject: [PATCH 203/835] Add diagnostics to Server --- lib/OpenCloud/Compute/Resource/Server.php | 21 +++++++++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 5 +++++ .../Tests/Compute/_response/Extensions.resp | 8 +++++++ 3 files changed, 34 insertions(+) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 20a7692e5..2d42f840e 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -775,4 +775,25 @@ public function resume() return $this->action($object); } + + /** + * Get server diagnostics + * + * Gets basic usage data for a specified server. + * + * @api + * @return object + */ + public function diagnostics() + { + // The diagnostics is only available when the os-server-diagnostics extension is installed. + $this->checkExtension('os-server-diagnostics'); + + $url = $this->getUrl('diagnostics'); + + $response = $this->getClient()->get($url)->send(); + $body = Formatter::decode($response); + + return $body ?: (object) array(); + } } diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index eb741f96e..f98efa103 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -376,4 +376,9 @@ public function test_Create_With_Bootable_Volume_Delete_On_Termination() $this->assertEquals(0, $obj->boot_index); $this->assertEquals(true, $obj->delete_on_termination); } + + public function test_Diagnostics() + { + $this->assertTrue(is_object($this->server->diagnostics())); + } } diff --git a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp index d1b920829..ee590d812 100644 --- a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp +++ b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp @@ -119,6 +119,14 @@ Server: Jetty(8.0.y.z-SNAPSHOT) "alias": "os-keypairs", "description": "Keypair Support." }, + { + "updated": "2011-12-21T00:00:00+00:00", + "name": "ServerDiagnostics", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/server-diagnostics/api/v1.1", + "alias": "os-server-diagnostics", + "description": "Allow Admins to view server diagnostics through server action." + }, { "updated": "2012-03-07T14:46:43Z", "name": "OSNetworksV2", From 4162d4a17e8fea416ad5ef5ca1803fc888022396 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 19 Nov 2014 11:12:10 +0100 Subject: [PATCH 204/835] Removing env vars and shifting to placeholders --- samples/Compute/attach_volume.php | 41 ++++-------- samples/Compute/create_network.php | 23 +++---- samples/Compute/create_new_keypair.php | 27 ++++---- samples/Compute/create_server.php | 57 ++++------------- samples/Compute/create_server_and_get_ip.php | 62 +++++-------------- .../create_server_from_bootable_volume.php | 45 +++++--------- ..._bootable_volume_delete_on_termination.php | 47 +++++--------- .../Compute/create_server_with_keypair.php | 62 ++++--------------- samples/Compute/create_volume.php | 35 +++-------- samples/Compute/delete_server.php | 28 +++------ samples/Compute/detach_volume.php | 36 +++-------- samples/Compute/list_servers.php | 27 +++----- samples/Compute/update_server.php | 28 +++------ samples/Compute/upload_existing_keypair.php | 19 +++--- 14 files changed, 151 insertions(+), 386 deletions(-) diff --git a/samples/Compute/attach_volume.php b/samples/Compute/attach_volume.php index cd7625150..84b05b70e 100644 --- a/samples/Compute/attach_volume.php +++ b/samples/Compute/attach_volume.php @@ -1,5 +1,4 @@ getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Crete Compute and Volume service objects -$region = 'ORD'; - -$volumeService = $client->volumeService(null, $region); -$computeService = $client->computeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); +$computeService = $client->computeService(null, '{region}'); // 3. Get your volume -$volumeId = '{volumeId}'; -$myVolume = $volumeService->volume($volumeId); +$myVolume = $volumeService->volume('{volumeId}'); // 4. Get your server -$serverId = '{serverId}'; -$myServer = $computeService->server($serverId); - -// 5. Attach +$myServer = $computeService->server('{serverId}'); -// Specifying null will auto-assign the block. You also can be specific - '/dev/xvdb' +// 5. Attach. Specifying null will auto-assign the block. You also can be specific: '/dev/xvdb' $mountPoint = null; $myServer->attachVolume($myVolume, $mountPoint); diff --git a/samples/Compute/create_network.php b/samples/Compute/create_network.php index 002023486..5f8c38fbe 100644 --- a/samples/Compute/create_network.php +++ b/samples/Compute/create_network.php @@ -1,5 +1,4 @@ getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service object -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get empty network $network = $service->network(); diff --git a/samples/Compute/create_new_keypair.php b/samples/Compute/create_new_keypair.php index 9b8fa62f1..c00df6e8c 100644 --- a/samples/Compute/create_new_keypair.php +++ b/samples/Compute/create_new_keypair.php @@ -1,5 +1,4 @@ getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service object -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get empty keypair $keypair = $service->keypair(); @@ -46,7 +40,8 @@ echo $keypair->getPublicKey(); // 5. Save private key to a file so you can use it to SSH into your server later -$sshPrivateKeyFilename = 'new_keypair_private'; +$filename = 'new_keypair_private'; $privateKey = $keypair->getPrivateKey(); -file_put_contents($sshPrivateKeyFilename, $privateKey); -chmod($sshPrivateKeyFilename, 0600); + +file_put_contents($filename, $privateKey); +chmod($filename, 0600); diff --git a/samples/Compute/create_server.php b/samples/Compute/create_server.php index 682a2478f..e6ca5ee3e 100644 --- a/samples/Compute/create_server.php +++ b/samples/Compute/create_server.php @@ -16,66 +16,33 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use Guzzle\Http\Exception\BadResponseException; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get empty server $server = $service->server(); -// 4. Select an OS image -$images = $service->imageList(); -foreach ($images as $image) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntuImage = $image; - break; - } -} - -// 5. Select a hardware flavor -$flavors = $service->flavorList(); -foreach ($flavors as $flavor) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} - -// 6. Create +// 4. Create try { $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}' )); } catch (BadResponseException $e) { - // No! Something failed. Let's find out: - $badResponse = $e->getResponse(); - echo sprintf( - 'Status: %s\nBody: %s\nHeaders: %s', - $badResponse->getStatusCode(), - $badResponse->getBody(true), - implode(', ', $response->getHeaderLines()) - ); + echo $e->getResponse(); } $body = json_decode($response->getBody(true)); diff --git a/samples/Compute/create_server_and_get_ip.php b/samples/Compute/create_server_and_get_ip.php index 46b1d0dcf..a6029bc7c 100644 --- a/samples/Compute/create_server_and_get_ip.php +++ b/samples/Compute/create_server_and_get_ip.php @@ -16,71 +16,39 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Compute\Constants\ServerState; use Guzzle\Http\Exception\BadResponseException; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get empty server $server = $service->server(); -// 4. Select an OS image -$images = $service->imageList(); -foreach ($images as $image) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntuImage = $image; - break; - } -} - -// 5. Select a hardware flavor -$flavors = $service->flavorList(); -foreach ($flavors as $flavor) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} - -// 6. Create +// 4. Create the server. If you do not know what imageId or flavorId to use, +// please run the list_flavors.php and list_images.php scripts. try { $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}' )); } catch (BadResponseException $e) { - // No! Something failed. Let's find out: - $badResponse = $e->getResponse(); - echo sprintf( - 'Status: %s\nBody: %s\nHeaders: %s', - $badResponse->getStatusCode(), - $badResponse->getBody(true), - implode(', ', $response->getHeaderLines()) - ); + echo $e->getResponse(); } -// 7. Wait for the server to become ACTIVE. +// 5. Wait for the server to become ACTIVE. $server->waitFor(ServerState::ACTIVE); -// 8. Retreive the server's IP +// 6. Retreive the server's IP $ipv4Address = $server->accessIPv4; diff --git a/samples/Compute/create_server_from_bootable_volume.php b/samples/Compute/create_server_from_bootable_volume.php index 3dcb8a811..141a94f12 100644 --- a/samples/Compute/create_server_from_bootable_volume.php +++ b/samples/Compute/create_server_from_bootable_volume.php @@ -16,54 +16,39 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, - * - RAX_API_KEY: Your Rackspace Cloud Account API Key, - * - RAX_REGION: Rackspace Cloud region in which to create server; e.g.: DFW, - * - RAX_BOOTABLE_VOLUME_ID: ID of bootable volume in Rackspace Cloud region, and - * - RAX_FLAVOR_ID: ID of a compute flavor (general or higher) in the Rackspace Cloud; e.g.: general1-1. - * - * - You have an existing keypair. For this script, it will be called 'my_keypair' - * but this will change depending on what you called yours. - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use Guzzle\Http\Exception\BadResponseException; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service -$region = getenv('RAX_REGION'); -$computeService = $client->computeService(null, $region); -$volumeService = $client->volumeService(null, $region); +$computeService = $client->computeService(null, '{region}'); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get empty server $server = $computeService->server(); // 4. Select bootable volume -$bootableVolume = $volumeService->volume(getenv('RAX_BOOTABLE_VOLUME_ID')); +$bootableVolume = $volumeService->volume('{volumeId}'); // 5. Select a hardware flavor -$flavor = $computeService->flavor(getenv('RAX_FLAVOR_ID')); +$flavor = $computeService->flavor('{flavorId}'); -// 6. Create +// 6. Create the server. If you do not know what imageId or flavorId to use, +// please run the list_flavors.php and list_images.php scripts. try { $response = $server->create(array( - 'name' => 'My server created from a bootable volume', - 'volume' => $bootableVolume, - 'flavor' => $flavor + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}' )); } catch (BadResponseException $e) { - // No! Something failed. Let's find out: - echo $e->getRequest() . PHP_EOL . PHP_EOL; echo $e->getResponse(); } diff --git a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php index bebb72902..20457105b 100644 --- a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php +++ b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php @@ -16,56 +16,41 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, - * - RAX_API_KEY: Your Rackspace Cloud Account API Key, - * - RAX_REGION: Rackspace Cloud region in which to create server; e.g.: DFW, - * - RAX_BOOTABLE_VOLUME_ID: ID of bootable volume in Rackspace Cloud region, and - * - RAX_FLAVOR_ID: ID of a compute flavor (general or higher) in the Rackspace Cloud; e.g.: general1-1. - * - * - You have an existing keypair. For this script, it will be called 'my_keypair' - * but this will change depending on what you called yours. - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use Guzzle\Http\Exception\BadResponseException; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service -$region = getenv('RAX_REGION'); -$computeService = $client->computeService(null, $region); -$volumeService = $client->volumeService(null, $region); +$computeService = $client->computeService(null, '{region}'); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get empty server $server = $computeService->server(); // 4. Select bootable volume -$bootableVolume = $volumeService->volume(getenv('RAX_BOOTABLE_VOLUME_ID')); +$bootableVolume = $volumeService->volume('{volumeId}'); // 5. Select a hardware flavor -$flavor = $computeService->flavor(getenv('RAX_FLAVOR_ID')); +$flavor = $computeService->flavor('{flavorId}'); // 6. Create server from bootable volume, setting the volume to be -// deleted when the server is terminated (deleted). +// deleted when the server is terminated (deleted). If you do not know what +// imageId or flavorId to use, please run the list_flavors.php and +// list_images.php scripts. try { $response = $server->create(array( - 'name' => 'My server created from a bootable volume', - 'volume' => $bootableVolume, - 'volumeDeleteOnTermination' => true, - 'flavor' => $flavor + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}' )); } catch (BadResponseException $e) { - // No! Something failed. Let's find out: - echo $e->getRequest() . PHP_EOL . PHP_EOL; echo $e->getResponse(); } diff --git a/samples/Compute/create_server_with_keypair.php b/samples/Compute/create_server_with_keypair.php index 65898e657..1fb932692 100644 --- a/samples/Compute/create_server_with_keypair.php +++ b/samples/Compute/create_server_with_keypair.php @@ -16,68 +16,32 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - * - * - You have an existing keypair. For this script, it will be called 'my_keypair' - * but this will change depending on what you called yours. - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; use Guzzle\Http\Exception\BadResponseException; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get empty server $server = $service->server(); -// 4. Select an OS image -$images = $service->imageList(); -foreach ($images as $image) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntuImage = $image; - break; - } -} - -// 5. Select a hardware flavor -$flavors = $service->flavorList(); -foreach ($flavors as $flavor) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} - -// 6. Create +// 4. Create the server. If you do not know what imageId or flavorId to use, +// please run the list_flavors.php and list_images.php scripts. try { $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor, - 'keypair' => 'my_keypair' // Enter your own keypair name + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}' )); } catch (BadResponseException $e) { - // No! Something failed. Let's find out: - $badResponse = $e->getResponse(); - echo sprintf( - 'Status: %s\nBody: %s\nHeaders: %s', - $badResponse->getStatusCode(), - $badResponse->getBody(true), - implode(', ', $response->getHeaderLines()) - ); + echo $e->getResponse(); } diff --git a/samples/Compute/create_volume.php b/samples/Compute/create_volume.php index d4e827388..8bfc67b41 100644 --- a/samples/Compute/create_volume.php +++ b/samples/Compute/create_volume.php @@ -16,42 +16,25 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Volume service object -$region = 'ORD'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get empty volume $myVolume = $volumeService->volume(); -// 4. Pick a volume type - -// Option a: traverse through list -$volumeTypes = $volumeService->volumeTypeList(); -foreach ($volumeTypes as $volumeType) { - if ($volumeType->name == 'SSD') { - break; - } -} - -// Option b: pass in UUID if you already know which one you want +// 4. Pick a volume type by passing in UUID. If you do not know this, please +// run the list_volume_types.php script. $volumeType = $volumeService->volumeType('{volumeTypeId}'); // 5. Create diff --git a/samples/Compute/delete_server.php b/samples/Compute/delete_server.php index ba78d4524..e3e433d47 100644 --- a/samples/Compute/delete_server.php +++ b/samples/Compute/delete_server.php @@ -16,34 +16,22 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - * - * - There exists a server named 'my_server' and you know its ID. Run - * create_server.php if you need to create one first. - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service object -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get your existing server -$serverId = '7af22b0d-7d01-4460-a222-77e65a9ab184'; -$server = $service->server($serverId); +$server = $service->server('{serverId}'); // 4. Delete it $server->delete(); diff --git a/samples/Compute/detach_volume.php b/samples/Compute/detach_volume.php index 0edb057f3..d0dc3580e 100644 --- a/samples/Compute/detach_volume.php +++ b/samples/Compute/detach_volume.php @@ -16,42 +16,26 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - * - * - There exists a server named 'my_server' and you know its ID. Run - * create_server.php if you need to create one first. - * - There exists a volume name 'my_volume' and you know its ID. Run - * create_volume.php if you need to create one first. - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Crete Compute and Volume service objects -$region = 'ORD'; - -$volumeService = $client->volumeService(null, $region); -$computeService = $client->computeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); +$computeService = $client->computeService(null, '{region}'); // 3. Get your volume -$volumeId = '{volumeId}'; -$myVolume = $volumeService->volume($volumeId); +$myVolume = $volumeService->volume('{volumeId}'); // 4. Get your server -$serverId = '{serverId}'; -$myServer = $computeService->server($serverId); +$myServer = $computeService->server('{serverId}'); // 5. Detach $myServer->detachVolume($myVolume); diff --git a/samples/Compute/list_servers.php b/samples/Compute/list_servers.php index 2712eb6c7..1cb88e777 100644 --- a/samples/Compute/list_servers.php +++ b/samples/Compute/list_servers.php @@ -15,33 +15,24 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Compute service object from the client. -$region = 'DFW'; -$computeService = $client->computeService(null, $region); +$computeService = $client->computeService(null, '{region}'); // 3. Get the list of servers. $serverList = $computeService->serverList(); +// 4. Traverse over the list and output key information. foreach ($serverList as $server) { - echo str_repeat("-", 80) . PHP_EOL; - echo "ID: " . $server->id() . " | " - . "Name: " . $server->name() . " | " - . "Image ID: " . $server->image->id . PHP_EOL; - echo str_repeat("-", 80) . PHP_EOL; + printf("ID: %s, Name: %s, Image ID: %s\n", $server->id(), $server->name(), $server->image->id); } diff --git a/samples/Compute/update_server.php b/samples/Compute/update_server.php index 558ec979b..847b0836e 100644 --- a/samples/Compute/update_server.php +++ b/samples/Compute/update_server.php @@ -16,34 +16,22 @@ * limitations under the License. */ -/** - * Pre-requisites: - * - * Prior to running this script, you must setup the following environment variables: - * - RAX_USERNAME: Your Rackspace Cloud Account Username, and - * - RAX_API_KEY: Your Rackspace Cloud Account API Key - * - * - There exists a server named 'my_server' and you know its ID. Run - * create_server.php if you need to create one first. - */ - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service object -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get your existing server -$serverId = '{serverId}'; -$server = $service->server($serverId); +$server = $service->server('{serverId}'); // 4. Update it - here we'll change its name, but you can also update its // `accessIPv4' and `accessIPv6' attributes. diff --git a/samples/Compute/upload_existing_keypair.php b/samples/Compute/upload_existing_keypair.php index d2886680d..12b81daab 100644 --- a/samples/Compute/upload_existing_keypair.php +++ b/samples/Compute/upload_existing_keypair.php @@ -16,24 +16,19 @@ * limitations under the License. */ -// Pre-requisites -// Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key - -require dirname(__DIR__) . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Create Compute service object -$region = 'ORD'; -$service = $client->computeService(null, $region); +$service = $client->computeService(null, '{region}'); // 3. Get empty keypair $keypair = $service->keypair(); From a81ca887e65301d437ccf942e56e840948276fd6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 19 Nov 2014 11:16:30 +0100 Subject: [PATCH 205/835] Add list scripts --- samples/Compute/list_flavors.php | 38 +++++++++++++++++++++++++++ samples/Compute/list_images.php | 38 +++++++++++++++++++++++++++ samples/Compute/list_volume_types.php | 38 +++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 samples/Compute/list_flavors.php create mode 100644 samples/Compute/list_images.php create mode 100644 samples/Compute/list_volume_types.php diff --git a/samples/Compute/list_flavors.php b/samples/Compute/list_flavors.php new file mode 100644 index 000000000..17f9dc371 --- /dev/null +++ b/samples/Compute/list_flavors.php @@ -0,0 +1,38 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Compute service object from the client. +$computeService = $client->computeService(null, '{region}'); + +// 3. Get the list of flavors. +$images = $service->flavorList(); + +// 4. Traverse +foreach ($flavors as $flavor) { + printf("ID: %s, Name: %s\n", $flavor->id, $flavor->name); +} diff --git a/samples/Compute/list_images.php b/samples/Compute/list_images.php new file mode 100644 index 000000000..03cfcbe9d --- /dev/null +++ b/samples/Compute/list_images.php @@ -0,0 +1,38 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Compute service object from the client. +$computeService = $client->computeService(null, '{region}'); + +// 3. Get the list of images. +$images = $service->imageList(); + +// 4. Traverse +foreach ($images as $image) { + printf("ID: %s, Name: %s\n", $image->id, $image->name); +} diff --git a/samples/Compute/list_volume_types.php b/samples/Compute/list_volume_types.php new file mode 100644 index 000000000..e10854b3c --- /dev/null +++ b/samples/Compute/list_volume_types.php @@ -0,0 +1,38 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Volume service object from the client. +$service = $client->volumeService(null, $region); + +// 3. Get a collection of volume types. +$volumeTypes = $service->volumeTypeList(); + +// 4. Traverse. +foreach ($volumeTypes as $volumeType) { + printf("ID: %s, Name: %s\n", $volumeType->id, $volumeType->name); +} From 2ad609e746040614a866ae3fc766a209b3b454c3 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Wed, 19 Nov 2014 05:40:30 -0500 Subject: [PATCH 206/835] Mock the response for Diagnostics test --- .../Tests/Compute/Resource/ServerTest.php | 7 +++++- .../Tests/Compute/_response/Diagnostics.resp | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/OpenCloud/Tests/Compute/_response/Diagnostics.resp diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index f98efa103..c97aabb76 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -379,6 +379,11 @@ public function test_Create_With_Bootable_Volume_Delete_On_Termination() public function test_Diagnostics() { - $this->assertTrue(is_object($this->server->diagnostics())); + $this->addMockSubscriber($this->getTestFilePath('Diagnostics')); + $diagnostics = $this->server->diagnostics(); + $this->assertInternalType('object', $diagnostics); + $this->assertEquals(524288, $diagnostics->memory); + $this->assertEquals(-1, $diagnostics->vda_errors); + $this->assertEquals(662, $diagnostics->vnet1_tx_packets); } } diff --git a/tests/OpenCloud/Tests/Compute/_response/Diagnostics.resp b/tests/OpenCloud/Tests/Compute/_response/Diagnostics.resp new file mode 100644 index 000000000..e9fdec80d --- /dev/null +++ b/tests/OpenCloud/Tests/Compute/_response/Diagnostics.resp @@ -0,0 +1,24 @@ +HTTP/1.1 200 OK +Date: Fri, 15 Nov 2013 14:12:58 GMT +Content-Length: 1364 +Content-Type: application/json +X-Compute-Request-Id: req-164dc435-90c2-48ce-9d69-96f170c518c8 +Server: Jetty(8.0.y.z-SNAPSHOT) + +{ + "cpu0_time": 17300000000, + "memory": 524288, + "vda_errors": -1, + "vda_read": 262144, + "vda_read_req": 112, + "vda_write": 5778432, + "vda_write_req": 488, + "vnet1_rx": 2070139, + "vnet1_rx_drop": 0, + "vnet1_rx_errors": 0, + "vnet1_rx_packets": 26701, + "vnet1_tx": 140208, + "vnet1_tx_drop": 0, + "vnet1_tx_errors": 0, + "vnet1_tx_packets": 662 +} \ No newline at end of file From ff3a4bd469ff017b8de199d77ea6da48fab85edc Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 17 Nov 2014 12:19:21 +0100 Subject: [PATCH 207/835] Adding more LB sample scripts --- samples/LoadBalancer/create-lb.php | 55 +++++++++++++++++++ samples/LoadBalancer/delete-lb.php | 39 +++++++++++++ samples/LoadBalancer/ssl-termination.php | 45 +++++++++++++++ samples/LoadBalancer/throttle-connections.php | 48 ++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 samples/LoadBalancer/create-lb.php create mode 100644 samples/LoadBalancer/delete-lb.php create mode 100644 samples/LoadBalancer/ssl-termination.php create mode 100644 samples/LoadBalancer/throttle-connections.php diff --git a/samples/LoadBalancer/create-lb.php b/samples/LoadBalancer/create-lb.php new file mode 100644 index 000000000..b13834bb0 --- /dev/null +++ b/samples/LoadBalancer/create-lb.php @@ -0,0 +1,55 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +$service = $client->loadBalancerService(null, getenv('RS_REGION')); + +// Create empty object +$lb = $service->loadBalancer(); + +// Optional: add back-end nodes that need to be load balanced. These are +// usually servers. +$serverNode = $lb->node(array( + 'address' => '{ipAddress}', // substitute your server's IPv4 address + 'port' => 80, + 'condition' => 'ENABLED', +)); + +// Configure the type of Virtual IP your LB will use +$lb->addVirtualIp('PUBLIC'); + +// Create it +$lb->create(array( + 'name' => '{name}', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverNode) +)); diff --git a/samples/LoadBalancer/delete-lb.php b/samples/LoadBalancer/delete-lb.php new file mode 100644 index 000000000..9d39cbba2 --- /dev/null +++ b/samples/LoadBalancer/delete-lb.php @@ -0,0 +1,39 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +$service = $client->loadBalancerService(null, getenv('RS_REGION')); + +// Retrieve existing LB +$lb = $service->loadBalancer('{loadBalancerId}'); + +// Delete +$lb->delete(); diff --git a/samples/LoadBalancer/ssl-termination.php b/samples/LoadBalancer/ssl-termination.php new file mode 100644 index 000000000..7904133de --- /dev/null +++ b/samples/LoadBalancer/ssl-termination.php @@ -0,0 +1,45 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +$service = $client->loadBalancerService(null, getenv('RS_REGION')); + +// Retrieve existing LB +$lb = $service->loadBalancer('{loadBalancerId}'); + +// Update SSL config +$sslConfig = $lb->SSLTermination(); +$sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => '{privateKey}', // specify your private key + 'certificate' => '{certificate}', // specify your SSL certificate +)); diff --git a/samples/LoadBalancer/throttle-connections.php b/samples/LoadBalancer/throttle-connections.php new file mode 100644 index 000000000..b96b39c5a --- /dev/null +++ b/samples/LoadBalancer/throttle-connections.php @@ -0,0 +1,48 @@ + getenv('RS_USERNAME'), + 'apiKey' => getenv('RS_API_KEY'), +)); + +$service = $client->loadBalancerService(null, getenv('RS_REGION')); + +// Retrieve existing LB +$lb = $service->loadBalancer('{loadBalancerId}'); + +// Set up throttling +$throttle = $lb->connectionThrottle(); + +// Allow a maximum of 5,000 simultaneous connections (maxConnections) per +// minute (rateInterval). Limit each IP to a maximum of 50 connections (maxConnectionRate). +$throttle->create(array( + 'maxConnections' => 5000, + 'minConnections' => 10, + 'rateInterval' => 60, + 'maxConnectionRate' => 50 +)); From 3c233d8703462de2be9545da2469fa67a414ae7f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 19 Nov 2014 13:29:19 +0100 Subject: [PATCH 208/835] Remove remaining env vars --- samples/LoadBalancer/create-lb.php | 14 ++++------- samples/LoadBalancer/delete-lb.php | 14 ++++------- samples/LoadBalancer/list-load-balancers.php | 23 +++++-------------- samples/LoadBalancer/ssl-termination.php | 14 ++++------- samples/LoadBalancer/throttle-connections.php | 14 ++++------- 5 files changed, 22 insertions(+), 57 deletions(-) diff --git a/samples/LoadBalancer/create-lb.php b/samples/LoadBalancer/create-lb.php index b13834bb0..9ff7661a1 100644 --- a/samples/LoadBalancer/create-lb.php +++ b/samples/LoadBalancer/create-lb.php @@ -15,22 +15,16 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key -// * RS_REGION: the Rackspace Cloud region you want to use - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -$service = $client->loadBalancerService(null, getenv('RS_REGION')); +$service = $client->loadBalancerService(null, '{region}'); // Create empty object $lb = $service->loadBalancer(); diff --git a/samples/LoadBalancer/delete-lb.php b/samples/LoadBalancer/delete-lb.php index 9d39cbba2..9be357778 100644 --- a/samples/LoadBalancer/delete-lb.php +++ b/samples/LoadBalancer/delete-lb.php @@ -15,22 +15,16 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key -// * RS_REGION: the Rackspace Cloud region you want to use - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -$service = $client->loadBalancerService(null, getenv('RS_REGION')); +$service = $client->loadBalancerService(null, '{region}'); // Retrieve existing LB $lb = $service->loadBalancer('{loadBalancerId}'); diff --git a/samples/LoadBalancer/list-load-balancers.php b/samples/LoadBalancer/list-load-balancers.php index a5e68c1bc..0b62f1755 100644 --- a/samples/LoadBalancer/list-load-balancers.php +++ b/samples/LoadBalancer/list-load-balancers.php @@ -15,30 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your Rackspace Cloud Authentication URL, -// * OS_USERNAME: Your Rackspace Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API KEY, and -// * OS_REGION_NAME: The Rackspace Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate an Rackspace client. -$client = new Rackspace(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an LoadBalancer service object from the client. -$region = getenv('OS_REGION_NAME'); -$loadBalancerService = $client->loadBalancerService(null, $region); +$service = $client->loadBalancerService(null, '{region}'); -// 3. Get load balancers. $loadBalancers = $loadBalancerService->loadBalancerList(); + foreach ($loadBalancers as $loadBalancer) { /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ var_dump($loadBalancer); diff --git a/samples/LoadBalancer/ssl-termination.php b/samples/LoadBalancer/ssl-termination.php index 7904133de..24767a1fa 100644 --- a/samples/LoadBalancer/ssl-termination.php +++ b/samples/LoadBalancer/ssl-termination.php @@ -15,22 +15,16 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key -// * RS_REGION: the Rackspace Cloud region you want to use - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -$service = $client->loadBalancerService(null, getenv('RS_REGION')); +$service = $client->loadBalancerService(null, '{region}'); // Retrieve existing LB $lb = $service->loadBalancer('{loadBalancerId}'); diff --git a/samples/LoadBalancer/throttle-connections.php b/samples/LoadBalancer/throttle-connections.php index b96b39c5a..6da2641f3 100644 --- a/samples/LoadBalancer/throttle-connections.php +++ b/samples/LoadBalancer/throttle-connections.php @@ -15,22 +15,16 @@ * limitations under the License. */ -// Prior to running this script, you must setup the following environment variables: -// * RS_AUTH_URL: your Rackspace authentication URL -// * RS_USERNAME: your Rackspace username -// * RS_API_KEY: your Rackspace API key -// * RS_REGION: the Rackspace Cloud region you want to use - require dirname(__DIR__) . '/../vendor/autoload.php'; use OpenCloud\Rackspace; -$client = new Rackspace(getenv('RS_AUTH_URL'), array( - 'username' => getenv('RS_USERNAME'), - 'apiKey' => getenv('RS_API_KEY'), +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -$service = $client->loadBalancerService(null, getenv('RS_REGION')); +$service = $client->loadBalancerService(null, '{region}'); // Retrieve existing LB $lb = $service->loadBalancer('{loadBalancerId}'); From 37e041400a6114968fc462a58ffd06f5012ca745 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Wed, 19 Nov 2014 08:30:43 -0500 Subject: [PATCH 209/835] Add method start/stop for Server --- lib/OpenCloud/Compute/Resource/Server.php | 36 +++++++++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 12 +++++++ .../Tests/Compute/_response/Extensions.resp | 8 +++++ 3 files changed, 56 insertions(+) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 2d42f840e..2472123d2 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -796,4 +796,40 @@ public function diagnostics() return $body ?: (object) array(); } + + /** + * Start a server + * + * Starts a stopped server and changes its status to ACTIVE. + * + * @api + * @return \Guzzle\Http\Message\Response + */ + public function start() + { + // The start action is only available when the os-server-start-stop extension is installed. + $this->checkExtension('os-server-start-stop'); + + $object = (object) array('os-start' => null); + + return $this->action($object); + } + + /** + * Stop a server + * + * Stops a running server and changes its status to STOPPED. + * + * @api + * @return \Guzzle\Http\Message\Response + */ + public function stop() + { + // The stop action is only available when the os-server-start-stop extension is installed. + $this->checkExtension('os-server-start-stop'); + + $object = (object) array('os-stop' => null); + + return $this->action($object); + } } diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index c97aabb76..25be71f53 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -386,4 +386,16 @@ public function test_Diagnostics() $this->assertEquals(-1, $diagnostics->vda_errors); $this->assertEquals(662, $diagnostics->vnet1_tx_packets); } + + public function test_Start() + { + $this->addMockSubscriber(new \Guzzle\Http\Message\Response(204)); + $this->assertEquals(204, $this->server->start()->getStatusCode()); + } + + public function test_Stop() + { + $this->addMockSubscriber(new \Guzzle\Http\Message\Response(204)); + $this->assertEquals(204, $this->server->stop()->getStatusCode()); + } } diff --git a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp index ee590d812..68c63da10 100644 --- a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp +++ b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp @@ -127,6 +127,14 @@ Server: Jetty(8.0.y.z-SNAPSHOT) "alias": "os-server-diagnostics", "description": "Allow Admins to view server diagnostics through server action." }, + { + "updated": "2012-01-23T00:00:00+00:00", + "name": "ServerStartStop", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/servers/api/v1.1", + "alias": "os-server-start-stop", + "description": "Start/Stop instance compute API support." + }, { "updated": "2012-03-07T14:46:43Z", "name": "OSNetworksV2", From e64dc1411d2b7dfbf4894d5cf88e1d7c845c1a72 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Wed, 19 Nov 2014 09:09:16 -0500 Subject: [PATCH 210/835] Fix status code for start/stop action of server in tests --- tests/OpenCloud/Tests/Compute/Resource/ServerTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index 25be71f53..0b2a8c0ee 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -389,13 +389,13 @@ public function test_Diagnostics() public function test_Start() { - $this->addMockSubscriber(new \Guzzle\Http\Message\Response(204)); - $this->assertEquals(204, $this->server->start()->getStatusCode()); + $this->addMockSubscriber(new \Guzzle\Http\Message\Response(202)); + $this->assertEquals(202, $this->server->start()->getStatusCode()); } public function test_Stop() { - $this->addMockSubscriber(new \Guzzle\Http\Message\Response(204)); - $this->assertEquals(204, $this->server->stop()->getStatusCode()); + $this->addMockSubscriber(new \Guzzle\Http\Message\Response(202)); + $this->assertEquals(202, $this->server->stop()->getStatusCode()); } } From 7816070956c5fd7cad439f069e7f1b6540cd173b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 19 Nov 2014 17:18:43 -0800 Subject: [PATCH 211/835] Adding capability for version negotiation in service endpoint URIs. --- .../Common/Service/CatalogService.php | 2 +- lib/OpenCloud/Common/Service/Endpoint.php | 43 +++++++++++++-- .../Tests/Common/Service/EndpointTest.php | 52 +++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 tests/OpenCloud/Tests/Common/Service/EndpointTest.php diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index 8000d1615..f21e47a76 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -208,7 +208,7 @@ private function findEndpoint() // Search each service to find The One foreach ($catalog->getItems() as $service) { if ($service->hasType($this->type) && $service->hasName($this->name)) { - return Endpoint::factory($service->getEndpointFromRegion($this->region)); + return Endpoint::factory($service->getEndpointFromRegion($this->region), $this->getClient()); } } diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index e331ce334..3450afc82 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -18,6 +18,7 @@ namespace OpenCloud\Common\Service; use Guzzle\Http\Url; +use OpenCloud\Common\Http\Message\Formatter; /** * An endpoint serves as a location which receives and emits API interactions. It will therefore also host @@ -44,17 +45,18 @@ class Endpoint /** * @param $object + * @param $client HTTP client * @return Endpoint */ - public static function factory($object) + public static function factory($object, $client) { $endpoint = new self(); if (isset($object->publicURL)) { - $endpoint->setPublicUrl($object->publicURL); + $endpoint->setPublicUrl($this->getVersionedUrl($object->publicURL, $client)); } if (isset($object->internalURL)) { - $endpoint->setPrivateUrl($object->internalURL); + $endpoint->setPrivateUrl($this->getVersionedUrl($object->internalURL, $client)); } if (isset($object->region)) { $endpoint->setRegion($object->region); @@ -119,4 +121,39 @@ public function getRegion() { return $this->region; } + + /** + * @param string $url URL of endpoint + * @param object $client HTTP client + * @return string URL of endpoint, with version + */ + protected function getVersionedUrl($url, $client) + { + try { + // Make GET request to URL + $response = Formatter::decode($client->get($url)->send()); + + // Attempt to parse response and determine URL for given $version + if (!property_exists($response, 'versions')) { + return $url; + } + + foreach ($response->versions as $version) { + if ($version->status == 'CURRENT') { + foreach ($version->links as $link) { + if ($link->rel == 'self') { + return $link->href; + } + } + } + } + + // If we've reached this point, we could not find a versioned + // URL in the response; return the original URL as-is + return $url; + + } catch (Exception $e) { + return $url; + } + } } diff --git a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php new file mode 100644 index 000000000..6faae1d52 --- /dev/null +++ b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php @@ -0,0 +1,52 @@ +addMockSubscriber($this->makeResponse('{"versions":[{"status":"CURRENT","id":"v2.0","links":[{"href":"http://hostport/v2.0","rel":"self" }]}]}', 200)); + + $e = new PublicEndpoint(); + + $expectedUrl = "http://hostport/v2.0"; + $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport", $this->client)); + } + + public function testGetVersionedUrlWithVersionedEndpointUrl() + { + $this->addMockSubscriber($this->makeResponse('{}', 200)); + + $e = new PublicEndpoint(); + + $expectedUrl = "http://hostport/v1"; + $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport/v1", $this->client)); + } +} From 84743746602c305b9e8b9eac9597d7a71da57600 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 19 Nov 2014 17:48:15 -0800 Subject: [PATCH 212/835] Making customary whitespace sacrifies to mollify the PSR-2 lint gods. --- lib/OpenCloud/Common/Service/Endpoint.php | 29 +++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index 3450afc82..361411672 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -129,8 +129,8 @@ public function getRegion() */ protected function getVersionedUrl($url, $client) { - try { - // Make GET request to URL + try { + // Make GET request to URL $response = Formatter::decode($client->get($url)->send()); // Attempt to parse response and determine URL for given $version @@ -138,22 +138,21 @@ protected function getVersionedUrl($url, $client) return $url; } - foreach ($response->versions as $version) { - if ($version->status == 'CURRENT') { - foreach ($version->links as $link) { - if ($link->rel == 'self') { - return $link->href; - } - } - } - } + foreach ($response->versions as $version) { + if ($version->status == 'CURRENT') { + foreach ($version->links as $link) { + if ($link->rel == 'self') { + return $link->href; + } + } + } + } // If we've reached this point, we could not find a versioned // URL in the response; return the original URL as-is return $url; - - } catch (Exception $e) { - return $url; - } + } catch (Exception $e) { + return $url; + } } } From 64ef510ba3e196fb40b3e3cbfd3dd6a4ed3de7a8 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 20 Nov 2014 13:44:24 +0100 Subject: [PATCH 213/835] Adding more samples --- samples/LoadBalancer/blacklist-ip-range.php | 38 ++++++++++++++ samples/LoadBalancer/limit-access-to-1-ip.php | 42 ++++++++++++++++ samples/ObjectStore/upload-directory.php | 34 +++++++++++++ samples/Queues/add-message.php | 48 ++++++++++++++++++ samples/Queues/claim-messages.php | 50 +++++++++++++++++++ samples/Queues/create-queue.php | 36 +++++++++++++ samples/Queues/delete-queue.php | 39 +++++++++++++++ 7 files changed, 287 insertions(+) create mode 100644 samples/LoadBalancer/blacklist-ip-range.php create mode 100644 samples/LoadBalancer/limit-access-to-1-ip.php create mode 100644 samples/ObjectStore/upload-directory.php create mode 100644 samples/Queues/add-message.php create mode 100644 samples/Queues/claim-messages.php create mode 100644 samples/Queues/create-queue.php create mode 100644 samples/Queues/delete-queue.php diff --git a/samples/LoadBalancer/blacklist-ip-range.php b/samples/LoadBalancer/blacklist-ip-range.php new file mode 100644 index 000000000..68805cb38 --- /dev/null +++ b/samples/LoadBalancer/blacklist-ip-range.php @@ -0,0 +1,38 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->loadBalancerService(null, '{region}'); + +// Retrieve your load balancer +$lb = $service->loadBalancer('{loadBalancerId}'); + +// Blacklist an IP range +$lb->createAccessList(array( + (object) array( + 'type' => 'DENY', + 'address' => '{ipAddressRange}' // Replace with your own CIDR + ) +)); diff --git a/samples/LoadBalancer/limit-access-to-1-ip.php b/samples/LoadBalancer/limit-access-to-1-ip.php new file mode 100644 index 000000000..fa04cea73 --- /dev/null +++ b/samples/LoadBalancer/limit-access-to-1-ip.php @@ -0,0 +1,42 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->loadBalancerService(null, '{region}'); + +// Retrieve your load balancer +$lb = $service->loadBalancer('{loadBalancerId}'); + +// Limit access to just 1 IP CIDR +$lb->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '{ipAddress}' // Add your own CIDR + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) +)); diff --git a/samples/ObjectStore/upload-directory.php b/samples/ObjectStore/upload-directory.php new file mode 100644 index 000000000..304779be4 --- /dev/null +++ b/samples/ObjectStore/upload-directory.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->objectStoreService(null, '{region}'); + +// Find the container you want to upload to. +$container = $service->getContainer('{containerName}'); + +// Specify the local directory you want to upload. +$container->uploadDirectory('{localDir}'); diff --git a/samples/Queues/add-message.php b/samples/Queues/add-message.php new file mode 100644 index 000000000..fce3b742c --- /dev/null +++ b/samples/Queues/add-message.php @@ -0,0 +1,48 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->queuesService(null, '{region}'); + +// You MUST set a client ID before executing any operation. This ID must be a +// valid UUID. The SDK can set a random UUID for you if you don't want to +// define your own, just leave the argument empty. +$service->setClientId(); + +// Get your queue +$queue = $service->getQueue('{queueName}'); + +// Post a new message. The body attribute specifies an arbitrary document that +// constitutes the body of the message being sent. The size of this body is +// limited to 256 KB, excluding whitespace. The ttl attribute specifies how +// long the server waits before marking the message as expired and removing it +// from the queue. The value of ttl must be between 60 and 1209600 seconds +$queue->createMessage(array( + 'body' => (object) array( // This can be anything you want + 'event' => 'foo' // and not necessarily an object + ), + 'ttl' => 2 * Datetime::DAY, +)); diff --git a/samples/Queues/claim-messages.php b/samples/Queues/claim-messages.php new file mode 100644 index 000000000..af7b81f12 --- /dev/null +++ b/samples/Queues/claim-messages.php @@ -0,0 +1,50 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->queuesService(null, '{region}'); + +// You MUST set a client ID before executing any operation. This ID must be a +// valid UUID. The SDK can set a random UUID for you if you don't want to +// define your own, just leave the argument empty. +$service->setClientId(); + +// Get your queue +$queue = $service->getQueue('{queueName}'); + +// Claim messages that fall under a few conditions: limit the claim size to 15, +// set the grace and TTL period to 5 minutes. These are just examples and may +// be replaced. +$messages = $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE +)); + +// Iterate over your claimed messages in order to perform computation on them. +foreach ($messages as $message) { + /** @var OpenCloud\Queues\Resource\Message */ +} diff --git a/samples/Queues/create-queue.php b/samples/Queues/create-queue.php new file mode 100644 index 000000000..28aa0c659 --- /dev/null +++ b/samples/Queues/create-queue.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->queuesService(null, '{region}'); + +// You MUST set a client ID before executing any operation. This ID must be a +// valid UUID. The SDK can set a random UUID for you if you don't want to +// define your own, just leave the argument empty. +$service->setClientId(); + +// Create a new queue +$queue = $service->createQueue('{queueName}'); diff --git a/samples/Queues/delete-queue.php b/samples/Queues/delete-queue.php new file mode 100644 index 000000000..1ec8db139 --- /dev/null +++ b/samples/Queues/delete-queue.php @@ -0,0 +1,39 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$service = $client->queuesService(null, '{region}'); + +// You MUST set a client ID before executing any operation. This ID must be a +// valid UUID. The SDK can set a random UUID for you if you don't want to +// define your own, just leave the argument empty. +$service->setClientId(); + +// Get an existing queue +$queue = $service->getQueue('{queueName}'); + +// Permanently delete it +$queue->delete(); From d26ce98cd2bd72455ae498b29deafc9edb421252 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 20 Nov 2014 14:23:54 +0100 Subject: [PATCH 214/835] Fixing non-PSR2 space --- samples/Queues/claim-messages.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Queues/claim-messages.php b/samples/Queues/claim-messages.php index af7b81f12..89d3825f3 100644 --- a/samples/Queues/claim-messages.php +++ b/samples/Queues/claim-messages.php @@ -46,5 +46,5 @@ // Iterate over your claimed messages in order to perform computation on them. foreach ($messages as $message) { - /** @var OpenCloud\Queues\Resource\Message */ + /** @var OpenCloud\Queues\Resource\Message */ } From d794f0b1915a96908f5319177a20c4c1fa3fe337 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 20 Nov 2014 15:07:49 +0100 Subject: [PATCH 215/835] Adding NetworkInterface --- .../Networking/Resource/NetworkInterface.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 lib/OpenCloud/Networking/Resource/NetworkInterface.php diff --git a/lib/OpenCloud/Networking/Resource/NetworkInterface.php b/lib/OpenCloud/Networking/Resource/NetworkInterface.php new file mode 100644 index 000000000..586ff63ba --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/NetworkInterface.php @@ -0,0 +1,31 @@ + Date: Thu, 20 Nov 2014 15:08:11 +0100 Subject: [PATCH 216/835] Make resources implement interface --- lib/OpenCloud/Compute/Resource/Network.php | 10 ++++++++-- lib/OpenCloud/Networking/Resource/Network.php | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/Network.php b/lib/OpenCloud/Compute/Resource/Network.php index 82aba2580..8b041df97 100644 --- a/lib/OpenCloud/Compute/Resource/Network.php +++ b/lib/OpenCloud/Compute/Resource/Network.php @@ -19,14 +19,15 @@ use Guzzle\Http\Url; use OpenCloud\Common\Exceptions; -use OpenCloud\Common\PersistentObject; +use OpenCloud\Common\Resource\PersistentResource; use OpenCloud\Compute\Constants\Network as NetworkConst; use OpenCloud\Compute\Service; +use OpenCloud\Networking\Resource\NetworkInterface; /** * The Network class represents a single virtual network */ -class Network extends PersistentObject +class Network extends PersistentResource implements NetworkInterface { public $id; public $label; @@ -154,4 +155,9 @@ public function getResourcePath() return self::$openStackResourcePath; } } + + public function getId() + { + return $this->id; + } } diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 8363b4d89..53716a8f0 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -30,7 +30,7 @@ * * @package OpenCloud\Networking\Resource */ -class Network extends PersistentResource +class Network extends PersistentResource implements NetworkInterface { protected static $url_resource = 'networks'; protected static $json_name = 'network'; @@ -71,4 +71,9 @@ public function createJson() { return parent::createJson(); } + + public function getId() + { + return $this->id; + } } From eef9a7aee314f11916acc168a1eae4672bc2b36f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 20 Nov 2014 15:08:21 +0100 Subject: [PATCH 217/835] Change createJson and add tests --- lib/OpenCloud/Compute/Resource/Server.php | 11 ++++++----- .../OpenCloud/Tests/Compute/Resource/NetworkTest.php | 1 + tests/OpenCloud/Tests/Compute/Resource/ServerTest.php | 7 ++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 20a7692e5..409aa3921 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -20,6 +20,7 @@ use OpenCloud\Common\Resource\NovaResource; use OpenCloud\DNS\Resource\HasPtrRecordsInterface; use OpenCloud\Image\Resource\ImageInterface; +use OpenCloud\Networking\Resource\NetworkInterface; use OpenCloud\Volume\Resource\Volume; use OpenCloud\Common\Exceptions; use OpenCloud\Common\Http\Message\Formatter; @@ -681,22 +682,22 @@ protected function createJson() $server->networks = array(); foreach ($this->networks as $network) { - if (!$network instanceof Network) { + if (!$network instanceof NetworkInterface) { throw new Exceptions\InvalidParameterError(sprintf( 'When creating a server, the "networks" key must be an ' . - 'array of OpenCloud\Compute\Network objects with valid ' . - 'IDs; variable passed in was a [%s]', + 'array of objects which implement OpenCloud\Networking\Resource\NetworkInterface;' . + 'variable passed in was a [%s]', gettype($network) )); } - if (empty($network->id)) { + if (!($networkId = $network->getId())) { $this->getLogger()->warning('When creating a server, the ' . 'network objects passed in must have an ID' ); continue; } // Stock networks array - $server->networks[] = (object) array('uuid' => $network->id); + $server->networks[] = (object) array('uuid' => $networkId); } } diff --git a/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php b/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php index 06ef7e0dc..35067e360 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/NetworkTest.php @@ -46,6 +46,7 @@ public function test__construct() { $this->assertEquals(NetworkConst::RAX_PUBLIC, $this->network->id); $this->assertInstanceOf('OpenCloud\Compute\Resource\Network', $this->network); + $this->assertInstanceOf('OpenCloud\Networking\Resource\NetworkInterface', $this->network); } public function test_Create() diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index eb741f96e..3d172b493 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -320,13 +320,18 @@ public function test_Image_Schedule() public function test_Create_With_Networks() { + $neutronService = $this->client->networkingService(null, 'IAD'); + $neutronNetwork = $neutronService->network(); + $neutronNetwork->setId('12345'); + $this->service->server()->create(array( 'name' => 'personality test 1', 'image' => $this->service->imageList()->first(), 'flavor' => $this->service->flavorList()->first(), 'networks' => array( $this->service->network(Network::RAX_PUBLIC), - $this->service->network() + $this->service->network(), + $neutronNetwork, ) )); } From 56c5e9e1b435e24e1bcd23935bbfda7cfa34f91a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 20 Nov 2014 12:05:31 -0800 Subject: [PATCH 218/835] Conditionally negotiate supported version. --- .../Common/Service/CatalogService.php | 3 +- lib/OpenCloud/Common/Service/Endpoint.php | 75 +++++++++++-------- lib/OpenCloud/Networking/Service.php | 1 + .../Tests/Common/Service/EndpointTest.php | 35 +++++++-- 4 files changed, 77 insertions(+), 37 deletions(-) diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index f21e47a76..42e436f49 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -27,6 +27,7 @@ abstract class CatalogService extends AbstractService { const DEFAULT_URL_TYPE = 'publicURL'; + const SUPPORTED_VERSION = 'unknown'; /** * @var string The type of this service, as set in Catalog. @@ -208,7 +209,7 @@ private function findEndpoint() // Search each service to find The One foreach ($catalog->getItems() as $service) { if ($service->hasType($this->type) && $service->hasName($this->name)) { - return Endpoint::factory($service->getEndpointFromRegion($this->region), $this->getClient()); + return Endpoint::factory($service->getEndpointFromRegion($this->region), static::SUPPORTED_VERSION, $this->getClient()); } } diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index 361411672..510835a09 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -18,7 +18,9 @@ namespace OpenCloud\Common\Service; use Guzzle\Http\Url; +use OpenCloud\Common\Http\Client; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\Common\Exceptions\UnsupportedVersionError; /** * An endpoint serves as a location which receives and emits API interactions. It will therefore also host @@ -45,18 +47,19 @@ class Endpoint /** * @param $object - * @param $client HTTP client + * @param string $supportedServiceVersion Service version supported by the SDK + * @param OpenCloud\Common\Http\Client $client HTTP client * @return Endpoint */ - public static function factory($object, $client) + public static function factory($object, $supportedServiceVersion, Client $client) { $endpoint = new self(); if (isset($object->publicURL)) { - $endpoint->setPublicUrl($this->getVersionedUrl($object->publicURL, $client)); + $endpoint->setPublicUrl($endpoint->getVersionedUrl($object->publicURL, $supportedServiceVersion, $client)); } if (isset($object->internalURL)) { - $endpoint->setPrivateUrl($this->getVersionedUrl($object->internalURL, $client)); + $endpoint->setPrivateUrl($endpoint->getVersionedUrl($object->internalURL, $supportedServiceVersion, $client)); } if (isset($object->region)) { $endpoint->setRegion($object->region); @@ -71,7 +74,7 @@ public static function factory($object, $client) */ public function setPublicUrl($publicUrl) { - $this->publicUrl = Url::factory($publicUrl); + $this->publicUrl = $publicUrl; return $this; } @@ -90,7 +93,7 @@ public function getPublicUrl() */ public function setPrivateUrl($privateUrl) { - $this->privateUrl = Url::factory($privateUrl); + $this->privateUrl = $privateUrl; return $this; } @@ -123,36 +126,46 @@ public function getRegion() } /** - * @param string $url URL of endpoint - * @param object $client HTTP client - * @return string URL of endpoint, with version + * Returns the endpoint URL with a version in it + * + * @param string $url Endpoint URL + * @param string $supportedServiceVersion Service version supported by the SDK + * @param OpenCloud\Common\Http\Client $client HTTP client + * @return Guzzle/Http/Url Endpoint URL with version in it */ - protected function getVersionedUrl($url, $client) + protected function getVersionedUrl($url, $supportedServiceVersion, Client $client) { - try { - // Make GET request to URL - $response = Formatter::decode($client->get($url)->send()); - - // Attempt to parse response and determine URL for given $version - if (!property_exists($response, 'versions')) { - return $url; - } - - foreach ($response->versions as $version) { - if ($version->status == 'CURRENT') { - foreach ($version->links as $link) { - if ($link->rel == 'self') { - return $link->href; - } + $urlObj = Url::factory($url); + + $versionRegex = '/\/[vV][0-9][0-9\.]*/'; + if (1 === preg_match($versionRegex, $url)) { + // URL has version in it; use it as-is + return $urlObj; + } + + // Make GET request to URL + $response = Formatter::decode($client->get($url)->send()); + + // Attempt to parse response and determine URL for given $version + if (!property_exists($response, 'versions')) { + throw new UnsupportedVersionError('Could not negotiate version with service.'); + } + + foreach ($response->versions as $version) { + if ($version->status == 'CURRENT' && $version->id == $supportedServiceVersion) { + foreach ($version->links as $link) { + if ($link->rel == 'self') { + return $link->href; } } } - - // If we've reached this point, we could not find a versioned - // URL in the response; return the original URL as-is - return $url; - } catch (Exception $e) { - return $url; } + + // If we've reached this point, we could not find a versioned + // URL in the response; throw an error + throw new UnsupportedVersionError(sprintf( + 'SDK supports version %s which is not currently provided by service.', + $supportedServiceVersion + )); } } diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 118ba4bde..6c54287af 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -31,6 +31,7 @@ */ class Service extends CatalogService { + const SUPPORTED_VERSION = 'v2.0'; const DEFAULT_TYPE = 'networks'; const DEFAULT_NAME = 'cloudNetworks'; diff --git a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php index 6faae1d52..c14401e7d 100644 --- a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php +++ b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php @@ -19,25 +19,50 @@ use OpenCloud\Common\Service\Endpoint; use OpenCloud\Tests\OpenCloudTestCase; +use OpenCloud\Common\Http\Client; class PublicEndpoint extends Endpoint { - public function getVersionedUrl($url, $client) + public function getVersionedUrl($url, $supportedServiceVersion, Client $client) { - return parent::getVersionedUrl($url, $client); + return parent::getVersionedUrl($url, $supportedServiceVersion, $client); } } class EndpointTest extends OpenCloudTestCase { - public function testGetVersionedUrlWithVersionLessEndpointUrl() + public function testGetVersionedUrlWithVersionLessEndpointSupportedVersionFound() { $this->addMockSubscriber($this->makeResponse('{"versions":[{"status":"CURRENT","id":"v2.0","links":[{"href":"http://hostport/v2.0","rel":"self" }]}]}', 200)); $e = new PublicEndpoint(); $expectedUrl = "http://hostport/v2.0"; - $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport", $this->client)); + $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport", 'v2.0', $this->client)); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\UnsupportedVersionError + */ + public function testGetVersionedUrlWithVersionLessEndpointSupportedVersionNotFound() + { + $this->addMockSubscriber($this->makeResponse('{"versions":[{"status":"CURRENT","id":"v2.0","links":[{"href":"http://hostport/v2.0","rel":"self" }]}]}', 200)); + + $e = new PublicEndpoint(); + + $e->getVersionedUrl("http://hostport", 'v2.1', $this->client); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\UnsupportedVersionError + */ + public function testGetVersionedUrlWithVersionLessEndpointInvalidResponse() + { + $this->addMockSubscriber($this->makeResponse('{}')); + + $e = new PublicEndpoint(); + + $e->getVersionedUrl("http://hostport", 'v2.1', $this->client); } public function testGetVersionedUrlWithVersionedEndpointUrl() @@ -47,6 +72,6 @@ public function testGetVersionedUrlWithVersionedEndpointUrl() $e = new PublicEndpoint(); $expectedUrl = "http://hostport/v1"; - $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport/v1", $this->client)); + $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport/v1", 'unknown', $this->client)); } } From c342dd6b31680ffe0599cbcadcd2800947505d42 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 20 Nov 2014 12:31:10 -0800 Subject: [PATCH 219/835] Make sure SDK-supported version of service is either CURRENT or SUPPORTED. --- lib/OpenCloud/Common/Service/Endpoint.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index 510835a09..c42c43285 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -152,7 +152,8 @@ protected function getVersionedUrl($url, $supportedServiceVersion, Client $clien } foreach ($response->versions as $version) { - if ($version->status == 'CURRENT' && $version->id == $supportedServiceVersion) { + if (($version->status == 'CURRENT' || $version->status == 'SUPPORTED') + && $version->id == $supportedServiceVersion) { foreach ($version->links as $link) { if ($link->rel == 'self') { return $link->href; From d8dfe7fb721df45031a7a9bbbad855d587682e0c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 20 Nov 2014 12:31:45 -0800 Subject: [PATCH 220/835] Fixing Networking service type in service catalog. --- lib/OpenCloud/Networking/Service.php | 2 +- tests/OpenCloud/Tests/_response/Auth.resp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 6c54287af..3c1f879e2 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -32,7 +32,7 @@ class Service extends CatalogService { const SUPPORTED_VERSION = 'v2.0'; - const DEFAULT_TYPE = 'networks'; + const DEFAULT_TYPE = 'network'; const DEFAULT_NAME = 'cloudNetworks'; /** diff --git a/tests/OpenCloud/Tests/_response/Auth.resp b/tests/OpenCloud/Tests/_response/Auth.resp index c5c1c146f..518a634e7 100644 --- a/tests/OpenCloud/Tests/_response/Auth.resp +++ b/tests/OpenCloud/Tests/_response/Auth.resp @@ -363,7 +363,7 @@ Front-End-Https: on "publicURL": "https://dfw.networks.api.rackspacecloud.com/v2.0" } ], - "type": "networks" + "type": "network" } ], "token": { From 0dc28aae9b23515e2557d4bd961b3bc7d401aa2e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 20 Nov 2014 12:49:29 -0800 Subject: [PATCH 221/835] Appeasing the PSR-2 fixer. --- lib/OpenCloud/Networking/Resource/NetworkInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/NetworkInterface.php b/lib/OpenCloud/Networking/Resource/NetworkInterface.php index 586ff63ba..30fe10de2 100644 --- a/lib/OpenCloud/Networking/Resource/NetworkInterface.php +++ b/lib/OpenCloud/Networking/Resource/NetworkInterface.php @@ -28,4 +28,4 @@ interface NetworkInterface * @return string */ public function getId(); -} \ No newline at end of file +} From bfe2c112dc9681d6a83acb2368fc957cca5b38e0 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 10:25:14 +0100 Subject: [PATCH 222/835] Adding create server with network sample --- .../Compute/create_server_with_network.php | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 samples/Compute/create_server_with_network.php diff --git a/samples/Compute/create_server_with_network.php b/samples/Compute/create_server_with_network.php new file mode 100644 index 000000000..6baaf51ba --- /dev/null +++ b/samples/Compute/create_server_with_network.php @@ -0,0 +1,56 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +$computeService = $client->computeService(null, '{region}'); +$networkService = $client->networkingService(null, '{region}'); + +// Retrieve your custom network +$customNetwork = $networkService->getNetwork('{networkId}'); + +// Set up an empty server +$server = $computeService->server(); + +// Create the server. If you do not know what imageId or flavorId to use, +// please run the list_flavors.php and list_images.php scripts. +try { + $response = $server->create(array( + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'networks' => array( + $customNetwork, + $computeService->network(Network::RAX_PRIVATE), // This is the internal Rackspace network - strongly recommended + $computeService->network(Network::RAX_PUBLIC), // This is the public internet - strong recommended + ), + )); +} catch (BadResponseException $e) { + echo $e->getResponse(); +} From 92a300e66350296aaa38f25c5d7dfb539bb63f1b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 12:50:16 +0100 Subject: [PATCH 223/835] Standardizing DB samples --- samples/Database/create-configuration.php | 31 +++++++---------- samples/Database/create-instance.php | 34 ++++++------------- samples/Database/delete-configuration.php | 26 +++++--------- samples/Database/get-configuration.php | 26 +++++--------- samples/Database/get-datastore-version.php | 29 ++++++---------- samples/Database/get-datastore.php | 26 +++++--------- samples/Database/get-instance.php | 26 +++++--------- .../Database/list-configuration-instances.php | 26 +++++--------- samples/Database/list-configurations.php | 23 +++++-------- samples/Database/list-datastore-versions.php | 26 +++++--------- samples/Database/list-datastores.php | 23 +++++-------- samples/Database/list-instances.php | 23 +++++-------- samples/Database/patch-configuration.php | 26 +++++--------- samples/Database/replace-configuration.php | 26 +++++--------- samples/Database/update-instance.php | 27 +++++---------- 15 files changed, 138 insertions(+), 260 deletions(-) diff --git a/samples/Database/create-configuration.php b/samples/Database/create-configuration.php index c744404ef..d884af0d5 100644 --- a/samples/Database/create-configuration.php +++ b/samples/Database/create-configuration.php @@ -15,38 +15,31 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * OS_REGION_NAME: OpenStack Cloud region where database configuations are created -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Create a configuration. $configuration = $databaseService->configuration(); $configuration->create(array( - 'name' => 'example-configuration-name', + 'name' => 'example-configuration-name', 'description' => 'An example configuration', - 'values' => array( + 'values' => array( 'collation_server' => 'latin1_swedish_ci', - 'connect_timeout' => 120 + 'connect_timeout' => 120 ), 'datastore' => array( - 'type' => '10000000-0000-0000-0000-000000000001', + 'type' => '10000000-0000-0000-0000-000000000001', 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' ) )); diff --git a/samples/Database/create-instance.php b/samples/Database/create-instance.php index cd31522c8..ba7117bb5 100644 --- a/samples/Database/create-instance.php +++ b/samples/Database/create-instance.php @@ -15,36 +15,24 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, -// * OS_DB_INSTANCE_FLAVOR_ID: ID of flavor to use for database instance, -// * OS_DB_INSTANCE_NAME: Name of database instance, and -// * OS_DB_INSTANCE_SIZE: Size, in GB, of database instance volume -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Create a database instance. $dbInstance = $databaseService->instance(); $dbInstance->create(array( - 'name' => getenv('OS_DB_INSTANCE_NAME'), - 'flavor' => $databaseService->flavor(getenv('OS_DB_INSTANCE_FLAVOR_ID')), - 'volume' => array( - 'size' => getenv('OS_DB_INSTANCE_SIZE') - ) + 'name' => '{name}', + 'flavor' => $databaseService->flavor('{flavorId}'), + 'volume' => array('size' => '{size}') )); diff --git a/samples/Database/delete-configuration.php b/samples/Database/delete-configuration.php index 4fc2d7460..144ef228b 100644 --- a/samples/Database/delete-configuration.php +++ b/samples/Database/delete-configuration.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, and -// * OS_DB_CONFIGURATION_ID: ID of database configuration -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database configuration. -$configuration = $databaseService->configuration(getenv('OS_DB_CONFIGURATION_ID')); +$configuration = $databaseService->configuration('{configId}'); // 4. Delete it. $configuration->delete(); diff --git a/samples/Database/get-configuration.php b/samples/Database/get-configuration.php index 70c93c89c..352364c4c 100644 --- a/samples/Database/get-configuration.php +++ b/samples/Database/get-configuration.php @@ -15,28 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, and -// * OS_DB_CONFIGURATION_ID: ID of database configuration -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database configuration. -$configuration = $databaseService->configuration(getenv('OS_DB_CONFIGURATION_ID')); +$configuration = $databaseService->configuration('{configId}'); /** @var OpenCloud\Database\Resource\Configuration **/ diff --git a/samples/Database/get-datastore-version.php b/samples/Database/get-datastore-version.php index 9f8a1bc78..137a088e6 100644 --- a/samples/Database/get-datastore-version.php +++ b/samples/Database/get-datastore-version.php @@ -15,32 +15,23 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, -// * OS_DB_DATASTORE_ID: ID of database datastore, and -// * OS_DB_DATASTORE_VERSION_ID: Id of database datastore version -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database datastore. -$datastore = $databaseService->datastore(getenv('OS_DB_DATASTORE_ID')); +$datastore = $databaseService->datastore('{datastoreId}'); // 4. Retrieve the database datastore version. -$datastoreVersion = $datastore->version(getenv('OS_DB_DATASTORE_VERSION_ID')); +$datastoreVersion = $datastore->version('{datastoreVersionId}'); /** @var OpenCloud\Database\Resource\DatastoreVersion **/ diff --git a/samples/Database/get-datastore.php b/samples/Database/get-datastore.php index 19e0c7ca2..5c93d0615 100644 --- a/samples/Database/get-datastore.php +++ b/samples/Database/get-datastore.php @@ -15,28 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, and -// * OS_DB_DATASTORE_ID: ID of database datastore -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database datastore. -$datastore = $databaseService->datastore(getenv('OS_DB_DATASTORE_ID')); +$datastore = $databaseService->datastore('{datastoreId}'); /** @var OpenCloud\Database\Resource\Datastore **/ diff --git a/samples/Database/get-instance.php b/samples/Database/get-instance.php index 0696c683f..21ccdd420 100644 --- a/samples/Database/get-instance.php +++ b/samples/Database/get-instance.php @@ -15,28 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, and -// * OS_DB_INSTANCE_ID: ID of database instance -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database instance. -$instance = $databaseService->instance(getenv('OS_DB_INSTANCE_ID')); +$instance = $databaseService->instance('{instanceId}'); /** @var $instance OpenCloud\Database\Resource\Instance **/ diff --git a/samples/Database/list-configuration-instances.php b/samples/Database/list-configuration-instances.php index e86365ec4..0bb9cd18e 100644 --- a/samples/Database/list-configuration-instances.php +++ b/samples/Database/list-configuration-instances.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, and -// * OS_DB_CONFIGURATION_ID: ID of database configuration -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database configuration. -$configuration = $databaseService->configuration(getenv('OS_DB_CONFIGURATION_ID')); +$configuration = $databaseService->configuration('{configId}'); // 4. List instances using this configuration. $instances = $configuration->instanceList(); diff --git a/samples/Database/list-configurations.php b/samples/Database/list-configurations.php index 5755400f2..a25f87cc9 100644 --- a/samples/Database/list-configurations.php +++ b/samples/Database/list-configurations.php @@ -15,26 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * OS_REGION_NAME: OpenStack Cloud region where database configuations are created -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{dbId}'); // 3. List configurations. $configurations = $databaseService->configurationList(); diff --git a/samples/Database/list-datastore-versions.php b/samples/Database/list-datastore-versions.php index 6fab90b06..3f2328f30 100644 --- a/samples/Database/list-datastore-versions.php +++ b/samples/Database/list-datastore-versions.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database version, and -// * OS_DB_DATASTORE_ID: ID of database datastore -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database datastore. -$datastore = $databaseService->datastore(getenv('OS_DB_DATASTORE_ID')); +$datastore = $databaseService->datastore('{datastoreId}'); // 4. List versions using this datastore. $versions = $datastore->versionList(); diff --git a/samples/Database/list-datastores.php b/samples/Database/list-datastores.php index 3cfdebc18..5d3389111 100644 --- a/samples/Database/list-datastores.php +++ b/samples/Database/list-datastores.php @@ -15,26 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * OS_REGION_NAME: OpenStack Cloud region where database configuations are created -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. List datastores. $datastores = $databaseService->datastoreList(); diff --git a/samples/Database/list-instances.php b/samples/Database/list-instances.php index 077c32a35..6edd22f7a 100644 --- a/samples/Database/list-instances.php +++ b/samples/Database/list-instances.php @@ -15,26 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * OS_REGION_NAME: OpenStack Cloud region where database instances are created -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. List instances. $instances = $databaseService->instanceList(); diff --git a/samples/Database/patch-configuration.php b/samples/Database/patch-configuration.php index e403d5dc2..60488021e 100644 --- a/samples/Database/patch-configuration.php +++ b/samples/Database/patch-configuration.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * OS_REGION_NAME: OpenStack Cloud region where database configuations are created -// * OS_DB_CONFIGURATION_ID: ID of database configuration -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database configuration. -$configuration = $databaseService->configuration(getenv('OS_DB_CONFIGURATION_ID')); +$configuration = $databaseService->configuration('{configId}'); // 3. Patch it. $configuration->patch(array( diff --git a/samples/Database/replace-configuration.php b/samples/Database/replace-configuration.php index b1309c717..9646f3773 100644 --- a/samples/Database/replace-configuration.php +++ b/samples/Database/replace-configuration.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * OS_REGION_NAME: OpenStack Cloud region where database configuations are created -// * OS_DB_CONFIGURATION_ID: ID of database configuration -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database configuration. -$configuration = $databaseService->configuration(getenv('OS_DB_CONFIGURATION_ID')); +$configuration = $databaseService->configuration('{configId}'); // 3. Replace it. $configuration->update(array( diff --git a/samples/Database/update-instance.php b/samples/Database/update-instance.php index 9ded49b84..81389aab8 100644 --- a/samples/Database/update-instance.php +++ b/samples/Database/update-instance.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * OS_REGION_NAME: OpenStack Cloud region in which to create database instance, -// * OS_DB_INSTANCE_ID: ID of database instance, and -// * OS_DB_CONFIGURATION_ID: ID of database configuration -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain a Databae service object from the client. -$region = getenv('OS_REGION_NAME'); -$databaseService = $client->databaseService(null, $region); +// 2. Obtain a Database service object from the client. +$databaseService = $client->databaseService(null, '{region}'); // 3. Retrieve the database instance. -$instance = $databaseService->instance(getenv('OS_DB_INSTANCE_ID')); +$instance = $databaseService->instance('{instanceId}'); // 4. Update it with a configuration. $instance->update(array( From 915c9ba9f2fd350746ce2ee229c8fef4efe0135f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 12:58:35 +0100 Subject: [PATCH 224/835] Standardizing Networking samples --- samples/Networking/create-network.php | 26 +++++------- samples/Networking/create-networks.php | 26 +++++------- samples/Networking/create-port.php | 31 +++++--------- samples/Networking/create-ports.php | 36 ++++++----------- .../create-subnet-with-gateway-ip.php | 33 ++++++--------- .../create-subnet-with-host-routes.php | 35 ++++++---------- samples/Networking/create-subnet.php | 33 ++++++--------- samples/Networking/create-subnets.php | 40 +++++++------------ samples/Networking/delete-network.php | 29 +++++--------- samples/Networking/delete-port.php | 29 +++++--------- samples/Networking/delete-subnet.php | 29 +++++--------- samples/Networking/get-network.php | 29 +++++--------- samples/Networking/get-port.php | 29 +++++--------- samples/Networking/get-subnet.php | 29 +++++--------- samples/Networking/list-networks.php | 26 +++++------- samples/Networking/list-ports.php | 26 +++++------- samples/Networking/list-subnets.php | 26 +++++------- samples/Networking/update-network.php | 29 +++++--------- samples/Networking/update-port.php | 35 ++++++---------- samples/Networking/update-subnet.php | 29 +++++--------- 20 files changed, 213 insertions(+), 392 deletions(-) diff --git a/samples/Networking/create-network.php b/samples/Networking/create-network.php index 02592bfb0..70ed6d9bb 100644 --- a/samples/Networking/create-network.php +++ b/samples/Networking/create-network.php @@ -15,27 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create a network. $network = $networkingService->createNetwork(array( diff --git a/samples/Networking/create-networks.php b/samples/Networking/create-networks.php index 6db031be0..aa8581364 100644 --- a/samples/Networking/create-networks.php +++ b/samples/Networking/create-networks.php @@ -15,27 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create multiple networks. $networks = $networkingService->createNetworks(array( diff --git a/samples/Networking/create-port.php b/samples/Networking/create-port.php index 753211bde..5494b0c03 100644 --- a/samples/Networking/create-port.php +++ b/samples/Networking/create-port.php @@ -15,32 +15,23 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network in which to create port -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create a port. $port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => getenv('NETWORK_ID') + 'name' => 'My port', + 'networkId' => '{networkId}' )); /** @var $port OpenCloud\Networking\Resource\Port **/ diff --git a/samples/Networking/create-ports.php b/samples/Networking/create-ports.php index 00ec0228a..3bcfe3201 100644 --- a/samples/Networking/create-ports.php +++ b/samples/Networking/create-ports.php @@ -15,39 +15,29 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, -// * NETWORK_ID_1: ID of network in which to create port 1, and -// * NETWORK_ID_2: ID of network in which to create port 2 -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create multiple ports. $ports = $networkingService->createPorts(array( array( - 'name' => 'My port #1', - 'networkId' => getenv('NETWORK_ID_1') + 'name' => 'My port #1', + 'networkId' => '{networkId}' ), array( - 'name' => 'My port #2', - 'networkId' => getenv('NETWORK_ID_2') + 'name' => 'My port #2', + 'networkId' => '{networkId}' ) )); diff --git a/samples/Networking/create-subnet-with-gateway-ip.php b/samples/Networking/create-subnet-with-gateway-ip.php index 512f49e49..646d0e56f 100644 --- a/samples/Networking/create-subnet-with-gateway-ip.php +++ b/samples/Networking/create-subnet-with-gateway-ip.php @@ -15,35 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network in which to create subnet -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create a subnet. $subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => getenv('NETWORK_ID'), + 'name' => 'My subnet', + 'networkId' => '{networkId}', 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25', + 'cidr' => '192.168.199.0/25', 'gatewayIp' => '192.168.199.128' )); /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/create-subnet-with-host-routes.php b/samples/Networking/create-subnet-with-host-routes.php index 2e20e7b87..3a2ea5e44 100644 --- a/samples/Networking/create-subnet-with-host-routes.php +++ b/samples/Networking/create-subnet-with-host-routes.php @@ -15,35 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network in which to create subnet -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create a subnet. $subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => getenv('NETWORK_ID'), - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/24', + 'name' => 'My subnet', + 'networkId' => '{networkId}', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/24', 'hostRoutes' => array( array( 'destination' => '1.1.1.0/24', diff --git a/samples/Networking/create-subnet.php b/samples/Networking/create-subnet.php index 2bd94c561..85cbf6462 100644 --- a/samples/Networking/create-subnet.php +++ b/samples/Networking/create-subnet.php @@ -15,34 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network in which to create subnet -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create a subnet. $subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => getenv('NETWORK_ID'), + 'name' => 'My subnet', + 'networkId' => '{networkId}', 'ipVersion' => 4, - 'cidr' => '192.168.199.0/24' + 'cidr' => '192.168.199.0/24' )); /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/create-subnets.php b/samples/Networking/create-subnets.php index 0c74167de..849b00c8a 100644 --- a/samples/Networking/create-subnets.php +++ b/samples/Networking/create-subnets.php @@ -15,43 +15,33 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, -// * NETWORK_ID_1: ID of network in which to create port 1, and -// * NETWORK_ID_2: ID of network in which to create port 2 -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Create multiple subnets. $subnets = $networkingService->createSubnets(array( array( - 'name' => 'My subnet #1', - 'networkId' => getenv('NETWORK_ID_1'), + 'name' => 'My subnet #1', + 'networkId' => '{networkId1}', 'ipVersion' => 4, - 'cidr' => '192.168.199.0/24' + 'cidr' => '192.168.199.0/24' ), array( - 'name' => 'My subnet #2', - 'networkId' => getenv('NETWORK_ID_2'), + 'name' => 'My subnet #2', + 'networkId' => '{networkId2}', 'ipVersion' => 4, - 'cidr' => '10.56.4.0/22' + 'cidr' => '10.56.4.0/22' ) )); diff --git a/samples/Networking/delete-network.php b/samples/Networking/delete-network.php index 88b7b8a38..12b7a6d8f 100644 --- a/samples/Networking/delete-network.php +++ b/samples/Networking/delete-network.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); +$network = $networkingService->getNetwork('{networkId}'); // 4. Delete network. $network->delete(); diff --git a/samples/Networking/delete-port.php b/samples/Networking/delete-port.php index 1f840b02e..c64792eb6 100644 --- a/samples/Networking/delete-port.php +++ b/samples/Networking/delete-port.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * PORT_ID: ID of port -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get port. -$port = $networkingService->getPort(getenv('PORT_ID')); +$port = $networkingService->getPort('{portId}'); // 4. Delete port. $port->delete(); diff --git a/samples/Networking/delete-subnet.php b/samples/Networking/delete-subnet.php index 0e8dfa5dc..e0b131c4f 100644 --- a/samples/Networking/delete-subnet.php +++ b/samples/Networking/delete-subnet.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * SUBNET_ID: ID of subnet -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get subnet. -$subnet = $networkingService->getSubnet(getenv('SUBNET_ID')); +$subnet = $networkingService->getSubnet('{subnetId}'); // 4. Delete subnet. $subnet->delete(); diff --git a/samples/Networking/get-network.php b/samples/Networking/get-network.php index f9cb456ce..2d62fcdc6 100644 --- a/samples/Networking/get-network.php +++ b/samples/Networking/get-network.php @@ -15,29 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); +$network = $networkingService->getNetwork('{networkId}'); /** @var $network OpenCloud\Networking\Resource\Network **/ diff --git a/samples/Networking/get-port.php b/samples/Networking/get-port.php index 33749a2be..e4f8dcda9 100644 --- a/samples/Networking/get-port.php +++ b/samples/Networking/get-port.php @@ -15,29 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * PORT_ID: ID of port -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get port. -$port = $networkingService->getPort(getenv('PORT_ID')); +$port = $networkingService->getPort('{portId}'); /** @var $port OpenCloud\Networking\Resource\Port **/ diff --git a/samples/Networking/get-subnet.php b/samples/Networking/get-subnet.php index 7397d9226..926310a6a 100644 --- a/samples/Networking/get-subnet.php +++ b/samples/Networking/get-subnet.php @@ -15,29 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * SUBNET_ID: ID of subnet -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get subnet. -$subnet = $networkingService->getSubnet(getenv('SUBNET_ID')); +$subnet = $networkingService->getSubnet('{subnetId}'); /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ diff --git a/samples/Networking/list-networks.php b/samples/Networking/list-networks.php index e1d3abb87..eceb5b889 100644 --- a/samples/Networking/list-networks.php +++ b/samples/Networking/list-networks.php @@ -15,27 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. List networks $networks = $networkingService->listNetworks(); diff --git a/samples/Networking/list-ports.php b/samples/Networking/list-ports.php index 5ddbe7e4f..4f2446161 100644 --- a/samples/Networking/list-ports.php +++ b/samples/Networking/list-ports.php @@ -15,27 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. List ports $ports = $networkingService->listPorts(); diff --git a/samples/Networking/list-subnets.php b/samples/Networking/list-subnets.php index c926d5e71..009e46ff8 100644 --- a/samples/Networking/list-subnets.php +++ b/samples/Networking/list-subnets.php @@ -15,27 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. List subnets $subnets = $networkingService->listSubnets(); diff --git a/samples/Networking/update-network.php b/samples/Networking/update-network.php index ec0a2ee50..94748471a 100644 --- a/samples/Networking/update-network.php +++ b/samples/Networking/update-network.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * NETWORK_ID: ID of network -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get network. -$network = $networkingService->getNetwork(getenv('NETWORK_ID')); +$network = $networkingService->getNetwork('{networkId}'); // 4. Update network. $network->update(array( diff --git a/samples/Networking/update-port.php b/samples/Networking/update-port.php index 59e174856..abc1cbaf4 100644 --- a/samples/Networking/update-port.php +++ b/samples/Networking/update-port.php @@ -15,41 +15,30 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, -// * PORT_ID: ID of port, -// * SUBNET_ID: ID of subnet for new fixed IP, and -// * IP_ADDRESS: IP address for new fixed IP -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get port. -$port = $networkingService->getPort(getenv('PORT_ID')); +$port = $networkingService->getPort('{portId}'); // 4. Update port. $port->update(array( 'name' => 'My updated port', 'fixedIps' => array( array( - 'subnetId' => getenv('SUBNET_ID'), - 'ipAddress' => getenv('IP_ADDRESS') + 'subnetId' => '{subnetId}', + 'ipAddress' => '{ipAddress}' ) ) )); diff --git a/samples/Networking/update-subnet.php b/samples/Networking/update-subnet.php index 6f74f9ffa..1259fd0d7 100644 --- a/samples/Networking/update-subnet.php +++ b/samples/Networking/update-subnet.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * SUBNET_ID: ID of subnet -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\OpenStack; +use OpenCloud\Rackspace; -// 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// 2. Obtain an Networking service object from the client. -$region = getenv('OS_REGION_NAME'); -$networkingService = $client->networkingService(null, $region); +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); // 3. Get subnet. -$subnet = $networkingService->getSubnet(getenv('SUBNET_ID')); +$subnet = $networkingService->getSubnet('{subnetId}'); // 4. Update subnet. $subnet->update(array( From 51865d60dd76cc51b22a57937c318715c8107c98 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 12:59:03 +0100 Subject: [PATCH 225/835] Forgot one --- samples/Database/update-instance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Database/update-instance.php b/samples/Database/update-instance.php index 81389aab8..53ca46326 100644 --- a/samples/Database/update-instance.php +++ b/samples/Database/update-instance.php @@ -34,5 +34,5 @@ // 4. Update it with a configuration. $instance->update(array( - 'configuration' => getenv('OS_DB_CONFIGURATION_ID') + 'configuration' => '{configId}' )); From bbe3514e01e97817902c9eaef23270e93f2dbc10 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Fri, 21 Nov 2014 13:02:18 +0100 Subject: [PATCH 226/835] Standardize Volume samples --- samples/Volume/create-bootable-volume.php | 19 +++++-------------- samples/Volume/create-snapshot.php | 20 +++++--------------- samples/Volume/create-volume.php | 16 ++++------------ samples/Volume/delete-snapshot.php | 20 +++++--------------- samples/Volume/delete-volume.php | 20 +++++--------------- samples/Volume/get-snapshot.php | 20 +++++--------------- samples/Volume/get-volume.php | 20 +++++--------------- samples/Volume/list-snapshots.php | 20 +++++--------------- samples/Volume/list-volumes.php | 16 ++++------------ samples/Volume/rename-snapshot.php | 20 +++++--------------- samples/Volume/rename-volume.php | 20 +++++--------------- 11 files changed, 53 insertions(+), 158 deletions(-) diff --git a/samples/Volume/create-bootable-volume.php b/samples/Volume/create-bootable-volume.php index 9a5f61f4e..687b79b81 100644 --- a/samples/Volume/create-bootable-volume.php +++ b/samples/Volume/create-bootable-volume.php @@ -15,26 +15,17 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_BOOT_IMAGE_ID: ID of image with bootable operating system on it -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Create the volume. Specify a name and size (in GB). You may optionally // specify a volume type, which is either 'SSD' (faster, more expensive), or @@ -45,5 +36,5 @@ 'size' => 1000, 'volume_type' => 'SSD', 'bootable' => true, - 'imageRef' => getenv('RAX_BOOT_IMAGE_ID') + 'imageRef' => '{imageId}' )); diff --git a/samples/Volume/create-snapshot.php b/samples/Volume/create-snapshot.php index b04199a1c..49b16fb1d 100644 --- a/samples/Volume/create-snapshot.php +++ b/samples/Volume/create-snapshot.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * RAX_VOLUME_ID: ID of the volume you want to snapshot. Run create-volume.php if -// you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Snapshot service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the volume you want to snapshot. -$volume = $volumeService->volume(getenv('RAX_VOLUME_ID')); +$volume = $volumeService->volume('{volumeId}'); // 4. Create the snapshot. Specify a name and the volume ID. $snapshot = $volumeService->snapshot(); diff --git a/samples/Volume/create-volume.php b/samples/Volume/create-volume.php index 47a6b2610..d950f4f70 100644 --- a/samples/Volume/create-volume.php +++ b/samples/Volume/create-volume.php @@ -15,25 +15,17 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Create the volume. Specify a name and size (in GB). You may optionally // specify a volume type, which is either 'SSD' (faster, more expensive), or diff --git a/samples/Volume/delete-snapshot.php b/samples/Volume/delete-snapshot.php index ec237e85e..09012d60b 100644 --- a/samples/Volume/delete-snapshot.php +++ b/samples/Volume/delete-snapshot.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_SNAPSHOT_ID: ID of the snapshot you want to update. Run create-snapshot.php if -// you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the snapshot. -$snapshot = $volumeService->snapshot(getenv('RAX_SNAPSHOT_ID')); +$snapshot = $volumeService->snapshot('{snapshotId}'); // 4. Delete it. $snapshot->delete(); diff --git a/samples/Volume/delete-volume.php b/samples/Volume/delete-volume.php index 60d12b0be..d68210f47 100644 --- a/samples/Volume/delete-volume.php +++ b/samples/Volume/delete-volume.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_VOLUME_ID: ID of the volume you want to update. Run create-volume.php if -// you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the volume. -$volume = $volumeService->volume(getenv('RAX_VOLUME_ID')); +$volume = $volumeService->volume('{volumeId}'); // 4. Delete it. $volume->delete(); diff --git a/samples/Volume/get-snapshot.php b/samples/Volume/get-snapshot.php index ad3db4d00..ff0896c6f 100644 --- a/samples/Volume/get-snapshot.php +++ b/samples/Volume/get-snapshot.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_SNAPSHOT_ID: ID of the volume you want to get. Run create-volume.php if -// you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the volume. -$snapshot = $volumeService->snapshot(getenv('RAX_SNAPSHOT_ID')); +$snapshot = $volumeService->snapshot('{snapshotId}'); /** @var $snapshot OpenCloud\Volume\Resource\Snapshot **/ echo "ID: " . $snapshot->id() . " | " . "Name: " . $snapshot->name() . PHP_EOL; diff --git a/samples/Volume/get-volume.php b/samples/Volume/get-volume.php index 172726156..e88ba7e7c 100644 --- a/samples/Volume/get-volume.php +++ b/samples/Volume/get-volume.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_VOLUME_ID: ID of the volume you want to get. Run create-volume.php if -// you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the volume. -$volume = $volumeService->volume(getenv('RAX_VOLUME_ID')); +$volume = $volumeService->volume('{volumeId}'); /** @var $volume OpenCloud\Volume\Resource\Volume **/ echo "ID: " . $volume->id() . " | " . "Name: " . $volume->name() . PHP_EOL; diff --git a/samples/Volume/list-snapshots.php b/samples/Volume/list-snapshots.php index 9e2db32fc..0e1f2f8b1 100644 --- a/samples/Volume/list-snapshots.php +++ b/samples/Volume/list-snapshots.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * RAX_VOLUME_ID: ID of the volume whose snapshots you want to list. Run -// create-volume.php if you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the volume you want to snapshot. -$volume = $volumeService->volume(getenv('RAX_VOLUME_ID')); +$volume = $volumeService->volume('{volumeId}'); // 4. Get snapshot list for volume. $snapshots = $volumeService->snapshotList(array( diff --git a/samples/Volume/list-volumes.php b/samples/Volume/list-volumes.php index 5a18b6ca5..18003855e 100644 --- a/samples/Volume/list-volumes.php +++ b/samples/Volume/list-volumes.php @@ -15,25 +15,17 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get volume list. $volumes = $volumeService->volumeList(); diff --git a/samples/Volume/rename-snapshot.php b/samples/Volume/rename-snapshot.php index db407721f..47e9f0dc5 100644 --- a/samples/Volume/rename-snapshot.php +++ b/samples/Volume/rename-snapshot.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_SNAPSHOT_ID: ID of the snapshot you want to rename. Run create-snapshot.php -// if you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the snapshot. -$snapshot = $volumeService->snapshot(getenv('RAX_SNAPSHOT_ID')); +$snapshot = $volumeService->snapshot('{snapshotId}'); // 4. Update its name and description. $snapshot->rename(array( diff --git a/samples/Volume/rename-volume.php b/samples/Volume/rename-volume.php index e886abb87..4855dc7dd 100644 --- a/samples/Volume/rename-volume.php +++ b/samples/Volume/rename-volume.php @@ -15,30 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * RAX_VOLUME_ID: ID of the volume you want to rename. Run create-volume.php if -// you need to create one first. -// - require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; // 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain a Volume service object from the client. -$region = 'DFW'; -$volumeService = $client->volumeService(null, $region); +$volumeService = $client->volumeService(null, '{region}'); // 3. Get the volume. -$volume = $volumeService->volume(getenv('RAX_VOLUME_ID')); +$volume = $volumeService->volume('{volumeId}'); // 4. Update its name and description. $volume->rename(array( From 84a84850b830b18fb8790e77c4175580d6f87a91 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 14:17:11 +0100 Subject: [PATCH 227/835] Shifting OS samples to placeholders --- .../auto-extract-archive-files.php | 38 ++++++--------- samples/ObjectStore/bulk-delete.php | 36 +++++---------- samples/ObjectStore/cleanup.php | 22 ++++----- samples/ObjectStore/copy-object.php | 35 +++++--------- samples/ObjectStore/create-container.php | 22 ++++----- .../delete-container-recursive.php | 26 ++++------- samples/ObjectStore/delete-container.php | 25 ++++------ samples/ObjectStore/delete-object.php | 29 ++++-------- samples/ObjectStore/disable-container-cdn.php | 24 ++++------ samples/ObjectStore/enable-container-cdn.php | 24 ++++------ .../ObjectStore/get-account-bytes-used.php | 20 +++----- .../get-account-container-count.php | 20 +++----- .../ObjectStore/get-account-object-count.php | 20 +++----- .../get-account-temp-url-secret.php | 20 +++----- .../ObjectStore/get-cdn-object-http-url.php | 33 ++++--------- .../ObjectStore/get-cdn-object-https-url.php | 33 ++++--------- .../get-cdn-object-ios-streaming-url.php | 33 ++++--------- .../get-cdn-object-streaming-url.php | 33 ++++--------- .../ObjectStore/get-container-bytes-quota.php | 24 ++++------ .../ObjectStore/get-container-bytes-used.php | 24 ++++------ .../ObjectStore/get-container-count-quota.php | 24 ++++------ .../ObjectStore/get-container-metadata.php | 26 ++++------- .../get-container-object-count.php | 24 ++++------ samples/ObjectStore/get-container.php | 24 ++++------ samples/ObjectStore/get-object-metadata.php | 29 ++++-------- .../ObjectStore/get-object-temporary-url.php | 45 ++++++++---------- samples/ObjectStore/get-object.php | 34 +++++--------- samples/ObjectStore/list-containers.php | 20 +++----- .../list-objects-with-limit-and-prefix.php | 38 ++++++--------- .../ObjectStore/list-objects-with-limit.php | 35 ++++++-------- .../ObjectStore/list-objects-with-prefix.php | 34 ++++++-------- samples/ObjectStore/list-objects.php | 25 ++++------ .../pseudo-hierarchical-folders.php | 38 ++++++--------- samples/ObjectStore/quickstart.php | 33 ++++++------- .../set-account-temp-url-secret-specified.php | 24 ++++------ .../set-account-temp-url-secret.php | 20 +++----- samples/ObjectStore/set-cdn-container-ttl.php | 26 ++++------- .../ObjectStore/set-container-bytes-quota.php | 29 ++++-------- .../ObjectStore/set-container-count-quota.php | 29 ++++-------- .../ObjectStore/set-container-metadata.php | 26 ++++------- .../update-object-content-type.php | 31 ++++--------- .../ObjectStore/update-object-metadata.php | 31 ++++--------- samples/ObjectStore/upload-large-object.php | 38 ++++++--------- .../upload-multiple-objects-with-metadata.php | 46 ++++++++----------- .../ObjectStore/upload-multiple-objects.php | 32 +++++-------- .../upload-object-with-content-type.php | 44 +++++++----------- .../upload-object-with-metadata.php | 46 ++++++++----------- samples/ObjectStore/upload-object.php | 38 ++++++--------- 48 files changed, 503 insertions(+), 927 deletions(-) diff --git a/samples/ObjectStore/auto-extract-archive-files.php b/samples/ObjectStore/auto-extract-archive-files.php index bd6a90957..9b61a0386 100644 --- a/samples/ObjectStore/auto-extract-archive-files.php +++ b/samples/ObjectStore/auto-extract-archive-files.php @@ -15,35 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; -use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); - -// 3. Upload archive file for auto-extraction into objects. -$localArchiveFileName = __DIR__ . '/logos.tar.gz'; -$remotePath = 'logos/'; +$objectStoreService = $client->objectStoreService(null, '{region}'); -$fileData = fopen($localArchiveFileName, 'r'); -$objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); +// 3. Open local file +$fileData = fopen('{localArchiveFilePath}', 'r'); -// Note that while we call fopen to open the file resource, we do not call fclose at the end. -// The file resource is automatically closed inside the bulkExtract call. +// 4. Upload archive for auto-extraction. Note that while we call fopen to open +// the file resource, we do not call fclose at the end. The file resource is +// automatically closed inside the bulkExtract call. +$objectStoreService->bulkExtract('{remotePath}', $fileData, UrlType::TAR_GZ); diff --git a/samples/ObjectStore/bulk-delete.php b/samples/ObjectStore/bulk-delete.php index d9b2d902c..4bcedda7d 100644 --- a/samples/ObjectStore/bulk-delete.php +++ b/samples/ObjectStore/bulk-delete.php @@ -15,38 +15,24 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains two objects named 'php-elephant.jpg' and -// 'python-snakes.jpg'. Run upload-multiple-objects.php if you need to create -// them first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Constants\UrlType; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); -// 3. Just to demonstrate bulk delete, create an empty container. -$objectStoreService->createContainer('some_empty_container'); - -// 4. Bulk delete objects and empty containers. +// 3. Bulk delete objects and empty containers. $response = $objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' + '{container1}/{objectName}', + '{container2}/{objectName}', + '{container3}' )); diff --git a/samples/ObjectStore/cleanup.php b/samples/ObjectStore/cleanup.php index c54227ce6..3d152c18b 100644 --- a/samples/ObjectStore/cleanup.php +++ b/samples/ObjectStore/cleanup.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// Instantiate Rackspace client -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); -// Instantiate Object Store service -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); // Get all containers $containers = $objectStoreService->listContainers(); diff --git a/samples/ObjectStore/copy-object.php b/samples/ObjectStore/copy-object.php index 989ab2a91..f0996696b 100644 --- a/samples/ObjectStore/copy-object.php +++ b/samples/ObjectStore/copy-object.php @@ -15,39 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{oldContainerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Create another container for object's copy. -$objectStoreService->createContainer('logos_copy'); +$objectStoreService->createContainer('{newContainerName}'); // 6. Copy object to another container. -$object->copy('logos_copy/php.jpg'); +$object->copy('{newContainerName}/{objectName}'); diff --git a/samples/ObjectStore/create-container.php b/samples/ObjectStore/create-container.php index 206c8f883..c0f5a4346 100644 --- a/samples/ObjectStore/create-container.php +++ b/samples/ObjectStore/create-container.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Create a container for your objects (also referred to as files). -$container = $objectStoreService->createContainer('logos'); +$container = $objectStoreService->createContainer('{containerName}'); diff --git a/samples/ObjectStore/delete-container-recursive.php b/samples/ObjectStore/delete-container-recursive.php index fa0724583..37b47d8ba 100644 --- a/samples/ObjectStore/delete-container-recursive.php +++ b/samples/ObjectStore/delete-container-recursive.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Delete container. +// 4. Delete container and all its contents. $container->delete(true); diff --git a/samples/ObjectStore/delete-container.php b/samples/ObjectStore/delete-container.php index 2f120d44d..7663e0140 100644 --- a/samples/ObjectStore/delete-container.php +++ b/samples/ObjectStore/delete-container.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container is empty. That is, it has no objects in it. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Delete container. $container->delete(); diff --git a/samples/ObjectStore/delete-object.php b/samples/ObjectStore/delete-object.php index 7d3383d3e..5cb8f3570 100644 --- a/samples/ObjectStore/delete-object.php +++ b/samples/ObjectStore/delete-object.php @@ -15,36 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Delete object. $object->delete(); diff --git a/samples/ObjectStore/disable-container-cdn.php b/samples/ObjectStore/disable-container-cdn.php index 4133a37f8..c0587f640 100644 --- a/samples/ObjectStore/disable-container-cdn.php +++ b/samples/ObjectStore/disable-container-cdn.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Disable CDN for the container. $container->disableCdn(); diff --git a/samples/ObjectStore/enable-container-cdn.php b/samples/ObjectStore/enable-container-cdn.php index 0d614ed98..ebd94c2ae 100644 --- a/samples/ObjectStore/enable-container-cdn.php +++ b/samples/ObjectStore/enable-container-cdn.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Enable CDN for the container. $container->enableCdn(); diff --git a/samples/ObjectStore/get-account-bytes-used.php b/samples/ObjectStore/get-account-bytes-used.php index 62a728b9e..2ad0239f7 100644 --- a/samples/ObjectStore/get-account-bytes-used.php +++ b/samples/ObjectStore/get-account-bytes-used.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get the account. $account = $objectStoreService->getAccount(); diff --git a/samples/ObjectStore/get-account-container-count.php b/samples/ObjectStore/get-account-container-count.php index 3f43adf1c..140c5cf40 100644 --- a/samples/ObjectStore/get-account-container-count.php +++ b/samples/ObjectStore/get-account-container-count.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get the account. $account = $objectStoreService->getAccount(); diff --git a/samples/ObjectStore/get-account-object-count.php b/samples/ObjectStore/get-account-object-count.php index a8529c5bd..3d1392c40 100644 --- a/samples/ObjectStore/get-account-object-count.php +++ b/samples/ObjectStore/get-account-object-count.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get the account. $account = $objectStoreService->getAccount(); diff --git a/samples/ObjectStore/get-account-temp-url-secret.php b/samples/ObjectStore/get-account-temp-url-secret.php index 027222c45..101ed6675 100644 --- a/samples/ObjectStore/get-account-temp-url-secret.php +++ b/samples/ObjectStore/get-account-temp-url-secret.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get the account. $account = $objectStoreService->getAccount(); diff --git a/samples/ObjectStore/get-cdn-object-http-url.php b/samples/ObjectStore/get-cdn-object-http-url.php index 3947df022..c5e6fe42c 100644 --- a/samples/ObjectStore/get-cdn-object-http-url.php +++ b/samples/ObjectStore/get-cdn-object-http-url.php @@ -15,38 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container is CDN-enabled. Run enable-container-cdn.php if -// you need to CDN-enable the container first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible HTTP URL. $httpUrl = $object->getPublicUrl(); diff --git a/samples/ObjectStore/get-cdn-object-https-url.php b/samples/ObjectStore/get-cdn-object-https-url.php index 9b8ffda2a..954598255 100644 --- a/samples/ObjectStore/get-cdn-object-https-url.php +++ b/samples/ObjectStore/get-cdn-object-https-url.php @@ -15,39 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container is CDN-enabled. Run enable-container-cdn.php if -// you need to CDN-enable the container first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Constants\UrlType; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible HTTPS URL. $httpsUrl = $object->getPublicUrl(UrlType::SSL); diff --git a/samples/ObjectStore/get-cdn-object-ios-streaming-url.php b/samples/ObjectStore/get-cdn-object-ios-streaming-url.php index 51a94f781..e38b8db11 100644 --- a/samples/ObjectStore/get-cdn-object-ios-streaming-url.php +++ b/samples/ObjectStore/get-cdn-object-ios-streaming-url.php @@ -15,39 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container is CDN-enabled. Run enable-container-cdn.php if -// you need to CDN-enable the container first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Constants\UrlType; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible iOS streaming URL. $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); diff --git a/samples/ObjectStore/get-cdn-object-streaming-url.php b/samples/ObjectStore/get-cdn-object-streaming-url.php index 70ef9f5b6..ca8a6233e 100644 --- a/samples/ObjectStore/get-cdn-object-streaming-url.php +++ b/samples/ObjectStore/get-cdn-object-streaming-url.php @@ -15,39 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container is CDN-enabled. Run enable-container-cdn.php if -// you need to CDN-enable the container first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Constants\UrlType; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible streaming URL. $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); diff --git a/samples/ObjectStore/get-container-bytes-quota.php b/samples/ObjectStore/get-container-bytes-quota.php index dd9cc1c45..ff6216839 100644 --- a/samples/ObjectStore/get-container-bytes-quota.php +++ b/samples/ObjectStore/get-container-bytes-quota.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get the quota for total size of objects in container. $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); diff --git a/samples/ObjectStore/get-container-bytes-used.php b/samples/ObjectStore/get-container-bytes-used.php index c82b92dbd..875068440 100644 --- a/samples/ObjectStore/get-container-bytes-used.php +++ b/samples/ObjectStore/get-container-bytes-used.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get the space (in bytes) used by this container. $containerSizeInBytes = $container->getBytesUsed(); diff --git a/samples/ObjectStore/get-container-count-quota.php b/samples/ObjectStore/get-container-count-quota.php index 8485f0639..76e5015d3 100644 --- a/samples/ObjectStore/get-container-count-quota.php +++ b/samples/ObjectStore/get-container-count-quota.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get the quota for number of objects in container. $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); diff --git a/samples/ObjectStore/get-container-metadata.php b/samples/ObjectStore/get-container-metadata.php index b6a74c90d..48a7ea759 100644 --- a/samples/ObjectStore/get-container-metadata.php +++ b/samples/ObjectStore/get-container-metadata.php @@ -15,32 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container has a metadata filed named 'author' set on it. Run -// set-container-metadata.php if you need to set this field first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Set container metadata. $containerMetadata = $container->getMetadata(); diff --git a/samples/ObjectStore/get-container-object-count.php b/samples/ObjectStore/get-container-object-count.php index 46d797c7c..34fdbe8d2 100644 --- a/samples/ObjectStore/get-container-object-count.php +++ b/samples/ObjectStore/get-container-object-count.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get the number of objects in this container. $containerObjectCount = $container->getObjectCount(); diff --git a/samples/ObjectStore/get-container.php b/samples/ObjectStore/get-container.php index d5cdb92e6..40cdc73d0 100644 --- a/samples/ObjectStore/get-container.php +++ b/samples/ObjectStore/get-container.php @@ -15,29 +15,21 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); /** @var $container OpenCloud\ObjectStore\Resource\Container **/ printf("Container name: %s\n", $container->getName()); diff --git a/samples/ObjectStore/get-object-metadata.php b/samples/ObjectStore/get-object-metadata.php index fc0ebdd0f..8f44a4190 100644 --- a/samples/ObjectStore/get-object-metadata.php +++ b/samples/ObjectStore/get-object-metadata.php @@ -15,36 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg' with metadata -// set on it. Run upload-object-with-metadata.php if you need to create it first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object metadata. -$objectName = 'php-elephant.jpg'; -$object = $container->getPartialObject($objectName); +$object = $container->getPartialObject('{objectName}'); $objectMetadata = $object->getMetadata(); /** @var $objectMetadata \OpenCloud\Common\Metadata **/ diff --git a/samples/ObjectStore/get-object-temporary-url.php b/samples/ObjectStore/get-object-temporary-url.php index 8a2b86fcc..5d438972c 100644 --- a/samples/ObjectStore/get-object-temporary-url.php +++ b/samples/ObjectStore/get-object-temporary-url.php @@ -15,42 +15,33 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// * The temporary URL secret has been set on the account. Run -// set-account-temp-url-secret.php if you need to set the secret first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object metadata. -$objectName = 'php-elephant.jpg'; -$object = $container->getPartialObject($objectName); +$object = $container->getPartialObject('{objectName}'); + +// 5. Set expiration (in seconds) +$expirationTime = 3600; // one hour from now + +// 6. Set the allowed HTTP method +$httpMethod = 'GET'; -// 5. Get object's temporary URL. -$expirationTimeInSeconds = 3600; // one hour from now -$httpMethodAllowed = 'GET'; -$tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); +// 7. Get temp URL +$tempUrl = $object->getTemporaryUrl($expirationTime, $httpMethod); printf("Object temporary URL: %s\n", $tempUrl); diff --git a/samples/ObjectStore/get-object.php b/samples/ObjectStore/get-object.php index 666f7ad16..89e231a83 100644 --- a/samples/ObjectStore/get-object.php +++ b/samples/ObjectStore/get-object.php @@ -15,43 +15,33 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ +$object = $container->getObject('{objectName}'); + printf("Object name: %s\n", $object->getName()); +/** @var $objectContent Guzzle\Http\EntityBody **/ $objectContent = $object->getContent(); -/** @var $objectContent Guzzle\Http\EntityBody **/ // 5. Write object content to file on local filesystem. $objectContent->rewind(); diff --git a/samples/ObjectStore/list-containers.php b/samples/ObjectStore/list-containers.php index 9c6f4af1e..71de3f1fe 100644 --- a/samples/ObjectStore/list-containers.php +++ b/samples/ObjectStore/list-containers.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container list. $containers = $objectStoreService->listContainers(); diff --git a/samples/ObjectStore/list-objects-with-limit-and-prefix.php b/samples/ObjectStore/list-objects-with-limit-and-prefix.php index d286843fe..e34548ca9 100644 --- a/samples/ObjectStore/list-objects-with-limit-and-prefix.php +++ b/samples/ObjectStore/list-objects-with-limit-and-prefix.php @@ -15,39 +15,29 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, -// * NUM_OBJECTS: Number of objects (aka files) to return, and -// * OBJECT_PREFIX: Initial part of object (aka file) name -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); + +// 4. Get list of objects, limited to a number and refined by a prefix +$objects = $container->objectList(array( + 'limit' => '{limit}', + 'prefix' => '{prefix}', +)); -// 4. Get list of objects whose names start with "php" in container. -$options = array( - 'limit' => getenv('NUM_OBJECTS'), - 'prefix' => getenv('OBJECT_PREFIX') -); -$objects = $container->objectList($options); foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ printf("Object name: %s\n", $object->getName()); diff --git a/samples/ObjectStore/list-objects-with-limit.php b/samples/ObjectStore/list-objects-with-limit.php index fa1d2dd2e..8bf6f4306 100644 --- a/samples/ObjectStore/list-objects-with-limit.php +++ b/samples/ObjectStore/list-objects-with-limit.php @@ -15,37 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * NUM_OBJECTS: Number of objects (aka files) to return -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); + +// 4. Get a limited list of objects +$objects = $container->objectList(array( + 'limit' => '{limit}' +)); -// 4. Get list of objects whose names start with "php" in container. -$options = array( - 'limit' => getenv('NUM_OBJECTS') -); -$objects = $container->objectList($options); foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ printf("Object name: %s\n", $object->getName()); diff --git a/samples/ObjectStore/list-objects-with-prefix.php b/samples/ObjectStore/list-objects-with-prefix.php index 6de68edc4..9e489e0d7 100644 --- a/samples/ObjectStore/list-objects-with-prefix.php +++ b/samples/ObjectStore/list-objects-with-prefix.php @@ -15,36 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); + +// 4. Get list of objects whose names start with a prefix +$objects = $container->objectList(array( + 'prefix' => '{prefix}' +)); -// 4. Get list of objects whose names start with "php" in container. -$options = array( - 'prefix' => 'php' -); -$objects = $container->objectList($options); foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ printf("Object name: %s\n", $object->getName()); diff --git a/samples/ObjectStore/list-objects.php b/samples/ObjectStore/list-objects.php index 8e53643ba..7f40f327b 100644 --- a/samples/ObjectStore/list-objects.php +++ b/samples/ObjectStore/list-objects.php @@ -15,33 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get list of objects in container. $objects = $container->objectList(); + foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ printf("Object name: %s\n", $object->getName()); diff --git a/samples/ObjectStore/pseudo-hierarchical-folders.php b/samples/ObjectStore/pseudo-hierarchical-folders.php index 9ecddcf67..f2cf68d37 100644 --- a/samples/ObjectStore/pseudo-hierarchical-folders.php +++ b/samples/ObjectStore/pseudo-hierarchical-folders.php @@ -15,37 +15,27 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Upload an object to the container. -$localFileName = __DIR__ . '/php-elephant.jpg'; -$remoteFileName = 'languages/php/elephant.jpg'; +// 4. Open a local file +$fileData = fopen('{localFilePath}', 'r'); -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); - -// Note that while we call fopen to open the file resource, we do not call fclose at the end. -// The file resource is automatically closed inside the uploadObject call. +// 5. Upload its contents to a remote location. Note that while we call fopen +// to open the file resource, we do not call fclose at the end. The file +// resource is automatically closed inside the uploadObject call. +$container->uploadObject('{remoteObjectName}', $fileData); diff --git a/samples/ObjectStore/quickstart.php b/samples/ObjectStore/quickstart.php index 0536e9c90..60c650ca7 100644 --- a/samples/ObjectStore/quickstart.php +++ b/samples/ObjectStore/quickstart.php @@ -15,32 +15,27 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Create a container for your objects (also referred to as files). -$container = $objectStoreService->createContainer('logos'); +$container = $objectStoreService->createContainer('{containerName}'); -// 4. Upload an object to the container. -$localFileName = __DIR__ . '/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; +// 4. Open a local file +$fileData = fopen('{localFilePath}', 'r'); -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); +// 5. Upload its contents to a remote location. Note that while we call fopen +// to open the file resource, we do not call fclose at the end. The file +// resource is automatically closed inside the uploadObject call. +$container->uploadObject('{remoteObjectName}', $fileData); diff --git a/samples/ObjectStore/set-account-temp-url-secret-specified.php b/samples/ObjectStore/set-account-temp-url-secret-specified.php index 2e197fc71..6ef4db8ff 100644 --- a/samples/ObjectStore/set-account-temp-url-secret-specified.php +++ b/samples/ObjectStore/set-account-temp-url-secret-specified.php @@ -15,28 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get the account. $account = $objectStoreService->getAccount(); -// 4. Set the temporary URL secret to "ssshhhh" -$account->setTempUrlSecret("ssshhhh"); +// 4. Set the temporary URL secret +$account->setTempUrlSecret('{secret}'); diff --git a/samples/ObjectStore/set-account-temp-url-secret.php b/samples/ObjectStore/set-account-temp-url-secret.php index 2a5f26e83..b69f81998 100644 --- a/samples/ObjectStore/set-account-temp-url-secret.php +++ b/samples/ObjectStore/set-account-temp-url-secret.php @@ -15,25 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get the account. $account = $objectStoreService->getAccount(); diff --git a/samples/ObjectStore/set-cdn-container-ttl.php b/samples/ObjectStore/set-cdn-container-ttl.php index 0ac60674d..1fa99b3e1 100644 --- a/samples/ObjectStore/set-cdn-container-ttl.php +++ b/samples/ObjectStore/set-cdn-container-ttl.php @@ -15,32 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container is CDN-enabled. Run enable-container-cdn.php if -// you need to CDN-enable the 'logos' container first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get CDN-enabled container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); $cdnContainer = $container->getCdn(); // 4. Set container metadata. diff --git a/samples/ObjectStore/set-container-bytes-quota.php b/samples/ObjectStore/set-container-bytes-quota.php index db241e60a..967989908 100644 --- a/samples/ObjectStore/set-container-bytes-quota.php +++ b/samples/ObjectStore/set-container-bytes-quota.php @@ -15,32 +15,23 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; use OpenCloud\Common\Constants\Size; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Set quota for total size of objects in container. -$maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; -$container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); +// 4. Set quota for total size of objects permitted in container. +$container->setBytesQuota(5 * Size::GB); // 5GB diff --git a/samples/ObjectStore/set-container-count-quota.php b/samples/ObjectStore/set-container-count-quota.php index 1867eb7d2..eed0e9055 100644 --- a/samples/ObjectStore/set-container-count-quota.php +++ b/samples/ObjectStore/set-container-count-quota.php @@ -15,31 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Set quota for number of objects in container. -$maximumNumberOfObjectsAllowedInContainer = 25; -$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); +// 4. Set quota for number of objects allowed in container. +$container->setCountQuota(25); diff --git a/samples/ObjectStore/set-container-metadata.php b/samples/ObjectStore/set-container-metadata.php index ac6e6669e..ee7525fc6 100644 --- a/samples/ObjectStore/set-container-metadata.php +++ b/samples/ObjectStore/set-container-metadata.php @@ -15,32 +15,24 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Set container metadata. $container->saveMetadata(array( - 'author' => 'John Doe' + '{key}' => '{value}' )); diff --git a/samples/ObjectStore/update-object-content-type.php b/samples/ObjectStore/update-object-content-type.php index cd6613172..7ce760f8e 100644 --- a/samples/ObjectStore/update-object-content-type.php +++ b/samples/ObjectStore/update-object-content-type.php @@ -15,36 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getPartialObject($objectName); +$object = $container->getPartialObject('{objectName}'); // 5. Update object content type. -$object->saveMetadata(array('Content-Type' => 'image/jpeg'), false); +$object->saveMetadata(array('Content-Type' => '{contentType}'), false); diff --git a/samples/ObjectStore/update-object-metadata.php b/samples/ObjectStore/update-object-metadata.php index 404b3725a..18df42dea 100644 --- a/samples/ObjectStore/update-object-metadata.php +++ b/samples/ObjectStore/update-object-metadata.php @@ -15,38 +15,27 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// * The 'logos' container contains an object named 'php-elephant.jpg'. Run -// upload-object.php if you need to create it first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Get object. -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); +$object = $container->getObject('{objectName}'); // 5. Update object metadata. $object->saveMetadata(array( - 'author' => 'John Doe' + '{key}' => '{value}' )); diff --git a/samples/ObjectStore/upload-large-object.php b/samples/ObjectStore/upload-large-object.php index 69e24d66c..c67ca4e1f 100644 --- a/samples/ObjectStore/upload-large-object.php +++ b/samples/ObjectStore/upload-large-object.php @@ -15,36 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key, and -// * LARGE_FILE_PATH: Path to large file (5GB+) on your local filesystem -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Upload large object to the container. -$options = array( - 'name' => 'large_object', - 'path' => getenv('LARGE_FILE_PATH') -); -$objectTransfer = $container->setupObjectTransfer($options); +// 4. Configure +$objectTransfer = $container->setupObjectTransfer(array( + 'name' => '{remoteObjectName}', + 'path' => '{localFilePath}', +)); + +// 5. Initiate transfer $objectTransfer->upload(); diff --git a/samples/ObjectStore/upload-multiple-objects-with-metadata.php b/samples/ObjectStore/upload-multiple-objects-with-metadata.php index f30ef3a49..4d67d9d11 100644 --- a/samples/ObjectStore/upload-multiple-objects-with-metadata.php +++ b/samples/ObjectStore/upload-multiple-objects-with-metadata.php @@ -15,48 +15,42 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Resource\DataObject; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Upload multiple object to the container. +// 4. Specify the locations of the files you want to upload $objects = array( array( - 'name' => 'php-elephant.jpg', - 'path' => __DIR__ . '/php-elephant.jpg' + 'name' => '{objectName1}', + 'path' => '{localFilePath1}' ), array( - 'name' => 'python-snakes.jpg', - 'path' => __DIR__ . '/python-snakes.jpg' + 'name' => '{objectName2}', + 'path' => '{localFilePath2}' ) ); -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); +// 5. Specify any metadata you want your objects to have +$metadata = array('{key}' => '{value}'); $metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; +// 6. Merge the metadata with any additional HTTP headers you want to set +$allHttpHeaders = array('Content-Type' => '{contentType}') + $metadataHeaders; + +// 7. Upload them $container->uploadObjects($objects, $allHeaders); diff --git a/samples/ObjectStore/upload-multiple-objects.php b/samples/ObjectStore/upload-multiple-objects.php index 08787bc16..835cd5e4c 100644 --- a/samples/ObjectStore/upload-multiple-objects.php +++ b/samples/ObjectStore/upload-multiple-objects.php @@ -15,40 +15,32 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Upload multiple object to the container. $objects = array( array( - 'name' => 'php-elephant.jpg', - 'path' => __DIR__ . '/php-elephant.jpg' + 'name' => '{objectName1}', + 'path' => '{localFilePath1}' ), array( - 'name' => 'python-snakes.jpg', - 'path' => __DIR__ . '/python-snakes.jpg' + 'name' => '{objectName2}', + 'path' => '{localFilePath2}' ) ); diff --git a/samples/ObjectStore/upload-object-with-content-type.php b/samples/ObjectStore/upload-object-with-content-type.php index 528b67bd3..ad0a21850 100644 --- a/samples/ObjectStore/upload-object-with-content-type.php +++ b/samples/ObjectStore/upload-object-with-content-type.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); // 4. Setup headers with custom content type. // NOTE: Normally you do NOT need to specify the content type. The @@ -46,16 +38,12 @@ // are using an obscure type of content or want to be explicit about // the content type rather than letting the service guess at it, // you may specify it as shown below. -$customHeaders = array( - 'Content-Type' => 'image/gif' -); - -// 5. Upload an object to the container. -$localFileName = __DIR__ . '/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; +$customHeaders = array('Content-Type' => '{contentType}'); -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData, $customHeaders); +// 5. Open local file +$fileData = fopen('{localFileName}', 'r'); -// Note that while we call fopen to open the file resource, we do not call fclose at the end. -// The file resource is automatically closed inside the uploadObject call. +// 6. Upload to API. Note that while we call fopen to open the file resource, +// we do not call fclose at the end. The file resource is automatically closed +// inside the uploadObject call. +$container->uploadObject('{remoteObjectName}', $fileData, $customHeaders); diff --git a/samples/ObjectStore/upload-object-with-metadata.php b/samples/ObjectStore/upload-object-with-metadata.php index 8325c8156..954b1ef38 100644 --- a/samples/ObjectStore/upload-object-with-metadata.php +++ b/samples/ObjectStore/upload-object-with-metadata.php @@ -15,43 +15,35 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\Rackspace; use OpenCloud\ObjectStore\Resource\DataObject; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Upload an object to the container. -$localFileName = __DIR__ . '/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; -$metadata = array('author' => 'Jane Doe'); +// 4. Open local file +$fileData = fopen('{localFilePath}', 'r'); -$customHeaders = array(); +// 5. Specify any metadata you want your objects to have +$metadata = array('{key}' => '{value}'); $metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData, $allHeaders); +// 6. Merge the metadata with any additional HTTP headers you want to set +$allHttpHeaders = array('Content-Type' => '{contentType}') + $metadataHeaders; -// Note that while we call fopen to open the file resource, we do not call fclose at the end. -// The file resource is automatically closed inside the uploadObject call. +// 7. Upload it! Note that while we call fopen to open the file resource, we do +// not call fclose at the end. The file resource is automatically closed inside +// the uploadObject call. +$container->uploadObject('{remoteObjectName}', $fileData, $allHttpHeaders); diff --git a/samples/ObjectStore/upload-object.php b/samples/ObjectStore/upload-object.php index e47648ead..42c50cd55 100644 --- a/samples/ObjectStore/upload-object.php +++ b/samples/ObjectStore/upload-object.php @@ -15,37 +15,27 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * RAX_USERNAME: Your Rackspace Cloud Account Username, and -// * RAX_API_KEY: Your Rackspace Cloud Account API Key -// * There exists a container named 'logos' in your Object Store. Run -// create-container.php if you need to create one first. -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('RAX_USERNAME'), - 'apiKey' => getenv('RAX_API_KEY') +// 1. Instantiate a Rackspace client. You can replace {authUrl} with +// Rackspace::US_IDENTITY_ENDPOINT or similar +$client = new Rackspace('{authUrl}', array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', )); // 2. Obtain an Object Store service object from the client. -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); +$objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('logos'); +$container = $objectStoreService->getContainer('{containerName}'); -// 4. Upload an object to the container. -$localFileName = __DIR__ . '/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; +// 4. Open local file +$fileData = fopen('{localFilePath}', 'r'); -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); - -// Note that while we call fopen to open the file resource, we do not call fclose at the end. -// The file resource is automatically closed inside the uploadObject call. +// 5. Upload it! Note that while we call fopen to open the file resource, we do +// not call fclose at the end. The file resource is automatically closed inside +// the uploadObject call. +$container->uploadObject('{remoteObjectName}', $fileData); From 8c23e703fe610401ea05ff012bd958654da92f7c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 14:19:42 +0100 Subject: [PATCH 228/835] Removing example assets since we use placeholders now --- samples/ObjectStore/logos.tar.gz | Bin 1545627 -> 0 bytes samples/ObjectStore/php-elephant.jpg | Bin 308254 -> 0 bytes samples/ObjectStore/python-snakes.jpg | Bin 1245980 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 samples/ObjectStore/logos.tar.gz delete mode 100644 samples/ObjectStore/php-elephant.jpg delete mode 100644 samples/ObjectStore/python-snakes.jpg diff --git a/samples/ObjectStore/logos.tar.gz b/samples/ObjectStore/logos.tar.gz deleted file mode 100644 index c757ac0b56021b349e7c873822363cd2609e5a01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1545627 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¥P) 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
bqyd5OGh|fb0j5z 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
UBjyw#K@GW(y?8D(Due0RoA!TabrQ905wB5IAF zf{4cenzoCLE#7K6IjYSaU^|P)oWj@{_nzZ}+qdhj%%7z?S5j-C&(uNN%IeszF0-<` zytTGkBMp5W8zho6f@RzbI3R8)Mgac+T<2axqUU{^FyO}EAcU3x>eWMGxjUb9Lm?fO zaW;R%TxEKSnuw4i}Z#@221 zQ`AE=tGJ@NW$9awZr|zM-*;k18bQ5Rr^}2aiLSCw=(|nHsBoK|A=gP&1X5Sl+v(}x zlWTfSmXzS`I6qB~X$IN|vvU zl%j}%%!Ceek%a*F@00!X+*ooyGwpkGb-F1v86DDPqm~)&LP=zgd6HKsa;^_6gT~%F zA039g+j>;(bKO4nN!s{AZBVs+W$BAwQi>9?}R$mI#o8%uZ|+z&YYb*1$qW5_LvOD&Gbt&iq| zNyAUky)$c$I@Ez`s^f5Bk+MBB+t_5B^W1A=r*w>1plp!3i5xlG{1NJk2yW$8UV4I- zdX-w5cvd7wP}%n<1oPXR;Pak!Qz2m{#i~0Yk9Dvg#bV#6H|k3JtgzE5nzAHfp`DeY zC$Jy@F^#`C2gY@)AJtI1Tud5Y>OceIdc2U^mo(S!ZbVmRWn}38SFazaDxh|shUknvB)brC+ z$r_-B0*b2KE-~aBXP$Iu;cJQBhWx8F*0-NV+tSem1kf@^RIzMk8D-tH0?G*UkJ~3$ zjfYhilV{ypyC{EEFZC5Rd6J%=f22esbc7dRnK)70cggdO4=)Z$L62%4>eM?dz}G42 zC8dkiFdYu6#BLnGl0Y`oTMhYFjoZ?ozg{Y-Xa4|+sg8=8 zHV^HGAxJsehmxRYAAJ)m1}K9@pjkjrHtY5HR3my^I(p?i*5{h4r}0uo1SGjW+_5L0 zoG|UE41)#waoX)pG;eUYqfhrz`B@}z(iDcBn+$m?5g#YMP6-+9-27?gNO&9}>H#xP zP2TL*+E8R$tjIx|(PF;V$uNm{I8l2Vx)v79J840+Oxh%zCJ!TYa&y}vuF zG}2CW%Edzs&PhKA9utVBDTJIK>R<-|^Uv7o*f^|-&yRfvVch;IEU(V)w_6Q$)TLyv zRMCD!jREAIaC6AP2c2m*3l!||LHS(ce4*;DoVUw02`j!6AP`Nzi1K(O4*ZYPu+tNq zcrnY^3ETw$R~w<}{-0c3plf0zUb66>`@iQ z*>$$rE%CuT2B{ZmWneuO?mU|HQ&p#BLf)E!}VYD(&9E|n3zDPa_|`xhW* zcNpa4W9LI_Z0=M&pm2yjkM+k;*3sUU;XPDR7}wHPA%K*QRH@*M_B{6I8o>I=`M9{Z zg!VP2kDGILgCOp^^(5U$@JmB_tfHe?P}8)rONUY%5O@Vfaz`9vw!N25==u2FJ+tIX z6SWfBY2Vvrm6be2)xD#irf)}Tsk_^2qpYT)2!<&tBVqvH!~pb$Lk2ECCtP<7?HlI6DJJW@v+D!Z7m=|+F4$j{j4;DfKG^voBNqz#8Z zJoA<(us@;iuc7qLn-oAfqs{s+ zpY?C7-1v-;4ZR58`K86rs0G>?>g4q4;CR*d1S(waBlREw+Z$;Zs+qSI?td?|FyI5u7wx75Cam{ac~b!~V+P);g40zEB3vFMlAcLN zrLmisyB~4xc^JUs9BRfVNCuwXs<`hlwZMN8yca2Aik2O&vF&LDn7~#z_G7>~Bpm0D zzKUDwhD{MmoYf}r1xyqU)1^MsG|a><)yX&=uyKLloi!U;2I+Z;;Z=0KPw`i;R{TVD z5HlH3pbAufLgb7NK0iU?RE!`es( zvF+oX21v(b44DCK_U@?du}M&}$yQ=kZ+EKPDlR!BoMfngENvucvIS~_Z|rji;{Jd9Axr7+FkB1`@uN; zw>rTTPbyM1JW2Rkc_DcW-kX#p9Fj+K!k$3D8NkpkwnBSB*ned@uFopS(_)q)phQtl z<0&U_WnfM*z#xqI&~VsW84mR;OyrUP+wxwjDl4tj>ZKAVnfD#h{&2j2NWmZ9JnL2m zSlA?{ZSH}qt+7@`Ea^2`uvTeQ$?bPNv48;_@$=3&)|^rXi+atLB3eLRY%diZb-84A zxW*%SWCZ(nhVrMj+;{Vzwuj(eWj(r#%T)Jn&TCaTwdouiVzNF#+Aq=-7dw+LL|fsA*-I0v?tb4~7591Beq zb9sV>MS_aq40SbO))DDQ1c(Zp;jw|=KO_LvKq|lJdX?omz}3BQqCwItXsoM}3b^VT zV}w@zSZ2osm~*>=H)kIsQa0T1qn=fq_WH?Psp=@={5I2t1hCo8S7tcFXC8UZnJlA7 zjCpS9cYEpBpo%w0DpS?m7u)TXh60A}at23^+MxiYWOH~O(DV%rB}F_Jbf%f|e+;oJ z6093J$>Sgv?s70R2j#~b#BOOiwz}C~Ho2k4(N31-7;1Rc=%V!_W|y70lacn^N`ara z<3*A&XGu{MZ9acgX+_bN>fgiFD2hm=*&&go*it6=2RH+eK?jqe{HKF}J1$Z5-3?7t z)atQW;9SKdk&`mCVS!v@l0f4l1ZN{xiHFr$v)^UvBKFx$0G2AsSiQ_u&e#q&WGVdJ zj|<+pQK*#B-7K)_AY-i%ED`{xeO-qVmRc5qo_Re* zY}3e!5W=a9;mI71$38Tx$+X1Z#;XOcFxoIFxtVL5W z-;vH6{3ji>8FBKQFD1<}9so4$xc>l@cGq>vi|PYUO!kV2lCqWg^;xp4NdZ*katFqc zl1P{|kV`$ewZ^+H6jXNmB#k(Q;Q${~+sklAZzpLyYC1@cStJaxqj7%fC{h{fzOuC1 zEwh;I;z*+#fNX*4U}uk#qX3S?ooZ%u`HnN+6WR!_epEDG;3-`^f2VO{iW1=K+}#n)c2XQTOMmxgrmTHtdXA-iDjnpiygB5I6;%TcIS0N+ z>#vn{Yzfa$610szUt_V|4bMCTQCamXrffB_R1}IDbyCCCP-Bg^U@^}dx#W!FUq;I6 z@iHeqFE)@))LcG)qN{~)g?gLnE#mDxOHjsQlkm)ul2`eJu_T2D0|2i*{Oia+JF3XX z%Ga|*lv}podOO`-_DoKU>KSU$rf%CP~8vZfnzZGQ+L4-8A}U#rzuG0rf>}s6^4! ztAMgAatQ=>1+m|rPQ2et>G*JCx;WZH9O8N2-Ou2(pC-3zM;1ERt`giWY$NSAB zWTtWWI+BQ36(MAB+^NQJGxy}^vP}1l8zL82q&aJnj@&1dv=s4_nnzaw zixeC`9((rh#&m?^nCoduKxUX!E z-aG3ievy!JU6ZU)+rRQq=7X|@H&0B79CY!AkwFUsw$kVZ7z5ZIa6ev88U*-lEOX83 z18O@fu+pDv=sE5b)m3r|X)R_bon6USnHMB(jYb0uj9~4+=baA?&I}{Jxc2o@(cK+I z)6Ydp1-TIr0KzfO{1834@5mkXqmws_7lcT=2XAFUEZ9)%I#%e4wR!$vZPE~hiXPjQ zN%BJh@!!U|qf{ayceQXUlq*-!ZB(oHdSs-j15gxE3(!*j+vkJG-D!+WEZkuV(%+t&X86(xtdYTF`& z;pL;Hcmfc3_h+6*$T$EGCtGEWjp9ci$w6a|%~qXtYZ6&n*(3^-SP3_@DzY&7@J~PA z@1(lK$0iuVMQL^Iw;#{pRy|B4A4H9y>B~L7;UrX&7?Cy^5I%BtouyZ>EN

{OOlbO}{Rma0~koXbx!lW(kt4p?VsZ-L*BmeA+Q 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 -^sYFfoudHUX7; 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

    6cgyoF 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

    7P4p6isG`9TvojnF$P7M{BHW4lyD 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≤(vh9L3Z8S*pn ztX`!vATY+`PLS0eR}N5xeQ3GSTez$Lhw4im2Ox0T&z`5z3yx>tn&fZ{K(?^s#(;AxifTx)41zEEJWs zQY9rlB)d~{U@q{P<0lxxkWP3w8XhTYa!HZaUPi-_@~M<)4JaDp)R!xVQwu!^rZGnw zKdC7@J#bG4oO}V#X6?qYezun_y+aY0PSVmHsMUabU!|x_zo-3S4F~a4bhJgXnbw`2 zM;Kyz?^W=m51x4*b?LfHE}9c7opQ9*_T!JmNCfbUo;wwP;cpc1S}hk!*_gNC>H!W^ z;1F}zFC_3g_}7Y`s}C8XXM-UiRqsUYxAjuyIC2!pY?RSRm6SDf^m5=dfG`ZWY=8*l z0th(x#&z!Vq7k^$e7(Tvm8DWg*9p}GbT0^|TA2U}rDJr#z+Twz z<3D{~SUZ#gZE~xX7LI2@3RFuUxEzmBh~wnvfuQAEv78(CtS)nQT&lWq(Ri{MCR!$^ zFSbZ#?#CpN?Cc3S`Th0M$6}O?(0S-P0#r#-wt7OT5eQl`3Jm0^!1>6|GDmT%VMpe3 zn%n;XIabhJu_#BVEfzXzTC0^G;})pxLcxnO4u>#a3)GS5W=2aVDphf|%bqrn3JsqK%xj?*Ljm&XIpB3|2~OSezQJV(&=X{uLm z^R|;Bt}s{=&z{3RMmuWJLLGIwUCm)CMW3g7fo)UM+OL&H8c?jUOCe=Ya!xaX55O22 zh$bvxYR&!1L$ML&g4 zQ$s~=s4>j4Nlm+K3U>qVAJu?IAYkW652xJrl4ocKdhVZN!5gp7&ak`OZu|k zMQjIB+m1NK?ignSf!Jflu|!jNh6cE8-jn#w0lOi7pSw>>bAlR!BKmL%;#pKOfw+%x zBc9kF9ku5=`SBooUAd#|wy|D9{{T@R{tu6-w0&38c9<5Ck)x&b5*a4+)<#qX%43oP zax?b>w!fDj^*htF<;N>V)bCxq;cw#0y8UIdRW#v)daIB)4UB{H_TctDHT>u5c<*6! zgLdCNzAx2!7+i>Al~sUMQp?FCbN#+`=UpcDv|4+qFiA4Vg#brYTsCva=RdFcX=x3k zYqPSvr;vTWH=$A-)Jajb1iehhV9yiisLcOzfgPk^)13K_KVk9)Dii zyvz+Xb{8~RR**;RU=>8C8RvolC&zD$4G2q1ogoTDzMM)7FoXkwq4#b%`8uOq`&=Vc zUyE6C!vq0=&nxms{kv$6X}#5erQ)KYouhKa#???y94Np8_8s-k-*sZ`sdc7wd^p0h zeq4;_z~f8ENK|lEl=Uh&EDDb2{{VdGMa59Hc%DRSU-21(8`M}OZ!5&EJK zh8}l@RWdATA;`dQ{qNgMi0lv-DdM7bC1VnKECKKOjQHn5Y-743DSa}ujH=)PfD1Nw z{@yh$XbN=&0-_>D#)||oC4uJ$!5``UylCxrbypP*Ib~59k=GgFzfT>ickzM#nqR~8K_yM6r$QZ wB8z~K1f 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¥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 z zg1V)DA4fq`aU~-$w0SNI1ueaY2S3TOGyrwIq_L{;2&@lcGY_ zI#op8n25y_4_XvFHZnO34`J93+`BZINFk=j;uYBykQ`_w6#oEUEO$EUDjK_OO*Kqw z>Uwc9lN8DnZGQyei5M8~fOV$_SeeDo$6;w6t>@y`NWjEOB0 z)>L33mV)patS_neYNJght@qu6pV~U7PX#Jsh4E;_@rM7F}rzMB8#H>qK1m5 zqV#Ro3K9o0pk`uolHeW~0;Aje=U-NKSm|yVQ*PG)+UIk>e&5MR4lB)Vrt4)eit9Ai z(z~Xoo<7W@+wLpMk(`j-!5L9@<7vkokIt3puQ4<`heyTxs>M z%D09t)`950qPIgO(u%wkFd_t;AuTY+3%DN{Iwl3-!}d&i@Yo3e?swmnYPC(*-BWn{ zLcHK;eGb!5XuA+}MNdG5yiIw!8GDR>Th|77wbGbt2N)1Xug2s_z;#(R5wWS<&yr+q`5>4dYnp^n^8qes`>3qy5KNZzikyuH4bw$oiS z^deI#M++)4NTly%d}JpFpZx1H>BnDCbi5rTe!SA@$b135g)F*9>hDfnuTXTg`=@Sn zNCdO~B7Ks_Sj6zCKm~h@gU7dxdx`#{`wIrP*m28~~0I)z$ zzsVduM7K)1q%xX7%!V8WKHTwlNf+xvTuKlyDPEPZ5!Gxrt6C} z>WVwM)T+Zy=;SjJ%G(JdMI5U3`^wjMTt!7vDQh%W>yFOSR@Unk`Fz{-$->#nc>Eb{JriiJ9hXyDllkqdEl)O_A3`g z^!(HvS#Q4EuWLJ$skT(9Nf0hj2>TK?^T&=gTyC5jPpWwPw{#D>8G6e909ShUo*z=} zWVgL#sgOlj(n1u)IRQeIOmI|#li$uX4y&Ie(znZHf{w>>ceam2JKok_lIK}*uB4sp zP*8y=j~HfBKdCTA8$JI3@2zg78ywJVcGy+$hy&bwr)9hTh1JsZw@(xwHoIl_kj0N>?N=hY{vEFC#pVUEpatD=GkqiJRes$(qK z@sLO#9l*y2Q`Pe`vT~rtgtSE9b%HFK`?N<1SCBbbhHH&Inz~bQ>S}ePMo3ysgvQQ< zoGSioF+KP`-@c)rO5kTp;B{6w!gtt~?l|Al)c_}jdDY)j`Y)^uG{%mZE@eGhcBHqL z4VJ*&+>8|iBf;0E&2~pml;c7F0Bq6Vo_)||h&)rcS+lEtqIG9T^%nHDo9(tG6%N$& z;3Q4~Kg?W$cmNZg1~pmmW^cjOXs4bOoQ_E#}!!=DY zRskg2&Q*uCfEiyHBR%xKA$QtX`6wIQ@IO>cX$#ou-k0cQmXZs9rwU%%(K};fJe!)N za7*#z;De3^nlcS${n3a8yDDy{xXhQi#RT)!iQ`Er;)puNc7;;Bdxi#BACu0j)H)lo zdNZiNru*N%c$8Bi!j+)%8DRH55L!YoD z?d!kftN~QD6?YhBf*L!`*55r#Bn(vG1t;3u&*l8RkAD1X!~v5Y3%WjYn)bYcG=Jm9 z<9oEr8A8+IjcV=Iv23l)Wn`!s>SH^KhTsBExrqUWNXAIkuh?0N(G&*b*z>60aciOh z%(7Hyy0+N$i)^QctX8umN=OY73?48$W4~-1{A(@UesMq9@quf9h`G0IJkdncZ6qTq zM)Wotsh*I$iM*j8M$B;DnHazT4{+n1GY?3U?QNDfGyr>^Y^AZ;DaKYckqGlAdy_*K=Ac9eoWkcF*jZvwKK7DTCz z7}zXwdVsGv3I+fik9Ur7ondwEiq4@~4|r=^TN9+%@A3T7(SYaaXhC04{{RC^Pe)B1 zBw~1>*&1xzJ#F3BjPde${`%PJoqHxYykG{1;b zNa4L+4)#z-1ntQO9k4!imyMk?+MgqA?Z8B9Ji8$aeI3fSScPrwq7Wrru(K8}2*_3= z^~W56p89#w$0JK*1l??`*3x0x{+rovbH^<$b?(tQAUm^qkQKoI1UTdXKmd$!pN(rf zqhNjLfx7iX1dbKoQT+^>&jeDXbD+&}GcGm%ZE3Iu>o!$r*|p|`g+=yyKS=7QUm(1(=q&tNOouB9i;cp zdF`y9sWICuEbz@8L?23`gBxBMM1p;jPNt&4H8ob&mZG64{{Wj%BHQ?N#t6@B;E;g# z0R8oqjniDsF-B|&3C|p78SZ;=r?DZ( zn;F?`m7->yJD%P4Rfags+9XTr>%GE?qwv#y5rYiGB5!OCA1qFM5y)aV7+;NUWMO2s zUk)p;THD=O!uBfK+k1+pSGdBasx^du9ayTF90Cr|LH6KdApZbSJ;t?qKS`DuJV(v} zZ5G)6O1>B%fP$4ImasKCQ&Ch^E6!Kc6D>r8+)y#yj^&$-k_I)Ok%tyXQfw}Wzr-h7 z`vNxv`dKddrVPQq>)TDJm+XRgzXy{{S&i92L$xbB_nG zPpNgB>~hyR+c<&19IL|Nz4Dp;FZ6!TQ){7?*w#x;N3KNTa>$^PNieQSU<&|O+73o^ z-$%}#F04t*5wXR-Q$)M}01@t0uS`rKUC?Z-AR)){_M1pdtdykeWJw_zC9~Taf)as^&x-xNO!*WI0tBP$rx;7Aa*A@ z-RY7^9B#)Dxy=@J_V`1L650lnTj`%kB^@mnPtx3Ks=Y}4)67|9b!IG}Z$zP=#dNrHRBSdGK$`Tmc$$9m zg_O6iy;`NESnoD_pbhE*W0khfP6=Fv!jpl9Bq-!})o@|Y>hZa=#^D$=K^*r6#Ue6^ zvaW0vNSY^(hJq+5q(dW(q$&_cv!3TTJG;8!ZB?O~VNTLT z3b|s_z(cuF$Zf-L&$of_b)|)l+2xWa-IpdAkP|BwtO&@DRz2@qh}ibCb@tCt${AtMJ@S z5`7gU$F1!~ZkwiOr=^Pj08du$y`$XNCxBmR=PSo^$rwHKPqrLZMqqc09BP~VHQfBA z+{#M|zKnIcwZU%}0bI(f6gVD=BA;={IX==mk@L=RsnYC;OBmA`e{#vwbF$BW zxX{5ZEiDDKRK~1aGl_?C4o@gP=YkHB>OV!ro@+CZU+4B3MiBw$?0JFZPPaz z6){soMk8q<5rr)qL_x^f4hBhMpS}h$rXa$L5x-={ZATu>7ubC7`lGd=^1IOeHMP=I z*3m;JwI*doR?A?F?Z@RL4hAw08gCWNomwgejAS#M z^2ct{J9oys>kd}Mu(`k1QR%xOGLQ(Ogw4Uhhu~VSrZwOOQdxINad>07~R` z?s(Fy&SqCtml_DmTENmbzEDjQO48T$4lAru#sOM?tbQf*B*WP9~3bv(? zgu`VY)Z7WmfOy7EJN@;8^p`^FQOxObK>Vi+FCxMx5+T2aL)^Na!D9Jmv%A1 z=N+-Ej3|E7ml=rYXaqaGn&k=B4b`F!n3loSb=39J)l*a?YnMqFBJkMl!93^lA3Ab= zh*e*P5%?sZg!Y8nO)46r_ z`5gWzQZ$ich246(lA@kTD5o+B04hvu9{~RVhk{Soo_Ou6U}d^S0g~_>(N<84Z7iC`;G={qQzm*SgFWpjJzuD)iX{~kX{8-$r zf)y;80(=fh`v7n|UEQ<$U6B7^S@B z_TmF6d$)CSzhtgZu|b933qkZBP}=SgtraM!r;t9@X-_4B;c&SeU}Gciqxb>jHq{vV z6{eFps-mijeUA!MEQ*KRLj?=T_!<8IE_Jif;B!fhhwoB{NJ`1yRj#wvRL@Hcl(Aam z+aX93{#L_`Io2;y>miOYX)kd85y{`5g>5c8JRs_C;MI1SDQc!QGeS5j zxyrV4xw~WCoN{yX<9cZ#$8UmRApEXuZw869vg#@dR;%>n{6!dsWNL-=Hy~E->-IRq z6OeLojb1!s z+W!F3lzAF`7Le#}lZqp0wpEJhEo}zJsR~Q72hL+0gPfe_u+~)heMF0bH#wezfAeHF zh|c`2<f4ALtuKvw-SG26bi;BF9qjTjA*tD9A62k(nl7x-q9 z+f8YS&~6DTO6*wSLH=G&FxdRP`<+rr^11PRqm8*%dssJa$vtnOt`~zO+uDJj)JKA( z4^STJ5O%@Boa2G_(ZOWlzAJ@}$ZjO^op#=zrG==(yV1miv~tMKj0yasJYaV??Z&*P zQTku$*)!QQ-oY>inn~1sYkyu<38fFKdZxu>l8W0eQO#_5zdxlL721v5zvW*5Wb?Zi z*U|dyabrr@SU-dnFuPlIrT+j@ihJ!5hKj9fS)bH`SI7!8?sXW>KU|FXBUl|@4YS7| zZ7#9$sQCwp;rb4YW`@n5o)DJLQy&lB0q_8S;1^eR>X+lOz$n&9P?q_FR!&LvFNQ zDrnlPt1VPls47idFC9ZE^%Z@ag7fI)gOh-8r#_LIl68YwIA`pV${hBc+%~Xx8!m2i2bl$+TGwbU z;i0RJYK0=B(~?c$6r2y=aU_o8&a?iijAb${yu$bVk+n6#!XCZU^rbZSDb}^#)S^0Eyj@0dMe= z`W$`q?3s8N(G%h{NOl7Equz?4eVZ#lbb9p)SnYCGP|G{}9is#j5{wZWzE1wYo^#0O zjbU_OrlslS!SB>h{?uv-p-}Y|t1U67sm$tqHffico{r*)*StOIJuY}Z95$_>OHvzm0a6OieIHQ z)ek$|C!W56RSfMZk|Q^_8%Z4h07=J;Hip*q8AtEt($Dq~Ec!bA5KMNs)O`}^8t=um zEY&g0(zKNHz@O(i$F!dBgX1_jIn%M?G#w2T(c0Bq-Kj#ieJi?HE?WC5{;&l&Kffvo=ksQP9+(Y4sE5jYcMo7MjSrDiaGN#AFRAm8ixt{jKqtK({{RZ_cKaor-btpr6f!-{BSeVCU(;niS0n+R z-ax?dt4{!ygH0Z9vWA*hmY$KGdf6tp{{Rf7j4@Pj3kch9=3)j|4hDHXG=?-iNx4r0 zC>Y;Dw2#GKU7_xz`g3kcmYHreRkd+rY%rFPOv-Xqz!~F`0pp(C^-rj9--`AKUAwN` z*|qEPipSDcQFL9m>AmJvlHYH?+nRGxBRpa`LXZYZfER^gzv2D(Ds<`aa5lvO&UWrC zKgHkU)iBvDcdf0VztJT*w?$1sJd#4u$lHAwi*3U=?SKaZznv%avCC`^#>R1s9apvU z%B!ZN;`(W8p5GKtN+*V>KwxdI%F4eZJ&EJDf#02L^p2MdG5wxuEgNoYlqXErc1$^X zy60(XTWV6tbfysx=9eoW9l$3eBO@Q9AGWklP3aN1IyQmtWd>(Gg4j^iW7V3-npUfZ zXC-7t04#Dc`+4V_b{X-hc0L5}4K(kA!U!%qxbC9g>8q)&?JCpUE2+b7oJQg?25>i) z?i3JybB`JX-6keV8;s!@u79WExhX7tWm6ph96;m0MArFldm$^eWD&T1=Xbk+J-Fw$ z135Za#75=UUDSksNevz6(rG08aS9qT9YHw+F=@O4M`PoSFZP*QZYO}aHH7)lMdQ)w zsyYjrMKx1J?PHD4ENs|yQaj@#f_^+}LkH`ZMQxTRNL#gn5FATQH-(w0`qIsEyH(q& zBUHR4LZnK>g9>D>WG#j6{vm22 zno%@TmsNDmGFKZ}c?=2U3>`$l&S7|AaRR^{&2Sa)i4r?kqImT0RZz=GMMXzVUn(Rf zP{|fnY;bwN1pI@Hj1k{cjSGAzvSeY3PISBPH2bD1-Lf*W-MWs!B==63rK6sjG;hSm zG`m%~><>6{OKl|M8PIcB=^iKXLDROL>aJ}oN{{x6poZNw73#-9B*9!e#@m(nBih{O zl`F^jjSDj>W@0)fG_V&9y>F6@jv7U{(Oo)s{h_)F(?=x)my)+gh>@WY7NmlE2mVXe%PM|20=oQPWl)~wbrMOE4RdrQ*f)z@&N{C&N zLE3OnZutX~k*yA&B+_`2nl^!k{A?^TmT7mWFFE9 zz#RG3tQhol2+O_@C2f^0dP}D(pryqe(907!iQ{AZw{AB9f&+Z`&Uo*oVj>ufaXcHo zJ72f51H%E~E^91w_6nM%gE2gFZEfrCreVN5j9`w#B#p<$HI>ugsSYMXwK!1Kz}*IC z(AiL3db&!-hTTU)BQitjxN^m%JGS9T;d>M3{@P)=U}g9QJrTxqxyOfk5$KB6klQIm z)qOPxwp==iC>+m91ZY5GvBP0maxkEb1D_z^X`iUNW8uXzwCf~_1DYqFfVq#=-B$fQ zx?h?xWuU8#?-f-XFZ{o8ISN-8$pH5{^q){%*zAuP;jh#U^V`$q2uLJ40Hf-ElGrrV z6zy$>E2LC4(lRV>69NMBj01vLFMN(NIr!Ez&khD0k30A-L2-h%N?U!( z;PE~T5tz`f(r{NRoc^c37}hsSjtH~k5jM9!$pS~cgUD4>6c-9QF)hZDJB7)ndG|7H z3X8^6@LK?2kDY91VM{1;V`)3{+hxLbgHLz_`k|YTU&l{96%yJOU8PzESsFQ8B=V$; zA9t3|o<=dO&a&>SEQ1pnjtvw`fU)ioNo{Hm2jK^G*IC!n{s-|C7psh=BncHE_M(7B zSy=MK9lgFhXtDH|*^(VCcz};vv}PTWrpa0LCh1J^3VPY8qas-1l0A<%Cn`@JfZ9%Z zA3ERa*%KJ$kdv%&>w9)WE*D7BvW^Pa=89W-HEc2o7cvwPNV(if4hs?oAb8N8=NOx; z+nfRXN)gIyPW!1VTNDCwa7Lw()A>`wCiU3M@5bDN-IqMzlYx<)AuLa;ak1#MAdpDi zU{G3bq3slXL1LwLy+Jti+BqC9)m2ia5ue5U75bk%%RQCHI5=_^c%T1aF?RtOi`0^@!G;Qpseo|yykMs|vI->(S3 zgTY=Vu)wzj^~F6jD(fXdh7!fp9F+iY2>^`qfHYjlU6HXhf89s}x(w_7uC-dBs)mN! z9Bx1ik;J?94&j3SW7y+QG!n|cK*vZ-aeXEda=n#PW1G16@}`WP32BN2)HL{BoJ7E!93?s?x_wu5%lbt z+UfOKcNWri74U}hum0R+ztu^!^bgctl(F%uR8zk*IJgA5#j&)X6^unGUNKgpl z9yPm_i-R1Gj}5?fG;YUb%#42y$<@%;+Un|>yLIk@w$E@3SSerS$N=71JMAMmAN{V~ z^ovN}V^V}M%<8NeYJ=3Dy z(bl`Q6f}41bJW~oK_>Xhvk8D1+E0Oya1Vpv@|o%X0A{FIb|0d%T}X2A^!~ofQB_-S zl9oCEQ`Ik0uE{AIhGEBmFbNs%c<-#-iJ8-3mo!FR00e=tcWsIJRC}0*=C)PO98{M& z`Z#8rA*vmNDDGf ztLf{!atLN~BP5Ezpxv~knADK!~ae2g6A5C|h2U>xg3xb2H2%!p9hx3n^z^7U=9-)Og@HB`0e7x8d~ z2)>y(Z-)5yWDo{?>iHd0HLi1Ey9cmb=-N%IJ20894RxfJo@zl~(EsikcZDZ+EDn;`l5N816Rt#7>zy)bGIGxxDk$V&UxX8 z`dpacvMjcjc4!-)WOlc?TW_JR-8ETeqoa}vx{H)(A)WRrF<>$VJAip1yK;1-y=HkY z743677ZI_p{{V_BTWOur);^xLRnlH-p|{hCHvk?|<-y>bA8z1CE*1j`Hf=3BJwwP<8 z)x9AVqWdey929WIph4;%NX8H5z#iXF;{=Qm_Ses`KAw^idYEt_o@(xEwWcGC8`i1p zR@L=VIqPk(O3Y-2s#0VubDg8GJY?kg`|DF3+0O?eEP&R#dZ23f1p6f%2|o$T(zPp1 zaB7#YX{t=IgH;?HB4m$K2aSLbO8#y;YtMa6K2%e;?AXtg5D9a5yWjhvxyGuNfxRA@ ztfFY3G%@-ynujtY#zO_eGP7g22LO2Z*SyRvk@VQh2HgB5B;{sauaeb8d5&nhg`+VB zDM<`$$y1+b!#4~-#t80n+~^OCwT0*KR8nXjWQKa`sjYOBCUu4=+I2EhuWxzJwYw0# zn~dpPHcVo9>JtgI)_^wf$NNvI_f~spz@ZkP5UZB-`0oqWK$fxaVoX zf5f-LrpDUsR*S538f%xF4^u;afI%}J^Se=^$d+gmT4954Q;Q{ z3v`_mcIo>~;)aqdEU}NoJkgOB#0iX&hlSip3VCDLd}e0!>3WPtGVd+^p%_RuQ(oyu zaHvFvzIvzwHpd0jh5XzB$!u|!9OGDdcy7vXg*)st*!&U8hh)W~>SSs&6?Ajdsz!M@dUbPjj-z6TMi4$&y0H0I35Ua0$-g$mhP6cyZaSE$DV`-BaBc95cBUE%3=jA<{qb`!gEH!m>moTP9sisktT(DFfz&YFoGD#eh ztNlrnjU;StJdHGOZTl{eFziC}NznHxDSr_i?v|Q1_l1%I?=)<{?*L?Tl5lW-a&)wR zPsx(V0LfB+Z+05);C2H}o;>uak{ z*c!<2VlI>lw(fm6KFBb@OWfUb30u$Uh4%H*HoAJcI(tQ3MP&(aD_|87K@cGAVm5-= zILh)qJB=xcjUy)U8o~)1?3r^5Jgsmi)kV1dW$GKH>dZy5#T^9F7OMzFHvEH~YHy-2==Vesg@;- zji5~2ggy58JpAccCd@F$iKq=C&rwE25{jMB;5OYaq&v5l(-jqDHe%(4)a4raIL7g3a5@UB|r+%Lio<* zP)1ni1dNR5SkO;^E?D8yC6HfsJ<+H%(eA14A5!{O2;roAwbqInHC2Wxafzx5@EL&$ zc7Lzi8aG+`f2GZi87v{ehg)~5a0h?*MGiJnYpuGzXQ-*_Dm#r-H0>IaR1&5lxnyA6 z9#^>bMlO&=jo?d(x6JKv}(V_5)?Ed7>VA-GwUeXm7aA(ZV2sNYL2=zrD#(4g-Hc=4 z@unf_`JF-sLn|E^2E5+es!QTUD??7iFV6n8vtDSrj;`NPOD{{v6(tfh*jVHWP8v`% zf)^o^oM)VkZuF@7rkBG!z9TfdVA|fjP{Xv=>(@_p6$G{I70oTTiix5VOpaM&GAKKM zaxw-n$>$srHR5o346%m2gh1x*ONHOJ;G2xFk*j-J*PyBFclk`z7Wk|+Xo|}4uEltU z8)irK0@wijjOQF|HX)Oc+-#SLTJGauv~kIWuX|5*uKFUu2U6Wqg4cC=c~F#TWp7Bq zv3oG%mh3UXJoy@oTwk>6k`tgXmg(l}zrqYr(@7kaQ}qg}ucL2~8@$wF-%kLO)uor; zJZFL!azNY;b^?FQ>t<{Wy?^{?TRi~1`|har8%yf%t`=&(pS#*a)V0V!V1P~pUaGkE zka;WzVSo=j<49(GDVvhw_dCM}u^qVQ^-vqVB&Cwj)qcI=($s&Bg;qGL7;bdX3UkHN?Q;AiuvL>;8nUyyV3r_$QON+2kJCDn z)1$|YvocnfkPhCbVktwOZj`gzO+=5AfCw+U`nkaW0P}H;0z5}z z!{Fih8copObIDzx7lb%0OJc2(y0TQKXc()ZJ3|kUGs_dmUI#cB({ImsXPnwFn5qo26vOwWo&SYgp4rA&QHkX>n>cuEU_5H>E^p%2%;kMLE))?Q`fLi zR$O3$qPm9JB&L;_GU+)h?Iq4tTgmOu2S&rr&vgDCK{_mfW3r^o0BO5QzpCwbsIb;l zboIjRVtvUp3k6AqfkmE%bkSm(%-zdZ5*AZMJM`0O`2A4%xb^!vthMvzZ8Tcn?;gRgRy zS6lTJwzjs_a;~1580L;3yVM0Z3PD_r$AiEe`+JeEFCS5p7I5b~R7Y?|!K2^VHyEY> zih5%8YBxzLQ>bgBriya0tkJ5YHb-XVOJg9n+;-Qh#p-=M@yU?vh7BgUQg&MYikiATuG6M=COV`8~6)e@?-V%?w&e-;z~b6zyo&C~@>weI@kg zr8-TD#wu*=$P28sTTLv1xo{WSsyHL|&T@37W9lK|I5oGm@O^?}SR-lj1$#=O_gO5x zC3&f+qm+6p4O~t6vBMO`$rv6+ayw(Tv+=MemPX_;-WX}H4*I=^!iPGVLRC}!7V3*t z%DS@YYP;Ix^nmpXMk8e|b0Vsq8=Qi2cqEN_8DwFVteEL@_$4AnLtAZiBP;h#+q!ND z<)oUnCa5U@^y(Nw?Sf7jy{Zocw*xr?xCM6f>=wS51c!epJD*aet$W26f$osqQ+TAM zpoUl~>edRTa%GMZcB1i?@&RDn;BsZ4`t*Wa}v;YHN4nDiQ%F z4>ACvHaTpQjksLocOdAP&dGLMQ8-qo0nhY9aE$qN(N|JVDrBgq{AEQ6RxHwcNF0xH zkOndW1MJEzBt?6BJ5VTW4iTGbDgRxWd18}NFqg@CXty0o0|@x zFZD6tFMf2dS^b&8)pCR6%Pb=H4ZGg^k;e*k0kUGz&jtG7Z;A`5)KBV0s`I+@89CXW zHj%jZJ%`S{hgRsg?Zsnd8i;V@c1p*T*?hoPWuxx4`pYevo~o;DLc)rR9DAAeW#dM1;c0UY=Xx9aBQ(~*8QHGXu`?EqC8gQLTa{xJN1R8I-`GJt z{{T&So}W8lGI3;Znr-l%md>Jj2r4S9zJ%1$Bxaututo_R##kQX-P*8oiX)qP{hRO?v?U+ z*`yKfO&!(YI;9cX&42!oZxxhq)KJNIueQR==}W&O8;BdY1$$#Hl5yikfxrA#%O9v8 zop(yfD@ZhWQ|++aI-;^-uQap9%so2U0y-}&3KhU;`xBmWai->r5Oof31jjURt8E|<8F}GAJc2vw-BHtF=x!Y5q!ve~;JZ-tSNC3& zr-G6f6rohpZX|*XtB!J<@D3Xz2hZDHmn#nu-~zx+4&KVbLU!zc*5w5ZbkNjooS4{& zp#`LQFnfcIz>l3*4?JVDz>YBB560Z9yb4I$w@q~|>QuSg=7O5uiUcx7pp;Nf?amJp za&gDop4uKa)42JWe{Gu}#9VHJZoj%xd1PoNHfktBFQs*9a5Q&5p^^pKq`p9yrv3pP zut5O!#!2ywd2WH#Acw3=jrOx61KbV!Uu5LYbE9X7-6V@f_P&$p`xG@|ntAE&Bm*Ii zpoW(K<7sXcdvoW;z19=Va+>$AgfA8riY`FCK~-MF)zg0w-0D%vDZ*s^yWn>1$!wi` z&je07v$L{^((o7w^#^Zt6Pt`zDAVcB@j6jfZmlI8#nnb7S(Y_8IrEHzyyKi>o^|i~ zyC-YfUQ2_G0DD)V_gu$%Ep5`Uy%=b%630DTQ=&r=42t}m$fe2dPv!76kuO7Qh}SXm zjw_DznPKdUsxSRnV3H)dS3H#QF?ED(Wr{EW2v~faklc~mK*;(**zGP8mjl|X{G@F* zX#`EzGu>vWwcM>igpca9%PB^u!QhR*)tqqI931G~ZdkLT62Nsf`S${dReOC^;J4FU zsv)>r;uGB?RP|j50ftUa#q2UiBOW-?y+0JY9m5&BuwL75@_^=^>(y;FMO8F%)59FI zu2xT2g@~{x*asxH7zekF6Av#URybtE;W@iEd)4Yva=bCB4^LZRs;D(pOHmxKu*hN; zYaUTp9^;JXKHhocXxQ;!hxpHlp!)v+Kg}#8aUATd?())4N&=}!qmvm`8#A1+L%Xpf z_aJLw+Gq&(Z>pp6Oz%_npDm$fF(ALE1+c z{r+?h5?HTEr4EgUayLnfwa(2+X0EwF#z-P^NT`I!io+XMU>lhGyMi!B-(GJg6nJvY zjpUq;K-}%HwPGt|SM=ijXSgL$H88DAvWS;!;yK0!0|pt`IRKNB@_Xwq=wmz(5dH|xcpn?F?p-@a zcC0;3eB7R!7-f(sMO7=0ZzQlJ5!~bLrz7h>W$_>lzYVwFe)aIHrQOmGt1G&W<3T+& zJ?`&2tb3CUgxXHhSRC`Twhw%BoeB@CUz>?eghtnnK|*vuaD^=tJzGy4bhFxOx{6K0 zBH@go)j@6894`cIQ<6`&KMV9|;$(%m(itQ>*9VH_Oi@X82Fat)U13pglhm#0`6QrG zDUa9P467>-%Px519248!-0RU}bZLBY>7%WcN1WO+y8UXVxJw6hcW{71-KPpY|on3&W|E@GBo9-0_w-DDnvLSx)X9P!Cg4htR$#s@m4XHjeg zvB?l2+u1^YvbyQJ?b4FDE!CBi*1<9dhNPiobp)wL3NnBlr*=sn-$%jf5;ij27`#T_ z^|c=3-jIWArey_hNW*xJp6@gfTuEO}Nuq6q!ux_1;DLa7^ZIH2vm5ZIEyst%i(n1b z{r>}plWp9$`Ol?C$>nKZ-pnsxF(W zrlMHQMI5u>JhMj=B1mz;3f_NS0r=5zJVL|bIGy{VIMRdEABVQkl$JFuv?_rb<;F9U z#@qr=>OT5?tfrT|(?=)RZm2Z@m)U8Qdp#`1c8-TZzL`U9D)htoa@)k z7;`llJVXBD{J#X~l`J~y(|Wf^)bQ0wRV;FDr;jV@qU4Nh9rMXK{dJxLtS)o5-9_lW zgS~adJhocPrQXo(E5yvkGYE-}cAWczHtrXZ&T*-GZ~!jOA*>v@i=?E$ng?~Qx^ts0 zR=Y(FlIbN*+DOth1QpLeoyf-H#zEu0v^riu#%b^!t%rlS=a10^tqtKfY`sNG)AAd2 z<9f8N3=afEj(IDb@_QV2@2?vVK${)MA*9UX*wCx~%I$R`>AI(-YiSaursr;w8el!L zFS;$~1917|U<{B?InM@zA~@Z7KnIWDJLyC(2b8{=?`Rhm{t)zO3bMq7j^$P$a@Zga zIL>=xzKG90xk#dTRMqN@Zgf>L!xhS&qBvGEUGfB=_5%Y4AOcQt`+3%rzKzCv*_K!f zz}VLN^STp`$ph8)Dw@y5)K%P-fLU+?kf0uL`5DJO^Zr_EFI4jbBfP*>o%UQ^;YYGo z+j_1}m8Ue6qFSbTL<;6ypfVg2$t0f{dlm=K;lAN1mb zt*oh4Ep4BMLo_z!OuUr&WqD0$PJc!_UR8EMxDT} zX#1)?tsitHk5%-oUD+yWt%ieapUh;bQzHp5LgU;3+ni%K=S_W}%0MjAf*a=DOXyFh z_a~JoI)Q7^okBqL$?8QS;BUeBuT}02gebg?jxyf3I zakbE`JxsYmk|D(SRa3YB0NQc8Xsh9f7#d`y#oXo1};QdPJm?b7o{OGir`)A5kj zi5xPj45u7#JbkzywuSW6C6Px1o?WC-*%-Z{pw{a9r26XHSuMdS9KPN5l5ma0*`vn2_xJ+oS6U3gh7Wb-!GkSrbM(^*;9;c~=D<^qpf*ZJxTd*Zs&=5V4^gYwB&4TG=&RH;yCjk#qy`{@%-@^iapd#!rsj*vTt>iJgelfZV_6RH9+vR?Hn^r*Yd0Oua4v4(@PPgIO5d)XTh(@YDye-MANP7x zkA7xXaxshu&NivY1pcQyYGj3JGXm+t<}M8BCs?wo4b{sSIxvOn|Au*sOcM%Mv*6<5>)h85s5tEnSc7toGVa z^~cgnrPgMG3hGo(NSOAi45e@hIlu(?AED1WYchDT+xDFIe(J!p@51i`UcgyG6n{BX2*c2Ws#?7^)Ay?b@xw46fufha~VG|aM9rBETH>y+=0$K z=YgWf&@n&{W=^BuWH&ofU3FdhviAfKRNkrSYMx?Ur~Im*4B+DoNEzS^ax{LW)12cB zn2fN~Wbvqbp=f9EC5^Jl)pk1TfwNboRSc6YP+9OoF;M=@9u%C6Z~-|w4rfowcCAd< z^S|^$A;i#j%Hr*R>I;>I>b17JcoV5`1Wt{+ZyVT>tNPRACi;KzYCevXT^^9DViXa{A5>+Maw*PQ?(ixl5ak%cwWWSk%@Z|#^+5C4()sFyVmuUrsZR)N@}Z=PD+?I!S8_w zA$Oc(3_;^LAnVffc=H68DJO2|3~NULEXUR!vFYmifpfR7QrY8Wh*Zr6$EF8@cQ1VN z&yM=>{Y+)Is+UO0ZI1rIFvo1PuhYJ@x81K)@XbS0Ej>jvV2@jJz{i}Q8yg^JJ;~O` zTgvc=lnZQ@X!fe%8PIMDcBj(*l)gnzeW$lvXIT%f35-Ti=aM+@f<{hroQ!K#y)u0a zmqF6t2>2<0$ZK|sMVCt-P1jM!ZGzKghN%p3$2-!1$^pRz2VilWWaR$3$d!gC5Ju|J zyG(k0)H@GO&(rc=V49p@VhlXTmCXS z8>JwlhEICb7{Pec1eO~>_igr(#s~TAO$dHWaYXXQkaZ9^K8O+%R7w@SR9a<%5mz+? zFh=fUM^;?PoG#`Ep54DqIUFsmFLi{n53ajhg=J8xu0{qT?dXi47|ALJW$;FL#zt~= z65&Get&xYho?s8@@S{$nPS|>)>mOcR=w*7i>uF}U6pQ{)Qsoq5EL?n%@Oe1Sj}KR4 z@dEYW4O^cs`_9>qIpZ%TY*?3{GAWs;9XNp83z{LGjL%$DQ&w zRE*N$wIdHwtFJV49YyGW3%b*am63Af24TCLgVX*NEVcT#Aq(DwGA}QJJlhD6}g(A z41mf82H}D;@!(@ajwgv+N8Tj_X}cnjCBlewy&XBtBdJN+ju}W@@dBU#LZofZGn3q&KTU0yVdP2shuQcZ;4kMVl(ajNpB|k)NFiTlF!p|Ezad~tCHxHedtqPtHo@Y3RFLy)3b20$YNE||y}_{KFDtcmaN zWo@wX4+Q38_OZ1#~#W{ z>;#&>^>kwJ*ElW|lF~?`c@i~vOhc6lV`GHmG0#5(o^`p^HN^v8c1wUB=d!EkEEYwC zvbFVtOMA5lC8b)D&OZ}USb)b1xW7{3_FgVANrh30tfv}132rU)A9IHYc=YUEd>R+rh*q(}( z^#MS_86#N8c#w?lWjP0eMzeZkFUpM1^ZF}fxJLf~4TjAlucSS7X6pIcm8FZQUCJV% zzt=?j9^^fLQk?8EW=R5^26j9X*!e#r2@SJFOk>C(Ly}KwbaN!hmS65J8E7sRZ980xV ze2U<-&-q!l_a}B2jyz*dV#^~2l4y;oF8Oxd9o$yuJ532%dbm_9bhdTJ3d z?kqFkX~7sc8SR}c{7D+m{xCBW_$g_KijmZ`H%9g6RYTLXRNY5NYAGgglS@S8$g+Wi z#!uzOG7n>(dDb>AL*{kj%0q>N7z6@GCjCbV;_Q7tyE>NE=~@~ZJADmD@e1{Nl9PWf z5+;rD$R9cTfH>sp%=&%txz2mZph*O6T-vO1YL9l&Pa3OKcba2UOM6iX3SZE0cMcb5 z{-Kf%Pk;}dW8?KWr-#LX!OcCwfZ9uo-F$YM+l4#apr@*XYq^AO;Bk;L3XBfN&pFfB zb4K{&EIOnFQ+<7{Rz}%KYqe|JZ;q8wSM?98S%#Sq6(Pn7dq?!=fxyOj&|lZ@{{W82 zL6g$=+msmwhH51oRecTV3ZqYVrnm?oEHOt31QLn{)nnW!QgVCv2T$e!AKiZH0SA?D zaIO{_YL=vuh`dmTjKn=XryE8=Q;t3b8$qyD$` z2Uc|LEudFf779SI`f6DlQfAjFXSd+FK9lYHMPXB)W%j!d^!Z-L?tW(Val? zRZb_VrFxkaQ2||}+l4ArV1P0(0Kgn%=rCln9j}j8q7L7kDR49jtopCi9WiO?KBA+# zS|+^IJSIYv^xL)uVS-1(k9Obht+qz!op83?M?7qMAcN|^IVpeK`U;9@DJYR7s*`W6 zEMA*9vI9Ld;;_Ecv>nAs10ld8 zZ`&R?*0W57p@H9Z9I$I9k_W7Am6ndRrJJX&^h+p-RU@fiRyiF10EGVlLj#1*>&`}s z8z7Od4k6p3G0g^xZGMXMCDv-+r_}M^C?k5#c;pUsSH58SGLf~~ zmpaCf-I82i4VLvyS4V4tqMD9u!%b5eark`WVE`Saa(O;B>ojz@;&Wxpl*k%I2M*(V z{)kVQfIt;#pH+S8?xLEumX^z5G_?w>(5!nTa;vv;I3XEF2Ly6HHKmo+A(~BX0C(Ee zA;#j}Yd`gWK=oAfw8iU9u0-7$&iG28RF_sflnc?gM~R-p$n#QCO{u z{4FG^f{8?BSObf$(Q+I-VJB*;Nz3QePLqmjokv+k zRJR3`wKCDbzN|<&BLw+5_Tc%_ooZIa7&00MVf~bhlFMtGcimi5eN*TOD4?gY+%D=Q z43V`>GKk!ta8gePx#JiCly}r(OW?%|TeX@W@Ph_Fj7uYN6o&dXrs356Nki3Z0^Szf zNijnrymd*);0$bh9(({h^TuSkqZ1_NxN4FzYWd${qF$Uv#pc$PKT`URpwL^b7x`%D z>E98-RZOb21y1(C9h74n6YcrdE;d$PNhic}H@dsEX&wy&$O$`gzR7c|sE4RJ7S~&K zGMPPiX$Mxjtz4Gx=Oq6)B00XYt5Wm zs^nK#ien z2+fkYL&H0q@Nx%iY3yvavQHi(e%_?_Q&dtHuRf|=>g%FfYB~yTpIGwj(t?r5q^n8+ zJg@))pp0V}I(`qS1uwRqLQZ5buvLTanDni4ybovN3$8o$rvNK<6d8{!s!d- zXrk}YRND1?t?FA1#VV}`YUyOeZRrLtPmg!08lDI^0Q_V1)7dP6@Uf8ZEe)&FlmI;2 zuNHsyjkwzC5}N##_v%R%Jqo+R$PO%v%7NfkYDfFf4_M{i&Kqk!(~5?5PbrM})S zXE8`zO%uzIU|_l4taFeLApNuPhB|!k%PURHBYGmRW0ov8W`>X(tVx*Pg$SM_?q(o@pkt(PXYG)l#>x z>w;8oI3`1uVBPx@tUia*dX9S}XpFg#)}dm6JPpWKi}#1ixJOn#k)^b)WvZ1Tf}C(l z&RIbtBnBgcfH)Z$9Qf4r-j>X4bCH!8js}W%p*@MN>R3aG2Wm{~H(Ww5w6u1c>aC^W zkk0Rjp`v9C%XCMLwg;l^pe4iCM1s<6|nB3kPI08R=jin{A{T|+nX zT4_TtOyFV8N#qlb-NrBmnwj{mjj_xHjyB|;)VQ=0yQwS_q2a#INpq_g8mNjhQ^&Bm z1e3wrjm$H^#tw6(hf9nBoEzq6n@4YpOhnL5omXgE>I^H;5z)_Z>Dtd$2#dn*x!4O3 z$bJ-HV1C?dAFt+R^#dRO02@OokSBSL?Ogs-~XX1p`T4wlG`K+S8nj zyouN<_Q3KxYeHr@;jN9gKp-;i)`jWbujqRZsd)Odw_5u|yK-q_GgQY;+kq-F7ly$A z4<|LmF&+(wDH^r-h3B^seVwX1es3OV!xcT4q`y>Lk9jFUAV11y^U<3iRV6f)RWuQ)hGeUhC;+MUjO3B+Jof(j*Tnjg7)#rl z1y|?}G7029R`P4t z+q#Cij`Iz`Qm5)`S58*c&{R~^#2Qa{k1Xw#Wh@ht-;^d#j=<~9{Z;iK$Le9RZgjV5 zAa1Q2U8>hqy}?^cLe`;7)N=bEG4bmR5EE%0*T>HT&Un|M!p@&D&jL32MgIVvsm7Gu zu}@cQprNU*Xse(S59QS{^^BebW41X{j@bCpa^}yABOQ$!bsgyNYu30{)Hc5859&RR zw)s(Nw$i}`EyZVQsSBt>2ap)^j^Oq=8SSq-^%v6;b!f~}jXJ96io3r4>cb$g0?;iE z*sZeEJQm99MPyd^nWtE)0o#^1Ra|n*gUCIOHSKXa7EXbzP}l$ur+P_-v>Gc!bl*f# z&^2zgnYD3$DzCm(J-4L&8=XX}Q(Yw)I7C>L zL2PrpX9VX1K6KL^8=XgDEPB$rq99*oWm`!F1r&dUjbW#kd{GqvnlcCFE&d>QZ?W_PMfY;_RA zU=C+u3CJ6AbCOQpbIo{%Imb3gGT3XASY&8iJ zf>doARYx6<2jFmfb6%+gnYbn%pZE0xxzcEIFZv<)u`vGiA^Mi4)j-h-nUao?V>3Y5 zEPYJ!PI&yomFJP4Iw0v~(Rh>W8gFg)8>*LrX{q1R^^vKuNBmxfN(21YBn1lRI17?k z_wDit@H3y(v)P!;e*~HiB&r8S)285Q*?^;9I&k^I9Wd~U5O-Z z_$&7xeJ?b!GMRd;uC`G19n~ix;f?)tFAS}p%bpzoAP~9dcgC}# z`fn}So*q-(MYprN{{X^vGS_V0Ql%;3vk2j%8K#K4w1z;&X$i<|4s-24rgf(mO*rrz zfBxHi!1upH?4m}HnIBH?Vk*_3r;?gi$ z-v0p2)%)5GrM*K5r;+rHkkb*L2bKKP}ec0W-)?@SKliz z1yrdWfdc^i{f3(`mx5y(9y=@euXSqxCVju@=(g{zG%Hz2FRn~!PO%hh6Zrw=Fn`Up zfe%E>k1Rb({w9&hNd#36kw}fZ1VfK-7~p3cyPn^sk@_9D*||>X zlDL4%Yp8K|!8?9SgJr8=1x>NqDLPu;O&s@%Z(0)~M?0wqI~UxeAbU>-CnvW$pQ<`; z7e;iFNGIpu{OXvUP{`3S zd9Ayjs5XPfd$9N9C|+h>eSO zJ9{HA8?>B;EQ-*crmNh@O{AaYDoZq8#1jEsSdcON|K&*SGfe&madv>Jbi`r3)D z`yR-$s^cS7x8mV6ZIL8-<0=3h*ziu=Z9lG)`u8dLl49gE0>?0&{cTqmd0bqYeJ?8$ zMOa`mMoC};?A{5+aB;>?PCk6=*JOYoZklKy9k%3qDQf}YGpRaihNM!~)X`J=u_YO93)w(YXvmSh7sat?Ez z206wKqHMM}>2z(?>szaUnzlR5maS+gsI4hZ(y>~9ZrpAhdX4eJV?W{2(mGhqm_YOI zzqg{3%A>liYz;Vdn|>MARSmrvLCH*K1LS$mi6k=Lm;vyok!f$#bgOixN?AeJO8TNy z+I^%FxFd}64sn2Z(K_=X7;W(h0{8riQcy|Q(pOpScG?=6$(br5hDlyD3H+spoUmSS zPCy*}yXh{8i5@IoE-Q%-+K&U`kewZ-owk-bqa;;TzMZytL5?=YHkBkqTapF|I0HQK z+fL+5g%qZI=^QNG0b}t~MXl1*w!C_DS4vlDQ`3@7l(8&?B(baGg~m4d&*tr(c-C%2 z%ygny$t>;gcvoi|gnw)Z|Fp^f{%F*Zicu;6;7O;+z;FR=ILa{;VuQ-$4Cjj8E;2sZt zI66uFIgz-ANPY?1@=L#!#^X;KzC^T`@gPNx&1@iDh+zU_+J`YVR+swsM&`)_8FzUxs1S}x%Y zMB+tY4-9_T9z0__om&t80F@b%c?(N+0lZhesvVA$1=hb<>@*aNZnYKlHPUji(^EFk z;Bs(q2Z7HV{`%3$f=o!fOm91^bN=4vD4fYhp#Z%_v# z46*D!0PIhG`pT2)JT9QdvL_!Yzq?+`n;WQ(;951ceR~j5rLNm8yVISDvZCdq;|-n1 z2rzg)aD3^%wm|5NPVP7Zl*1072t#%|g~sDqaEhWEO`?Cs0dd4#dAa3e_H^h`a6TWMGu^!38!MN?f3EhW~Oq7nklfQp=)F+SI90mpvX z*Pj0XLBtm3O6`@y-NhsuSfVu%7D`(BNMu z8~|27fT@-_7Ah(#$qq3DMOX0roGTVQE>HPsj+=**k~~~dU~^`-_Ct}?xK0c>D!$e#A&9YrH(kE43%>Nh0h?8 zr?V0l2jle9Jw7Hg6tf$6nhCSb@_z&{H5v%Lt=2gwqr6c`6jd>|r5Y^Tqj$mf@O|CM z&IfD^jbY(+ta*qrA2!5ON@hj~>slplvb0c31wC!jONGT0<^)g52+7*SV83DCfN`L| z>#SHL`!t03hyMT*Ryo}oEic`kWR&ZEs)~w+zP66pT&fSK@yZB|6!5|^*q(F82ak<< zQ4~R*JVcwX)e=Asz*0tvnyK!RQrq8Rq|Frj6Kr(ZLP3oG0Ejj-kB%f`%P`#X`-N-2!#TqOsJq2R{W%^e^4Fs+-FMV^=9K`w*tJcA+0oQlk3=0 ztBr~#)K00kii>j9*VEhUrbgfL6+rgoxFyKslg2PLrH>16-r;uR$oNZ0H>ADk!)vtO zDJotHt4TYIjv?5DVEY^6JaRnqtf|X&6B|G#&mO3Zu(kKo8~uvo9m1OZQAZ^NEV4!h z0puO3#CxziZtai`uv^q<*xc^8GLd!qem5=UUx5 zX>z0e^lc6s*Xi9*w3E8Vw_cB+p`@Buxz8kVA!(uAl>-NE--Fwp-Sp-(v9P)T&@~m0lwn5f0-Jcpz!}zzzf<(Gbh+djt~>BmJWjw+ z_FtgA1@z*Y_tCM%dX8FpnC00UsVoE_k8#KKU>p(HV@+bVF{GWrK8Vq?WZtlSKZmC2 zZa>3lT8bRrLml`(NX#;|;=pD&Wu2&f;fw#=Gk-!+UY{H_c zbC6GLkK6awT>UC}HOad}&4LXPz5JrMmozl2ZPymNrS7u4K`nkDo70qtnS@^9fT{q( zl0RemgRe&(JZy+Y54xdfq_C{$&Y^;yVS9>8^dV34x3aUVF(Ni3@WYRGFh~O$2UCz3 zUmJHW1b$&s5F35b1*_=~L<(BIp^E)o4c0_~mXcD)B2Y82l;e=x?&Rc-!&uneJ~m@U z!r~s$xK%-2RnqnKlhDTPaQsZM`gI%Ko6!nO9^~gLN4K{>IwPYpY|aOJ3Xf-X{nH&c z)Anl1lv685TU`pPDI!S1DDnv$6O0kg7e#|3BL-8Aj02oTuQ#WC!tV_;t=%_luA_n^ zucWD`D87i4;HQ9L&*e_|2b}9VKd3OCblJa3RuPWL=hD=)@KJ~=eFml3>8X?hhE6t> zunVgMDHr?o=;vd%@Ym?1G8=9V}Jk}1}8)5I8g>i zBgnH>JCwL{hnQ};&-G`hFZY=(a&+BQzT1F*66Z0P1^{T=JTW9>W6rhku^Bmjf=C>C zpHw%^A#l{t0(NuBMsHI5|tKIbuvpKt>uM(iE|@Hp0tHy#+r=E>adcSMvB!12n{eJ?fM z@eFA}3~^J$fHa%EtbCjvMlsv(ubJll&oQ6WT`-l}Tcn-G0W|Oq$ja|&FhLX!<{;{e z?oiz1WpR;)IsFIDiJ8(NWS%xOkDz(tlI~?BSJe)U{8HR)_V}WjRYa+dp&{BSU4{!0 zo<}=F4#ewHF1v`=HYUBr)wwsyN%5Q6XZjAGdQ0RTK=-t*rI%$LRV0L!O{!OT$=m9j z5=IW)d3@fxlK~kKxRYQ}Cacv9#z5IMu3t{=liJ0~GSSw!+^mPVVUV~!c+T$LJ7Zr} z!_96+Gcx5C<9)8EscTOvXVV|1cS?StH<)E=J9vmw%}+YAtfX`9U-4zP40g|cbe>;M zZhJ&?62^-IPdohDC}A$UT5|sYbiBbxqB^Oeq(un(MiOz3Nd)oF>y9(5n0jYcIZT5q z%ZvpA9p<64Z+hRp$(KmdgE~^-S5KjJ`)oZ#*KamI-b3^BNdIF}Q*q70ktvxi+;sp@Yvv{bdy+n|^U`?c7V~Ef|8R+kwe;V(rcdJ;BN2{@NV)?Ty+`-u6V(=&tAJpQx2~I%$^0 z)TWAeWr)OCdW8YJ4Snk@A~w(g$#i*3A=ltpY+A}OPip!<~J zwy(xGB;!5tp=B7zxk&@r8XeuB@SfHyPgK|0>FgB!Rdk@b1!E;sd$30s5)`(Je5E<3FA$v5cq;a^u=ZARj&SqcWe7)mr?Yx*Fj_jkq*>`=FCU zg`OzZp21H;OVZb)PVxT$EUi)oc2EZ5MmWh~+w{|!ZxJ{|2KJS3XC$iHcrH+0t8Vwo zikg~+jK+#5+98yY%cvVl;IFqJj|WTSbO&UJNJ%HCQJ+m$>aNT zfIDkjLqOFG?hnsxT*o+^J1)q54sU<`E@;x-EnNg+)|C`M z+`k;?WCC~!4%jS=*x-+8I3Qqgo^+>K>EB_H$iN&OcXW8FN(u>MH(Ye}p61Zj(NtDa z>@o=o63FC@%oOJ*=ky$Nt!xY|331CLmCbvIGzQ-P0Fpdzb55p&3iVc=vZ#U!dN__% zGKVpgs8?3@sLwc1G4OMY(%xCSzEoAcN1y0f8O4I`X|z7URao!s1QWDcpQU#3`>Q+4}^q$7I>r_ZN0aldK&rvsCmH*>9gyiBQO z2NVLq>S*7>vnGsNUXE$1Xswm@=x&q6XOcIK+`KteQoM!=4l)73`wyKnFVwvR62o;0xU zq&rsCzhtn>dw~yV2}^wH%QX!5SGAg&YUh~9&g{|pZ~Y{YKs=9-Il&p$_FqWFh!@7< zb#J|LQtkxoi>yCf`nKaoccpqIF|9iS;yERbH~3tfVSpJvJa^VhI&5snmRQLkumOLX z2qaU7s#MzAo2V-ko0PQGamnjdF2GO|&$M&gxf_AUI!0VWELzz?%ob{e{Q9VqX$*(D za@p*i9Kj$7H1u*sy%}b~q??{g5y&|pA@70npgtJfDWb>m9kxE+sPV`@g36D6p5fBB zDJm^AQQhN~Cxv5Z*f87(VB1d$1`asM&uw}sdJHhMO2;|Q+hgcayl1#mfLPN`C`=WAAfs|k>VqYTO;Z3TUf6YpNg!%Q>3#xCfQeMe%yG+3QkYX zwt5yYcThP3XB%x%I~~VjnqUc~Z2Fbbofg#8RG*2GSp7|y9=vg>C5Rv#@CzOZ#&xar zGsJUiYXchNuxO#c>=fuFefp1ju=QP)`hwNynWSZB6CsXSf#CW;XAkFVc^bD(pZ9{r zSsS3p@WEv)EHGLjy46%)oZl#7j2R;cV=>Cu+T^M6*p>P1`e(TU?}II3V}rE(frDl7CKg#2rfc!6phI{!NVn)+lGR$<+}oTO(w1A&!M z!Qf<^chZsk%bP)N~Yelr=HMD2a^AD(!XLa2;bO3`Q}5&jje*C#k{I@!2cM z8}%Cv1 zvs>G`&z)2B>!Yq+OnQQr>@AhfA|{HaV8#WA9huK@kT^d*^Q8LDb_7#J5Ocg@IIl0h z{{YEWwp#0@=_Gq?J-Xu(A~hj$sZgb5-VQb#mKpo`7#h4;kV!U++>b>AZtCNvzKzk` zmOGfcRaVv_?0>|uTmm@t4snB?#B<+RJxWTLSvLt}FG8OBE%GjO4BHc0qFaXePb?^s$bM(+d_($q2t(|$k^yX_nTd=0$g z>~zZ^YIb9rQK^P`Ne+5Rj zDcd!GkCm7D_UH7vnt1Ex{87C@FSb}9JJujTWsi`jJBNS#>pQG{X@v|U6^+!;c`YRF zH~hOOFv#7UKU525yYyF7UMTJK_Zp*GhjY_0aM84i+f?v!zyf&4#+S$Xn;u*@M+0s^ zJhnE!p2&G2tqo|{pHAy;eJANVJ(h}}_(U~vS5%ysNhFjfU|>6JB=P!_jR&RuNfe{O z%WFi9*or*-lo5>4zM@K{xEgWPAt+MkIH#1X0u=sQX9L&{Nl-z-7{L8?N&4H|{{Rj+ znmz|*V#U?^y2Vd)qNu8?6xHxUvT5WcO+x#DCq0;FXwC~aCj*^%T+X*9Msx9CKe`PB z8zAcL53=t7I=zz;>sJ$5Yo(+R-fHAQ97{YWA!qy0&^_>;#exm{Kb zkkUIe@Bvf=>m#r##=oU)*UCytdKx{6QA;Yj(@1y5P6C(@pKs;|9I3~RZhbNgz-738 z>Aq9*@P{Q`gsbWQ0Hu;yx}qBebxWmg?bI_ura@Piv_D1B^yf;%Ki-~-qrAu#NU6l}FSR#t$TPQgOLLt60G62PkmPk2 zxtZWZ){6z5{cQ|9uonQdO+An4?fS_Ww#7ZFR54V^1w%P_m=n0DqpWn! zp97;~WO+5ZsGZLmoGAu%9N$l@w+Vj{B}FA_#gihpwL>?xoMW-%40Faj>1XEPVz?6b zeAo6s()VoCI^RY0MvYvGkNg9->O*Rxb+>%p5XKn)KnWK~=oMtfjw?tJ;zhU$5l{{U;63<%mpW{sMw zCp`wt+>=Z8O@(;9kJQL*k(7u}bi>N)abZm&) zB$e8SeVRQ^!;TTAhBr8ek}J_a{*K!%*UK$E;;rdtzqBr~!BtSHQplJjpw3)jK_qv_ zI`nPEo&H9PKQxki7!3kSvg@wD=?dy7dSLYoG*{=HrZGtd94wJ_jUY zkadyt#%3M_4sT6+q!WM%c;9X~{8wZH;uej9X!Kn*Tpc5Cpz0}YQOM-MOck+}-GDad zm+zCGAOpsxY{=OJgEx4|o4#(6ci;;4T$a4&`Pr|7Mew*gmmps81l_E1*J{{YHYIm;pva-{G{2f6LoVNHxP zuo%#5b7X8s!kM1fm>lEE*56HfOQ-IZ`+HC|9jTIt*bnnJ<7at2M;MI1L}X%q10C^eH?ZxMZ&RTXk~dM04flDtGMI9K~``awB1Zpn%{_ zxDC6jzC;~vv%uA}%}LZvZ=O1pH1S58k(o#;$h>STAd$UQx+8m@=q z@4{Zq?v8re-)xD0kBjX0is*Mp&2I)qEXQie90Y@$kfSGnbFXKOlaG&#Bmg>Qfv6F% zKF84pM_5Z*9;UQb)zHya)k2H!4;@`Ng_qiSbw8M7XM%rh>*TrdWoPwP&i0KVq);Nc zCxtJ_b6gnEI6=2wp01T3sF=`HTPUfGOoli~X(Luql39o+1ZR#jfzKNH-$d!rzDHy< z8mb%j32}uBPNv=buQYu_9lp(9SyxQ<$_J|S;b@^RDOLkA1#fmc4Wp64;~YBqB`kZg zD1bHtZPh1}_mP9iJFE5U{)Fl!ql#-q%7%7N!-ZOd6w;{j&5?{T2X~R5zMtrDw=JW_ zX|02K6gk}=JP70fceE*Qwc2``;}s&=+T-d@&b1E!X(`?|0ZT6kiNRcuJNX>z#QiVV zv5eDnd7ASJ-sc}%ZxruK?1_g4qFZ1S>a{=9bT$iJQdHZhs}i0qxaFa~(f z^&gFUQ`8{o@!X)6-)?u~^yMbp7sd~W;C>1m`k_k?QczSHYhuY)Q7XwuFwv|bRDufD|;FG(Ay^8q;k}E|&W7ZF%LkT^VD#1SKGy3jjgJ1ffpd z!=0Wpo_p%O7p%h$RB<`3vJWG-y`ayPB~hwXy-U|Cd%Fwus;V1p`kj#lGbxkQMDC3s z?D%b?JP#ym8#n5FZk^V3PYjQHm<5K|-{l$DuapTQBEC~o>Fu+qtFAVvVphr>x4{!x0xvH|hDQ@Z2_X)U>hHQ_D>>pd~_Y0a*!2RN+d5 zKa`a?$-(DR_At)KUej+h(mQ(UL&@JkZ*EsEsg9Z&iYKml7NS(cO2LAvmEgM`HvVEu zaz6P7P3FevrE5cZ^i_PDZiDL{FUI~NhT^ksfGd=cZAMecU^{0R+x9xh%jr>OX#*Qn}Qc`{ENk~x|IHU`Fz zDhA+$u&e4Todv>Ld24?a40CU9Q(Z-qBt4}>7!yQg4AsQkic{{TB7y`Z;HKo{Sq*82ro+9H?u z&A3WsNh-qcjDou&3Zs#RB&pzXM}2yh^-O$eA{fMYRk$^N$!9bjoHkj@sV($&%Z+3f z*z0Y~X95IG`=V{xBWoNg=aZ5*XR*e!vNN#q?;XJDB%QY3w(63#p|;gXv?yM-o}%2B zdZlxMShB*SoU()MQcgxkIX-*fVd7mpWpf>qXRaINWsuMEHDMCP2Iq#Mo zxFZv_@3sOg{x8(CDHtt|6!y46?o{arVsNY;2~c4%Um)B+m-oB{z{f~2_5 z9O)z1$57q?TmJxyg*)HyJ(YY&qW}O(w6RJc6-<>Y8wHVlGz#917y!TwD{wmu_hL^v zGk%@IAj>!)$Sa!T{Oqh8M$SjFRZ*%!WTlEQW7=39p3{z4o&nFho-jZ?wC7sFXo@D| zeb$QH-@>U&yQ11?saEf0idLgiNLpeg^*|>ek)6x{Bpw^~I!7A;i~}Mu=Hv6{We)qP zb63CzSw;0;(QX%+DXF8YP5!KqDhU)Z>{maSV+u|Nw%wk|8*y+C zr*Y4!R}II)CC7Ux%cs7)RMJ+{Pja!fzKh!MJvtrIsElPv>={Tc#(k^^Jn`k`G{;X@ zK%Kp}PDbx2zE*~`eQLa3;ieJ15Ye|3)e>a}2m>t|oTh)j&a%3GdvfD*%WF@r+^cH} z74xs7s->*9R#7w4dZf)SslFl<@;4uLH(-H}8mk=W!DJtFo$uGwdn#%x6?KPB*4}Go zq@*G`u_H|u$j|bbxdVC0P(kA#85z^~{Yb|qwzl@J0<%~HwG^G|J2%xXhKjx`O??uW zBK4sa3erU2w;>LBC){=(c*l)oO{N3lLEtMBroh8DU zO3u+3(m;|fH*y<*PBXycZaLC(^<12cSz#kdBV(q&Ug+W$-;|>pk5^Y;dSZ&6;SD{a zqM{UvLhe~)i+>=I`B|SorUAeQPyIaMJ~?i9vdRLzJ@2{&(JC)PS*SXO?3QbtBt%Ak z1{|?Fa#&+<^Nv9I9y{w}9!5(cjl!Ep+kVJ8n9#KEOMN-E(Ne|htxHI(q{Ob?lvrr87jC05#Z|+r)S0DoH7oe2gd5fr@DAp zlGRg1G&9?&?h*_t%49i>hd2YXFwQ^#>t=iq*o?%1K%dD}3gvW43U;owUTw-*ndB2? zPUcMTSbdKeU@`U@@_k^O=fD^Upl`A(fUC70d+36uvI@y+>cnoZH@HOYxlbb}0}bDt zco{g>WF1C)Mi#@*xFrX@C3HPUR}8Axd6G#L+3D6u@FhZ|29=KoJ+YCk+Tt7T|?DI*c#*+JwT*gG?TSYw~xNK1tbrav$aE~B_XX;2cb+?4XqK`fHd0MW(@ z?PNLQgV^(q+T3hSiO0h>KR?MGuD(+K^y(MuI{EJ4DN?2rNN$mDL$?nlO!!N_Y5?i)wF`YCj$ zNxn#>eO>hXppDwsQAuPL+suG_De1(ECeU{NVpI%cAY}8L>3p2XB$<)t0^`GUe^eM^ zZjb^ghQ)8Fg(%0VSmmObWKsOchy`17@PRSzBZ4#g=?HqP24^vmybx=4?4M*V8?>u_ znCR%~{{WP0CYCwNZGv1A>5P#mKh@{IZ=CAiV2U@F#t6TA8~Y}8?e4Ud16%3(UOPOs z-|()v)s&PFTxutk%aII90xV}E z4u7O@a1S~2$kZIXsM}MPxcAa6PTkYHrFwFnDmc~Xo?cYt}WaPUL znJ8V@;Md2(Rz|hk1Y7j$qwZEX>S&$dxNZci$eTz6=ZEYF=NR#>`f4aJ2aE~RC292) z&H5_C6G8I+tIgID_d03n;CX7Ji!$RIursrEGllWW zT|;c8yQMNli~K;Ja|pve&V9H%1>w~3$i_8`1VxN2l#3?O^qwsxt&mYrbp>ofjyQho?O*;iYDpqnjtw5I45nW?HGWMl%Iv&1{#_sGT@ zE0c`pUW!bk;av?w`S$oqmpqzrRP~oTZNjvA1tSu!*N6hkBlq@?%Y%S?_VcX0Zkf$5 z=D$@+B-#?{$f#fNk4}`-b4>)NGR&x?f&eFXW#{$hSoytH99f!VvNZPmmrTNF1OxP< z*Ko4+bh5ONNoA~>W}ZrVovjfhVfGM9p>4pZ z-B&?v6*5#?=Y-2Dh?@lz?at-SJL4RE!PlMYxN*;tLj$xl-rVqpMQo|<-7ow?s-gpyD{{T;^tLv#MC~1t2*m#)^;}fU^x|4-v$Oo}K!1&jV z#l~h-h8Tfh7H!)Nj^pI^M2)q|d31>C8$=Q$m(z2bIm*-QL>qf|+E3qu;~3U{W)yRE zSv3b1v^0vk{>st9_kF^i)E5eB!&PmPV^ce@mC2A9Hy`P7_V1iyck8YAABQWP*qY|O zzk2(g)UOqjbgw;jSTEKmrvt6ED{ zfoiAe%E;uSlQ2|0VwqYNZpJZ;V4QM3*#iLTzv(S6{lPoc+1uR>z@-J(sB7u!s%E&` z;DlwwjZX^!Ze!Nq(24I>v$^XzM8Igk-zI89hb^ z1TY7Ko^!^Y#l>UjJo#LEdVHyCZ-lKK8`SGpQTRiAt)^AjB9T2q86!J{o3Q>}$NJu5(I=C!mhPZ$m-P{v#xa!$&)RZ*z(jb5T09lT%n192u2Lp4Sa(V6kFBsA@yh`De zT6{ieb4QJp!@)X7Dwm{sHR~x}OV#L9ER{{NIb>rRu0}TaCvgf{c=Mh$uhhDZK3*`! zjQO_oMI+T0bsy5JWgV8X;ac?)%p4a=g&SgS?6^GbJdFNa4*DB|k~Yh`=K6jDPUQwOgWvjfp_!QvO>vV@b5EreYXFO0 z8u8Oj65K3TsVWvzwZ9V+!q{H~5Jq=*{YIk_W0nlq@w$(w{>UKHtf`$D*WCvl@Y-wd z_UcQv{39}zjoT+_S0f5I83gA&zIADHr^Fw)-QN~IK2=^Qrp;$kD7HgiYP%y<%BH0w zDmTo4@c3`YKpsKxb>;fcRmjebxZW8BuDjDi{PtYpD`lN}kE<KP1`(s@gReY*gP0Oc|%W}doQxKgUEDaO?eQBF$9oPwMJP5|KJkJM{#E9n&$ zd~di)I8w@XAE)By)Y8{UXlZQ{I!;*2>})nl0C)^RKH@RsI?d|%?9J-KCFC`YXsb2m z1qigQw`=L!4aBuHc53Q++GIfs`UJzVuy%q53jx9Z0JgnL`o>Jza=ez?fDJS>-yf2x z1R@D-*2*?^nwkhDT+G~HoMQ+)9@*pVoNLd+)p~<+jy6$ih1Zm9pg5sEyQQa^mTEd% zI>`j4G)9DF<5mQ_ZXA*hI493NhP@tCj{rHtzrkWY-1ko`Nj^Zm?}WPcB?>Sjysff9V~P-G}K1w)D%$jCd5+2sReLJ-HtGRbB!sH zfgG`Fa5|Z|{{ZoJv_kT_T5ndWiCLqPuv}D>^ciw+aD9Y;7!MdY;QVumA5iNrKe;R) zEjEY_%jZ~iji;yVvE1olubQAbiiuJZP%k`f?Sb>-9BXE5yli-UNg7Pu>x=J0)6q+| z7L^fo#TQjpR@M)~+HNHTlEjQin;>o4$MWvRdk^1C%hjWez`$H1`Nf`DTE- zUDalSvS{~u5#S>@&R;Fgdv3?uNcA3_7D05`R%RXBvuJVu04hXwEFk(?p-)z@Ry^`Y z9Bf!ZtG))t;tLG6RQz*;@ul#bV~M8hg&rEQtons`h|2W!HIM?>DfqJ!KevIn9l#Os z#(QgN-9@d4%`hgtJYbr<&0Cux0vb4T!P=@B!kNGG%Qxa z{{Y@GrCF!Xveo@P)6i2@!zgAEa?vYAuv4dUP~;Pn^Zx*SHPY}j;WX+6*6#MW8$}hi z+FH3CUZ~2Z1F}G>vJ;c+JBDyn_au9cmWmb^J>+gc+$*+^-t4h`{_6#{^;>DBrnXea z0d|Nz8!VA1Cxv`r@!LNj_wIGEI*Wr{8=JB3U&yX=T;Lqu6~&97YC7hQ8|A9yOGQPr zv5B3PjEjIA_YMeb0nP{~&W9IG8AQuI^{}H`c2NC=6%VRDgV?SyPHUE>>*Ms}QBNyG zjHBE@+xfW%AakLfxtTjm8b=@Pt+?eXSlhCa7muaTTj`@(UZ$z4mN>RM8iJyuCne=k zoy;-;_zD5mP7Y%+-S##Et-BN3emCT%TLt;rKpvg{03-)s>if4$)ub(0wW}BrQ>`?< z>SR;wY{xO&&bylF8ic9{{TclITWt`Z%`eDqfGD^bm7qZZ_W1Z7KTBbV5N2IXlI}Xw^nH*!1TjM&r@Z*S( z59_iE812J3_ga&s@02sztnyd%-5m^12>_vPT=J=%pd5}o`Rp}bp^`D+Z0-cKxDB~R z41fOsar+Sj5EHqRXI$oiMIU|;OsM<)8*yIIl_EEHLKOk|=y{Fr;ax%04;BCUqY5xE{ z64pV|0yCGS{{V&B>8^A&k$xjAl?Hi$k~Rbe;A1&BJbqv42Urk77#s_Q->6fNEGHvDsQFONRw%YX?Ka zZoNfwvs`Gdwnv;9gQA9sIB#;}8~7t2V2lj-(p^s*r{T#I?Q5)?=g~I6YSSvB2=3Ad zX{?l1>eDMswG^i^%%Jw$<2e0%<5_rZeAysKKx3?rTKV-xA+)aItLob({{U4h=q;u) z!ey9}c8$0v?ci~aes#B*t+V)>V! zkfpPqI(rN0ew8%eX<;n}hVJzL06!Ete)h#kvsE_td1Cu*jk|4x?`c1~b9#3f3bDuY zQs#!8xKD`x00-0b)UrucSt`njCfU0?Z5iB0^62m^z+uc8Nst>wdo zU2Te@*>#Faw}vQY5N(LYRf_^iz~JDY=^UK*=UUmgtd4AHgMict+^%Ek#iNz4?NicM zP*9r7397Z!Kc?})GYzkRKncR*BPYnn^NnP6AE@!z40L(yHrs?)dVIh4H40iT-_X{3 zU6wOX5umpz6Ufn^A;)sSx4He?5WTU+Io3SAW){?>{{STLg~wsarxyF2Rb3PmlEAEr zXAPE$5S%Ni$L3rQ1Gav3=X(DDS;vs$=wrWl>K>QJmyKo#(=^43mx2fx6bP76+Sv-(E5n44k0#WMQ?Wr)e8d^El zc4%HCQp*c4U<(ikR|6Q&+rTb zkt^<|nb@sE5rP*Tuts?J<4t9B>`aWe&n~N>RDHb_K)si4iRsxocd5UK+hi*Vr-3DQ z8>cJ@CD;>%VaK_?Iq%MkA|6+>Vm~wj>L&Xy(4VLMB~{ea5?0e*t?wXE%&e-<5I>cq zBw%;JPPLYqNuNQh&mfceklP<{nV^u4=l0X1%@17L- z)~*jh!D0JEKleM^uWtJzmu!kt6Ieur#v!CBCEz-i3&0U2K>I>M{dWBWw1Pp~ zlnDj1ruwa>t-O?g8D&T&4i%8@@VVRuK?R$fk&X_JjRpoA46`-m-RR!_-pjFZ-dy5O zJbp>d^vdDVeI+F&Br~n@TA2+nS*G{;W0X`=`BWCq$;)=tG5VH1y-_v6pZN{%`z7V` z5nYNf>kq5dQC%vn)|(T=o|Iu`T6L0|KuIjy)SR3E065QVajgEZaxydBm5cuXWxx&Y z_UDUQ4=uY1tI<(hua@{Ow^;4%S2+iKs6zpqV3zEl5J~Ujoi^#(&k2W@U+L@JP`IAS zjn$UR6|wOhm z{Yg_x3{`^Kj?((lVN8&g%902577lPQI3FPBGeGE@s!sd<1u$of@_%t>a zt(D7|jPl9hQGcW^QZP+64cZVq&pQR$LLAu_#(;H=^qBihD( zt6cp)`k~jeLrHS5$44Dhhji@;c_WFpp56ZdE(ee?w>t8y$jOFtINC65k~ZJa@%pHi zCIVV6!qfDfL3+F2lA`(2%VVRdl1i9rkIVU*;4Cg(Lotx@B&mFO}w8lm8 zz4qNq5#Gws{W<6kudhmrm0hAbc;;61CW!MPlRO?hryRE@+wrOTmRxp6AKIAXZXb7U z#ZwkQcckK|>8_Kq-6`7JUmaXqSMhNyp0TK7z(mGC1h?zwSV!s%C*?zgVvxoN(sZ4# zlIMB^!<>61y}rk>f;^l3^VmPsW^Hp%G>X9p(%N$fkR@~a7GGCX}Coey_0j0Z9{{YJW0HO@H zTP=grojD~BM^W0Y*6L$am@&LlE0Tn6E$zo5_)&2nhlfuS*GxZ0j`iV$; z9c>Tc6Kv+HH1!>DSU@YdjLM#t|!z2N+N~!G!Nx!0Il~#P}wbA8x*tD+zoXk%KN84 zjro89n5Q5P4svnljbh1!*5ye!^BE1A-{avw8>ol(93@B98ViNPrzW`crK+wejG%h7 zlem^oZWsVa?lZ^(8OY9>!^g+U)%K>4T4+|=e^kSy2Y%^M(>*2CuuTAlmaZ$(!RsWk zRk5gqu^Y^J?oJ0B02Dy$znpmII{DOnbEvyF{Vj#Q6$frN=D9#D4&`4(^zNqDe5k(M z`kwqcpAQI%;ZL7cR4yWBC*DgH%Le?d$KTq0OFU5INz-Hn&T!HkKIOc6`U~AIT)+oA zC#8bEItz3XUFvO6yYAm7pr}E|2o(6p@-lcJ>&5i1p+`^CGo5}NT{xaEg;%?m6-D(S z>uS1o+fQ!lD(h1%Qm?NBySAf7$7^SRyOdxq4l$B7=|0VmEfNT}WIQ=xv4^y6Y>ogih!?^Z7=uCCq)k`Y@&!nau{{WdJ&trmcM^u3T5w2vZ5M9Vy+@PiiY6l zJZ=4sG2dC8Q>o_3ibP?$>~>v`{{VYY;;AV*H|lk&-yJf+m|d;2rz}i*bT*=cS||sHpoJKUDP!9S~@_~@zPD|5mJ3$<}d?=1aP_B zc|4Kr*PZ&mtmnf&g2tAJ?)KY&c_{3HH^3w#%s2c}mHJDkZx?%*xOEyDnDHERvPw*{ z#Js4<;HGi`JdS@|eMjm40NNBo3N39UhK=k}2y@7{YwLf+_VYVLPx8Y$u0 zph`)c76)!JaHKDO4sxT6>*kqqJECVCnVsW!mhc;~0sgnKuH-71BX|AQ+4Wl%TeU@H z1-7b*=Je5wq^yAxETwY&xCOJH1Lt0|ugMN(AS`XL?WXuXwiTO&7XTp$?dx@ZnG-ZUpzKJ!yV8>gcEngfx0o+IFbu>_Mo9M5QAk-zh5rEcagmT!ze3!ffuwq7 zW@NeE8I(}fwGPQ=JyBKDT&$JLaH6M*g(RlSNRxuZA1a@hAd&Of@viI8M-ESn*3qCZ zdQ^?>K0vuyFE@HoI;8SY#R-iGiSS=I{$msQk8&I1S(3{;+xAEv8yMrb+-{6zXc}9} zb@azj)l}SGp5+F}agwAEK^V>mIwW6CK@&uoW5jo;+P%>=P+U5m z{albqJsrS7Bza)c0PQMZoM06V+XEjbLi&TJGFc;JM@Gl_qYy)NEQi#0)xIrK+U=KR zzJ46~^U6SJ83sWhbBqtl4{$P0Ilf0&&3l9f2eP}7vG-TUb4xt?l)LF}o4VWjw$W8t zX|1ZMp1ET_o3z3hm}C%trTxF(I&TlEO%sEj)2QrE8zxy3UI}2YW%>BX89C428jh>bvGON=Z1M&comNeMCBf9d2t(aXb+bb=)HP!z46(3$ zgq5Hq5ZiKcr?4x@Z0E*x<@%NuHY_j9Gd0W}ox$LP@__xfSxc#Uay#f*Yg#p$P(4pf z@7=cnSTH!r0~qi=`t?}Yt%;41hfpAHI0WxVG9MweT~qYd{-V0N$y-TTJaaVf6s|){ z9>KvY$XpERSOD^r_#t0-0G3C`8wK}!~pUDunv0iH}cF|7Z zC~4{Aas+V(&!z_}oDMkvW3lh{)$yg6GgPf1=ehJ&Y*(@2L|1pGf}VIFm^-pWq9DLC zb{QVrWMCX{d+S18pyI&E8c7^Gus*6?iw%D5x_=u*)n7@ve&uMZtGgiS{-i)g>UhkF zuqFs>{(gDKe4YnwAaiGEx-!4EkO2y00D#1Uy!|?Y3a?ruuB^Ayi5aAhRRcRn+*o%V z&OSI9=S^W}^(nmDvbnbZ0G*P?7*GA?>ilU4)>*091-72WMM-J(aMH)HG=-Ntf?yB{ z8f|loQkX@qPj~EszmGVyMK92Oxui zz~_xArIG4u2x#=QVd%7Fv+3mpqh!=~DoSW1aZxJAD3jA5d)INnAaQ_u@^zmR3|gI_ zon4iP7eJxQ*G`&bnhLq-B>|jDh})xV@(Q6@ z)FgCnZ075PW`S!Jb*y)4$to!*X8b%1zSY3=lL|6bK6{_PJ+rJ~k{Wa{@_|OqlSpIf zy;V<6*VUvJTb$Pk_RSqdI})ydfzC2P&hH2Bt2;2dZ8A6(ReyyiJ0ly!EBxJT{)6lL zJhk;ymn&s;1!X{XMw(2DfI_Y~;4ou?z4;$Ha|bFccbJDJ&I;J5`24?HL5F4hJ7PYolQ@JjsJT=pvHObrg~MhYVIrRb5f3xWGvDHb!z; zP#la9ah#4vo;9_U(i>xPsOMPOBrN{`P%AB#yRDk4s-|jr<3hCz3p6Nz6@A3zj(?>2 z{q?^qu|Ch?_^k(?d(tp=stZ{3FIRMR_LgeODujwDV@9TJq{!s2=Wq@bk}x^II){)td}w&hJxH5~0d%2z`hFl7J^_zK6}{-6Rz2+`bS zkTy7)NvkwDJyM`ReJI()}<2IqLBxJg}ELoN1#s6?CNL+xOT?Q`4{mmsg-j>ldv>|#iRWoEDk zWiELQG`UZ7sE)7{?d`#t)4&@#4$j_C3B3B`zOyT13zFFQ_YS zuB&WHY2=--lu&l9(uaEG{X$6yh$R zRf?jp*w}$m9j`C1&T^ z2P4~@{FAI)w$5y5Ka%NR3q`fypG!&(w~7cZw)I)+Rb!7a#y^;?*d5)t#zuMg;~L28 z{YkTBjZY2~1t+EHx0+Lr~^SZ)o>MpnyF>u1~zNz{l^-fjln|v@^^>I;*htK7m#40C|u) zwf2UxOQkJiSHqq2LWO-Ijbavq5HWp+;-`<_Wto~}T%^C8w!q%Ya zPOP*^Z=p+#whB9&H}mPh46em-`FyB6ckFrXp-t7Y8pko@x=zEoWrZ10)Y{~Zb+u5( zRWi)!D=7=T)Zk#B%iNAVxYwEg0P62JJ8{3fxIV6p>WBY+rh+%7puaxf}p9 zhaJ0PBVK=~<Ok^=4;6)H1DF=W%Wc+v4 zpVYId^5n+pfv~T>@F62-8@y*H2{d*w%v+yq@tW2F_1|7-GSh1V_~yU zI(g&WPuqp)4Jk3yeS9h@-S73QZ-$|_#_)R-;ea4KZJc@M0`#X9DUmg^X)*)2uH)GTNSf`hbl0HzU3%&}J-!?ITWMn%Sz@gA zNMd4fA`kE!0uDz9oq2AjfhKgmFlmm4nj^P2x5{f{O;9MhFaH41>(Iv~ckw?}ojqts zDu|#EcQ$*mA7hO7)Z_g-Ga=MYr;lUXbVta0E|q<5(AHDPv{g3{vBwyRw=8H*3HR;o zj(P3#u7A{70ue4g8`|+?o=3LD1dX>&n{QIn*U{Sl02kBr^zR7V(X5+R1q1>>KJCP0 z9RC2^Yu1jVAi<9=LvAgyDv0*^N<9!&T`4OjLrf!j7U24))7W<85y;L#!2bR zJ-#GtEDhL$z59Zy9zq zexY>*Z&J+kzOjHPj}j^cag&gz$tX7sD`@T( z+Q*LESY*Vb3eu6tJ@Q5eA8h&3cy5*QmKGlj5(!l@S1C!?cRHx$rKflvqF7m2Iz9}b z{{YfE;EZ7S@^tH^JU3z7AihF{1e7%i9d+LAd!vRn+QOb*W>xmw3_rAy^1!Em-#FR8RiG>x?)0{{WwHvx(MqI+vYE<=tnB7uao-mpDC zqB9CBNM@dtJr!JuOC<6KiDUFxSxFnzo(?hb$Q)@5JUot{B$DEJ1Kp^OcfWLSK|7_V zQS}6!JwrV-&Yz?Sfk%>bT)tqE#=>M`k>jWW_fc@ zM|7rDQ6Z9ei=1~XS37{mY_Iuh@MN561fB_dEcpjM!{jT)6F&QPe`1bnH=7R!wcvb<5m z^z3;Fv;)1Ea0UPX=Olgfmr%r=^J&s_8trI~XajZm=k+_KE^&1=%Bs%PS9;SX;+0LB zi7=p?lmi18ARgTF<4^RQevcgUL75J-9c`+O{C{)@i(V|W7g&0FlJ^b7QB=kdPfsLX zvTr1UFisBMG4flU+V%Z2>Wr*+Xu@}e0NRZmyKn8n6!6@O*DXR)YaEH(vhJLDm5dvk-<8#Y;XELTTs>@0h3x$Jay9Bhsi6df^I!qs?^iD|1>`3$DCYzH5mkbY7I z21hx`<0o3(9}U>hHaTPhNWctrY&~}Fic3QTho0-r!=&%jRkoQaX15iPIEE=@0g#r+ z%JZD(&m4~0cPpyq$18DUlg4v~pjrT-+IK$8=V>_F4<4zN~#ao;IL*DQ-+(dLj^ZKHNUkK75hL z*O8MS8b=pt{o&@%^h^hDNykfMxAiSD)KVo?zBYtJjS)eyaqT$=hZ~P>-gB(}r_u4| zgp7vUI6INI^j34bD+^!K`J|}=?NnoFSn<{;ms`pV6jm30F&`mGDo~0O``{I3yr7GBo_V0kaUc)hiop8Kl?*`kJBdc&mD)XCHH zmtYM0N}NU<~o{U=_Y50-u>wmQxAsaOHa_#&vt_EU0Eyzst7|1lt4C|vjBMq z0FTq0>e*jUWCIWqk2$UooY8)P1^q?nYHRyh>8m63YZh-==Ts$J{1x&5{{S}5yFU&1 z&TJVTX1lip(p}Yh+r2GK^pl}Ff2OSy-6`($mkWZk$0Alqm!yb}0Ds@H+JO^2u}RF52Wb&XLXy2tCU^A{hRC*Thn%fX4T;xytB<7zh1$R4%m ziWHjq_@}3;^nV)Cf)V!u0R^x@Il(y@@z`r;qemm&=Hy00Tiomo`Yv$*U0PSqRco&` zbfOAYjshZ(p$0`w$CID&V1fs26QpKjNbHE`BsGo!fR06ON2pg4YG|ZOYPebKbl9qD zOFceH`g^b=z&l6-_Q!7ezFus2jPWF8{x!O#f}`CosZ%r+7O0Gt_lV9+gEAEW5Wzv< z^NezM$dZFM>0Y7gkaT!W4W+g0n` zTc;N2%x8^!Q>#y?KDL~aH(7O~D}PG#lvTE>n_WFsEYQYejh;j4P3`{xmfwE|fzCT? zGZIL$>R4N)tlF;s0Llub#2O_WQrrcmYB*zRYD8$HmQPYb=WNBumDeP2+w^#(mK`_b7YUG4+X5DPb16?@m`(& zQ5zIjZ)T(;)}K!;eMwg<$x$@4>|=z8PG*;A95HNwGC?O8BaSo&GB!wKfO$_9zvb+R z(A%IXFsb^EOJ7PSPm5)~MYe+g%z1FbCkc)Y0m&yq&v|5R!+)6S4!yP?@UP+QfbCsl zU0ZZYYoLnO;bV~+5&?~HcLl%$-GB#t4Efif${6I^j<0L~06QxP>}42Cqw*&U^pG~c2AXc9H!?u%IBg5P(tR^H=|oI_G) zF~HJ-$lJhR`-k;sKO=#pr!8ZIaJ&|tXx{$-(KQ^T5n(y4G7Hs!3aHYdqS#n5MH8?* zjpLLg3e~c(9{u6&&sx zKyC>>F`ZgGac}U204*MAA68wc`m5fQs(LEO?h)E)QKJ&Tb|_fF;{?Bu*~!O!z4NT; zvE7*vlf(=W3*24WIhZkx)GqzglczfCrzflaA8@2gWb0AsMm+B}7~1We|rX z7ElgY;3>x$BLe_xd|$A;z-&fh{{T2Gy@BmYB<7DJWQ6I89{-7OHI;}*;R3ZkeX9alI@b5?jaP%kX_1w zgPga1G>2Wq@v)4=ZxEn8Y>p7(I-W^eXP_++^zDw^#-hU`%$}iwI*?mUSe@w0$trSJBm48DabR)J zemlr+JTzF-*q(Rqe4!RK+q-hA`pVhVK9x1Lj<{3M03wo}l?XC8_Y;QBG2f0)Zro_O z?~&6HM@x>!x8PA)Xiz9a>HR&f{nYcNP36{l2n$prN(@qC^GX=zcTbvZ zklv>#mmZIh3P1SnH}alU;w>IxR;1|4D#KAaLx;MWnPkw!7l}S2@?PRXt16bp1nnipvc|)+r+^Sn@M^t0No|uh`>ikTLq6 zb?oHHXpym&l3lj0!q8-BB7sb67e?Bps=3wMDJsQFxr=u$q~L+L5tI6Hz#p!)CF$?S zbr*;Hdmy=kg(BPam#F$83ctkAehe@WnAR<(Mnks*fOmn&+ZGN6o6}cR z(+HgwAP2K==iW#4W1Nn}@uuU)!I|uu2Izc8a9w2ldMNu|SdG!@uzObv81=0p+^#ZQ z?Uc=HrYQ@?V;h4mGFiaJBqzDf4m%AGCQ#s9^IrXxogs9%yV0@7*lMn$mvvHQv2`6F_}<==%fE z*=p@iM1j@?oPx4W0mkJ_WD&<1$369l(|Vk^Fh`6{N2`yXcTR}gTb7pe1S%4*SK#QEIzU)dfm6kjSiihDFI%IOiM&85&C~>FF~k*A#6?uFDU>cICwxb?l7#;fA@T6$i!tBR$qmkUj%zBuwCi+85#Ll82+{T*kYsPTOi{)4w$=D5pjueQFxf@z_782%OU?NvPPQgSjd00&9*Z>VC*mH6^N z=;Wijjo+XjD%MD_HcW1xzFvNwQ$cL$2yN6<@gJGPyta11%%jDei-%TN~0EAg0j zVHSNa*U<+?+m)6pzPfeQ!m_bv>NqZzh*$m^mkJVD8A`Nkjxf16&O48tL@;B|3#OVs zJZDI=&$#wMXg-ORX6Ty))>}Qw;^|#m6bn#d#!(5~wON4~Iri@C2m}uL!-J*jfJD(= zYwn2K76?=9oiRaotp5Oat7vDXrhG`V>|F0B@{Y$Nwn@hv_ts}#=#x9D2`6QTxu6q$ zsb3hmj1A>97w84mVWfhBvZh*{=#xoR2vkVadz2I(nDLDH@2&2x$@*S{jNuq{>L-C* z5{|~T+@N2m7Cxcq+Z`oMR95Mto>hh%$jAiaEBUZ8Fse^E`{Rv9A6v*UoXn;~E&vb@}#T9IjtJF;)BP$x50N`Zz=f1Px`rbJ)Kn77e_Qp6o4E!By7j>IY@P9C+P4 z8#w8qeyr(m zOqoxx4xVdz+kW2)Ad5@3?!s048GOetyMAY!k;i z`2GfDt#fi+Cz#N2+i*L3CPOIF*Y;c8l3I1AwA(IL$kka{oz%qX+c@O&!NKPzI`=se z<4V@Z=^Lb71OEUBz>s!d)jd~N_*<2R-EgdyFosXT%2+P)rwXjYza9=a=baB1!G_-# zk*?gkk$%65q=boD*LU4!$^MkDUUePDrr~+NL3sQeMxunq##i?e8X!+20CE1B^Vxq| z;X5nCmA5Ds935U!+P%@ZmrrF=OZ2`jkFQ%@BrA7kRxg$;w2Q_;91_5uS0@DV=Q{HK z(BIS>;o&sIM9j-eB_I|<8I1)K;G7vH=8vC&|N5_lYJdhEK^M+oBe3W%JRH|2nQcL9cSZY zPZ^sIXZVfO1r4sd`ye!jc1W*Qs^4Ez0O{s50g6&f6YdD$0N=}8Fk_H&#y@kNcVIsr zFB>8=S3$fQJ@3&Cl3TQ_p-s`0wo7b_4c?wgXyjSira+~JS8mSvR>pFF>hqmF(>mi> zCWvDcH`x39E4y*J9@@aWC_azN)brER&v=4(f%RiaA_}yio@FiwB$dkJ&OjRTy>sc8^t}8` zJw&ol>)NV^9PX{E_9>@t`Vln%q!1{%Qq!V_s1d3!s6p-+@xp*xgMhqo&$#->q;#I8 zJQz^CM=6XO0?yoz#ab$JN%Tawj*Ps}TB&1-z2%upx;zk!LN@kkJhHLx;1R~WMm*;Z z4+-0g=ha594)yR*Je@ssw#8jlTTxRzKd?3irO;~C zM@{kUxE8qGqRJnvGE-JP)|N|^o)td5IxGqjwDu~A5qWq10M2 z$Rq<=uKxh%WiAdIF~oc%zgGQft-Rj)ktVBx7#3BUX(geFRf6(k8C?6fckTcOUZ#AW zksCphR=eaL5QB09w~*4M>C1q+RzW4U{92uvDjES!0QA^o`_4gA$JpZ|O2O1H9!ZW+ z7&`!@{{ZE=S`(zFmFJe+@>LTQ`(_ImRFM8)Tz`ZD2ko6R(J`{~B_|RR__osEM;^PS zB$hrNX&2PeRqmGLw*ZkgV`v0?8pT_RLx{O8^&2IEn z^>SDvmU=pC$dw?OWROW162&1n<8E@C@t!e_X3NwV%{Z-q0(K6)_wQcGm$YgmDtP*u zs*18YTZ0sh%d}2FSpnQm8N8_jxa>$6_y0x@Z8iPZ8Q=s5J9JNRub{fa z6@0Z1H5$O%)-sG;8wBGVj1?UDJPzkuGvE+0@|txytQOyM&i=@c%57IYQ|N$H1IYv(!2bYAUO^lmojsBD1e4RAEAjsTglO4f)OD+do~}Q`)<$55bM98$ ztW@qS0O0I6&mFX;OJbNcf%;ASuE&0v9BTGQH62?`d!<@iwbInotP8XHn+OY#TmF-{ z@tkvwA=UnuYuaMS%sOATk~Tid&gwzNgKCoh0IqLVh;6hs^|>UH!a%5h#c%vbBraHk z&tZ;u$<|*}!s&5hj2Rf$0yYP@9?E2tmRTDhiw)+Z)Rvsn7`B+%CWQe}#y==LdHqhb z`fQ^fJt4HuPTWvJX4vJ9`_{Wu*L6+2i775+#YH)tQPTw^B$B)UFh&3b&3g@I%SR6U7^kOKD3JK%ilLlPq#EE(Ut zdv^id{gs;SwhCWObkTZBrFzT#RkmqVQ@~1^3A6|G9DDd9;}{{X|CpaYMezn`6Ko|`05H|(+M6fL*4_xzO$ z?~t!9YX!Pl%RM-mol>LG$=#8Fe_l>=z}4`2qz-Xp57tKHaYMPk(Jwnea#-Gao=O^O zsHv#nc8~}fl*X+5WGPU8G2@-5w{CQ`H@s^HDrGm&8m!cv}{!`NOg6s;g|6W zYA-S&AIRZAR>=;01;`oaw;*8Zae9n~o2JLD^X{!|19Q4}+iPks6;VB^n<%l6Qz5qS zzV!s>dF60;7|wK71OBNU3i1_pxK}IXzWF2T5Rc80E%evVV6Yxo0)# zt_r&9i@hC+iYN_S;$X~?yazd(^{6?9}3#Lq6*9Gb!slhcqj1uRp?1hLe`6H&*p8v#p}+NAN4aoG1iZ5UuaSDAw? z)TGFD1ZhWik*TGbVOXggI+$lezj++E@3pW`eB+;;3Am4A#;FTk+;<778ZTgg7Var( z3~;4I#)V>Knoz?L3=l!U_JC9nd;XfgNP(F%n4pr*_d|WTQx;2sp&fgx3R|2oR$ASu zSgDCAR#66DdC9>f@G?|n`R}B9!zS@Wp6@K~Zq5^i)Iv zXY`69N4E#qIVW~JbIx_Gk)gM+C%4LR*w0|p;YbGW)YP*?+x8V~k`E zkDTd{okxJ zwboy-`AhPGzNAv!;50Ug9-@-914}F=O00^1D=|0$fzNZ#BO1{X64M#j`uczE2!Iaj zG}d%<^YsNS#=7l)lA#(#8_FSw9C85uftCev&h8Ggav_r);jP6JUwf+m0MavE=$O_^ zCBLK~s*}~KuD3xXWcqo9VGGJELpfw5k`IzT+STgVY-=^i6;}0rg4MN@(T27Pw~F&| zt(uw&pk#~Hjibzj4|dU#ykPzH;Tek?%QN(HY!6SxSG{r?+9z~k%~zoQ;VR;8%B1OTnl(LDU9}|>DQ7DiVgtDDV1O3DW-P=GNu`!lrt7>rwVr9 z;~(PVIyX+ipB?gBh+!LhD;EZlXhWzi7Mo*0(am(a%p_PUqLMP^7jhUlAYdQ|fIAHb zD;Pl^+S=bce~Z18b^uPxosXo&1!Q$IgsY@&)b#-W04+}8{f`XT^UHSAnbHhbGbjt# zZN=*Ee#+(butbq`y$z0fo3%wfsu&}POk#>MPDbE)CntapfKGMU{Vy9Ms2uMTQP2f* zxc5M5Anq6RmwW5OFZ;b{qm87ET1mozAsOJYQbysDa5MgTkEd~Ed6>F7bDLiWiwI@f zwjmqz^{S%20VQ2T@m@CoTCCtwe+x)3|kU{cv)SfY{Z z{{R;oGfvLrff3IDsL3Dg=7nY$kOQQ&cn4-=ul z$GOjp7Zd1)THE1crRwXyP<0)3wsoPYr;2uk;GQtPh6YuT=iQum_~dJ1EH{T+L24^$ zGZM=Uo26~l@mwOhnuYaD6Ud+}AQBt`NA(a?k347@jL39Bn3mr*?jD`d9OC`QIz zq^P7yu@z<3c9tWGW^B{5V+`%V+Hf)mI4!Lx@n3QXzlacj6ekw9?+a927u7OeW`;X$ z=C+z7T#9KQDZ$AJyN$!M5;mNjWJS<28q9kZ-6`|~ZP_C5r zdOG^b+bFDAo?lKTP-TIbpkR4yu^^N4RqhtZSt=QXX6&gX(ds0to~eie-EW2mOT zO?7qKs2ElHv_8MCu{>!a^S2`hAD9dik_U1;@V+3z#FNI%rpZ;P^GCJ%BZ(~7DAH-6 zj;chc4^vE&b1ZSFZT?@m1SlZmxdVW7uT%Pm6gYSi0$sO$`+6ZgkJJmBYn5djm9?~V z^wnajIHYF*Vi|{w`hmNZZJ+>uU1sTY&M`Dm9Hyk6`3Y0j)mP~%D{B*{YU$QcV)fcG zB#{3A(Z{ql0RI5w>iIuN<>q5PI45*}0{;N2S@Kw>@SPna)Ya3+(lk*~($GmF$4-n8 zI0G4xiZ^ViY;&GjO&rjJ%a7^282^hOKqaLOK((+NFC(DFgz-sInD=; zHAk_JiSa$!MNmH~trkcNFx+}d^CcZa$#8*gam%(eWE)Ilxg(r?&*`SHF`W}y#*KHw zy_Yexb^0Xb$LZyURTLFTQ(tnYm0m=peTamSy&re93}fBS;emmr<9p z?xs{%bpHTH)>A#o32LpBsQ?9(0&}}+Zo$CdlgB!E%kNr0bJ(m6Fc43M7auP#F=P zat{EuI6UCzvEvxfewFDtSrWQd^lH^4`l*a$PNB3>iUAWtfj7HxOANMC|Xa$ zwrh;Ox-JPP85qwP`)hMIBt<8MJG8sC?_>SRmTkfzvrxUtxhbx-P}`)-7>!Cg@;rq& z_JBhF0BuZtLGg{7tKCbeCX004b+(?Ori$ZFa)Ahpq^L@W&jnlj&m#x6mYMp4p%ZB& zSH=4(yjyKs+tP}hFt4enYMsGKq+uC{$T-Lx45{yd$nr)sG9d%UiKlBjU3W^=?dZR+ z)RzWe)GJRFZpLjEoc*+cV?a6vpJXH_N78*!)D`}n&Uq-LJDu7k;a7u& z$DVtBy0j7i9LEktetROaN<-CuNNT8Ss%4VzYiL?lXMrMO!ZzUS&TxI9gAtBM=U#87 zeHPB1;`~?nr~^W_yY)r!L%T06^mnK1lC}1Z;c$Dhvr1%@Tk!4d0QzvLJdgn=kIYBg zT2N!KvGokAcdBHL4aK0?2qOo8Pb6fX+~8pAJ&uga;h4p%X@7CDEv}dN&Au2Z zqMkM@>bMJ-_kG9Z$RF#CXbh&9A105w3Fp~f29jw>>A$BPMG$-K1=flhNJ&i9$+Qs5 zo<?H5k|YV;1FMT3cT`r5<6&_QvUQoIXkxO_DnJ}R;60{+okVy zpMj(_mYc;cCU~|1?;K@RWCiCTxg2TO-;UaAW)dFT9#tOcPYu~C?%hKOd#&<%nJT0; zC-D)in80Si35*lS=f-d}Ow1g1%Iw)88r`||HL@6;ye&EO(wf87oj+r@*WahMC0s2n zL`qbGcO9XNeqEq;131a#4I=4rA(|tS;MTknMJl<(ppUFQ7t{3CN|+m{DaeS$v(aH2 zIxqzLSm2jADs$NG#x!RaWkzCbhDh(=3gJ~}s|VEU-$RwQK}QYkVwY-DK`D)!DsZly zvw#lZeZ=+})`y{-TE^GE$EpTPyq6f?*)bvdv0%FjdYNrEyM4jRt3->n+FWrP9mi?o z9Pmy7@usF^kU7vY9mnx-PvPvK+_1C7^0+x!Eb2< zi~)~~X$X2_vK|gZruzf^5ZrqvoZT~Ks-Yc83AiUw(@+Rvr(PorecXl_4cjfAamIwq zmUx5#ra`cGdK{0%QL<5`gF2GmewO=9ZMNUTqe?tG+@AS~KXGFm;6_SIO#Pse#Np$z0RKppvPPg01a8U_1ENo*g<#WP953 z!qIig)tt9oh5AiLO;aI3PbEb(De1aSg+QrIqXl{P^W0-XIGLLN0D4Z2{_3VcN@~;G zt7s@5YTKn7%#6}D?Xfct>cMUS83Z5H0jhnS&Dt|aCEB<_7W&q?RMz78{94{>3^7j( zPY9O;oy*T7IT`G6sExi0ylGpm_OI%y95_Yr#Uxa9OHBTOnlI(NV5*bOKHzvK@B8V@ znHt(`Y|~%WL3j$5j9qD_g5O(RHEl!tw+TayyaKzMy9{GGT(G`IV~_sji~j&^Dmm7< zbyHKYaa~N1)hGm*-_%X)2;1r86M!+h`c8Z6QzH;z5t1F^>DsvEOnR!C+x${qzPM1* z%U5)OLLy?jg9P;)u*!x#n*(-pf;6+F4m!P62$i-=DH2-x{+>3cqq0`k)kO_D5RoxQ zas69!+~c_?zl|yOh*>Ga)lP2%{lxbCQDcB;)bzjWkEu6Xz3PgZ>vFkKMua+}9nA7E z!k1>{aJ*n-X_z{`b8=Y*PC00C+kQP&$!ORGo@?Q0KA@#|y@~AaSp$e#QRzpu4ni>* zIl(#42UtH}WB9pHjy$LNs@Pfvm|P&MN})$9z>;lE!a>81GX zbN=q%$rcwDzglP5ZSvOCBRZvGgBrxaReeQnO98-P+njkkYa!h(c;+l*q%`*Ih(`9W zy3?mB=~k9IZ6yjat1)IKGztmhg>#Od^o0a9MDK3oAq2?8_(tHlpn%r zBB`U6yVpvy5oIt);bM`F0Sb2(Bah#jU&_599eA_ z3K=S`^p}$>I98L@ryvF$i(|hhkbY0jwR(J66HDQ+kUfyY8b<1}_0zpiXsVj;l8++$oHMq}> zx1am4I467dOtFaVKBx6>O4^!%sQP;A5X6Z+%AP|bHdwyhi~;1Z0QM?IbSUvK$ky0H z{{RkGNf;Zrq$2+Sw6{xKZdS;ilCF|bJ9_jmLZmu^PU!M*p@|K&9^~g*FtSFnv9QBh zcQloJ&2jQ4*Hm6>D{VB>!Dy8kmNKl;e>))iK?}h>{^L%E7Y#LI(5ezpd_I%M4RnG} z#hAUW`4TE;^9}vDTyx_Zmct83EMU}*$r?Pqt1Wf?RFL%CAkbd0jMO>+c@hgwQ>gdlmZ0PZeOOxUEGkG)lY5fb3MayFCCMK;pl% zoxu6?pyC(fv`eFAYzJo#_j@C@Mje4sF3o4X(bmasy56a4B$P-JI)D~c3J&%{Fi*~T z&YE-+PVcm6pb`97!$>D|p|QnlxK+tLzpE=_yM#34NZy#LVougM#=y8`EI22|o^&xv zH9^W)d0VRaR#wWJ{WZzvlH@LQqBM-GO6FrAV}&_5#xd?30sXXwTrGVvaj^A6`%N8? z_g2955?pC$k_VbIEV3bym(`bcS&^3@1v%%j<5t0IpD2PjbTsd%dw+FTI?dXk`hQnd zaP?=Io|C8=NonDZXUfQ{f?o}QLC*ws!SAP>A;iZrWW*ijBT?M@ap;GVL6XM{S@h3M z!zHG!lCqk*Ca;1)C0r^t(#e2M)@;~7Ucl?#5l5rEA1ZDbn zJJlkaQG3+(o8#6}JpPOlenFj^NmY$=*b+b|o(6P=FVH_|4|{V?kZ27%ReYnfIpvKn zi%BBx)}XR=v{t)Cx2L4AhPBdWhlpE2U|H zN_MF-Y*x=I>Pb05OM-tdjx^N0Ov&X9ruO(${{TychyMMO?5Ekf7TI;Kl7@n^qUTyL z@+9s2$&pI?!RKfMuRPWE}7Afus8Rc0Y`cqs*PH7%e8m)NlX6#RoJO2 z-j-WZ);`eU7nlW&nopagm)yHXl=Tq0+|V@2c(~mrP-@GJQ9#pHPYr$)KCY7J7Dy?;uk7c$?Q9&dN z1TT`T8#(v3Z~^}R02=j}nEoWdwmo2S6&mnH$&FESTEAZBHt1@SC7xPYAs@(MtGnJn zAcA#S#{zeh>bT|Ds5 z1WqBPSs;~H*h`P_oaZ^zQ3L^&;bRv)*qM5zkZjX(_4a z$FCOPcRCD$q!M{N^YNwnu2GI75N3&A<_`WK2OxZbF|C&11#3DGuLmu^3Y*V#}Icv-8fYI?7&R^BhRwxbRbt)8A2Q5iW=>lV&`FdI$}ofO3P z`O7cA$FdNx`#d1amrr$u(3X2;MZ%CqB4sLBBpKVgjGxWN#zr(gsnzqN#QP=$1=DK1 zK+}LDaH}9MG!U1jDeU z$Ef~_+&b0^RdMPkYg`Y3Q4@bZc0n5#J_7)Ld-&Ew7>~<|rTaGhY;2l*Rpu!F0HeB- z>X*>?U1XzMoraD#i}5PHr9~zgKryD=ZQGx3A-O&I(6f3TJ0O=PHwhmYx~})?t>Sru zeO8>d{YbQZM6=V6N~W6P{+!JM!LmGLawNwrPEOwX^FL2bo7Ll$j)?eea0Rzu?S9or z(uT0nJRlCU>CUdXTkdzRn2F?yo=ves>JQ~)jNx0J1{js&2ZP@M={OmK21ZQN;|HB1 zzK`sH=K?i1g_!!|p!%lUbBg6XRN|si%p{&;>P7&@)>D$eZQ$pSe){*{PS1^xj@V{) zf)C|s2|9dkI?6QY6zN5HWa{avs%BJaVUn0Kx=g)@T;){m#t&hSo;<(R^TmbKVatq+ zhRE-_MQNe>+B`BmjV#+jw4{}VQeN&96%~{;O-Wh&`Lf8yo-nCVk z=8kM6lEENWO`{>0@?5YW9lznmr=j&1_>Psc86zN3?YAjunBNr2^U5QK=;c*)$Xq(3 zs;extv$D%@C`6Db3`6Yy01L3%pboxCLx<856>(r=~}?Hg9! zE!T*eW|d$F7FQ?$F2oV;#uTnO(pa;X$mm)xbq;8>6H{A5M|`HY(a9jTK;;-XK9p#z zOAWaICOOAF^QR?_Ul1}TDj0$8v;`m5kEES7Lq|_~u~*!cWHkuFMFKIA>_^G<4odD{ zy7QeY^{>=fG0lqDA=cv{+f`P|@qz&>K-PU$>06zuj4wH)u*6pt-U`aM}pBMc~5)oUiOs}lHEuvb%mRK!pmW}M!ajd+^GIArH zY)`!#w1$u3_21c9gLRO;r!A%$Xi|ELn=BGYVx84D?QO@BKgIG7Zu#R(LE`@arhS%c z4ldQdqHY#>tu8IUOldDrRokhq%}}a}TWb$d2{_-3Hr#WcB%j|@!034FW`{I5d!sa3 zXLOB)x+aoJ3a2;8WME@TdV*t-03>^xfxzVJ&vm>rjWgWV{^%ZiG*;vbQ9}1cm-%m< zM|ZQ+Q?!=qh~j3Bq((y*-i@&ld*C4$@(xMQ8ua6*nc#|2?Kt)3J-w`dC0RggnqAFo ziMn6u=UQ8OqE5K1tEssPs2RaUU?Po6C>bgU#z+IV+?{CjkEAfNGDN2oR+2B)yZ$_? znJ<*XvGqNUqDkzPoga3lzf%=0Q!ODZ@)CW;&ISUILmv679bWh!F; z?Pdvta58s$cnkd}J+qyC0~_h{hVte(xRKtGh@q&i6gFW&Yol5zE^}AK7%^0p1iNw> znj%I#jB}1OSa7F%B@G%}2O!b44q#}t3hmYt^yM9Vm2}jzK~7^zeUdg=Kn06nd%q|H z86b@>(uaqKktS=4oGVFpn2%jO#rLHyduqb%(*FSM2X44utt{}&^wU8S#{)(~w)_AF zP6!zpe%ZnCtlW;Vl^0NpDhR`XWm~q`t1>{=(C(WR9YmG)T1r|fi-3&OdXrJ4IcH8s zzrh6Hk_heFUqa96f*g+ufglG~#BMDQF}k7*Q0~_wX}ZrPzGSi5j9Hh3XJ$k0a&kb8 zgO5HtajZ;;-QpSiHnU$keG{-s7|_5iDDU-`Yh})&s>fSFIs8yD#_q~8xF9nw;v0p= zeh-7J{EnVW+F2{f^xz`T8%f+!8hf?Y^>eF%ZWh_9A%r}XZ%!hN#>9}IsmkM=;~CC- z=S%fokv!3u@?2U^02{IE^J#qnyl$ZN4W^OpbrVTKYrmEq2KB-|03^rxittC=;OAb_ z7#j-(F|On95A)GhNeS6~=@@PuCtq75(L+x&0J77s|!6-og03X|1IQ>8U zM;>NF6I$TYzysrSl=5jhj|Dj2>V>gP^z}9fELPzHxRG0b;LreZwSP7-gN{eLuFtdj>JrVOg3O15%H0E3W6 zJY!kCcc)F0m*T?ELA&`Sa22pjnohl_g55kcbm9t`iCN{ALnh&c00Z|NarW)sOU(nK zW~Xc{E^#O#soGC**XQ8Bmg6Y2d_#~9S_5u)A3%2nQq z=}**i+Y&~zZ;-V#k)6d~IRS70EXoPPWGOso44xLx;$=+c#z`WtgT}~)nrMY_X`Ss6 zRa#||wjac!dPZ1-mm#pgF+Kc)kPb-aJa0d#V;T^hUAX;F^f(=gsJmSzr}burl7_a1 zHvU{_%AzD36eU|E9A_Bvd-l`QVaEwCKf*0p+HG4!bb5MASKIc9LI}3*_kLDj zaz=7+0RS&LOB_em*ld=F_uIYy0O>IV@w0L^O6r(x28OEPJ)(l17oC)%NH$17+kyW8 zRskfO4ZbxP-%W_#9S5BA=C8#rGbIis!^(J9y%j|j=^&I{pou+myweS!rgFaS!v|>u zcwG6$vvGQBBWt5;i8_hjx8v-t9_JgR;H$S!Zl#V{DrqN(`dg=U*&GE9LCF6A7D*)V zr?#uqF-*E$sluJxZI$52(9aH)F}QlwRPoT$(EwvBSqnZTjz-Q99>AU1%Ht=_rcRp4 zGGeE@W7zZTeU!ERmr8Rbpv4`1)}Gc10qHvvoD3ju0AL(s_wV0R)|%Fb#Mcn(t-rdG z>)B0%sA%J>te~3bM$eERKk!nXz<2I;$2R54EH(5xPOQYp9!)Eqsfi(?$+IHf0!sT zlHp|11FKuyR?}Z;fuy)Z%j&9?+R6Pzm=bcNHzOouj{Ipqt2zWdNzH#vkQYs%c_+wgMTEUPbkuGT8?_Z654%$Qa1r>is?gjg8!X)^Y}>hDvk}MLiQsJ*7&#}6JC6e+Ep%@$dq3UZCKX6N?uwV3E0;uH%Lh>9g1RFdY__dOfj|2swoH}7i4Ye!O2xU$-uzl zxy}Z$`p;3x!g$BYg@BqM&?b(+*#1aIQ5w4|dj$;EtCY8D_kyYxq!g!P`xZ0q9@Wbe zj1D;j9~$*djkwVWn&N*C?2~Y{qWU}P&8~ui7%h=fO}0i02~x7i$BoSR8DW5P-y9R9 zA?tlQ9LSlH;@4{I2}?^FM}1YkUpkugcibr8w5+KNamOK9YBn5`#|@U?4&;pa*R9DA znLKF8A={5cBS}3+Y-^Ly)0>qitku7Vsfd#ka6ua~co@!C9hCRZyf$~#_^fGyN09sN ze0-^Gc1}yDOp2N_8KA6%bCcQvo5^akF7oIKjuX0KoQBkaBo@bV%_r9g;VWFiE=LdJpo|i-Gcl z;OP#&p@N#*M=h?=OL!2;>Qu*1v1*<+MN+bIPp}mvez`f<$$pXQ49DtI%QMJiuP$KT zh@;!L!s%JC9*A46tMDh#d|dqCUH7r!SMIve_KKA#&|4?n^N)H|ve%vVb+4Tf)UwA-QFWfkXCyHoaQjY4Kejw^snqFCplF}LIUB$hI9~4bvEO=9Nu!q6 zOH@&vX-n)@PmZ~ePir};b zj@V!sF2HA-TM_Ftzk6IKVmAN0%xWFd| zobk>%(XsktFbtVdNawhXgSfDz<*fjeq`Q4PrlgJ<>Omq~W~12x#}2_9WD~d&Fs+k; zjE*$a(oK`ojLz~ss^Io61zzh>m-UjPgr4y8%a^wK_|Byj{2({ z_#k1(aUcP?HbRGFH*#7k(~8~GRF>wZxur$Ix~4zoI1(592`80e4&WH~4V|Quoo34h z27R@zCb!&rC=?1}(RF;X{tD+UGe;dO7LqD=%yK9kH*N_T#z!CwYsU0kj5$baH!a_e z_K)J8EohFCsDk%HLvW;mc&dzy(!nV%%o*{PY~+GCz~Fppzg1%8PUwy%#Ft+BZ-ve( z`|35W^%OEe4I@-R2`wr}%uP58o^UbP6XyV6^Q}IWS@)PTBfdu2GG zWTC9QKE)BgyVH<9Tii}aCxebNja+?Sk2oxW*Vx!9cjYo^I_W86hOU~Jn=Ya&ZIwx_!T`T@{B@UV+IrJ-l4Np1GzC^Bz~^}w z-N^SJk&Pb}?T|^`$BXq=-<2-e)z8$m@mr#f2&&P)4(HPum6=Helnj%CK{*;~Moi{3 z&NLQmDE|Oub`!#GTYjJPy$|Cn;-)OJURq~mlI>qhN|j=^B%jp^10yc%0u=U9hQU3+?W{hXE89EqKn0*(o^1`zRW{3%En8}d z=)tzXSXF8lY26&^FjPJ^ zg#_oeGut|*E<8C>2%&K;VX*3_5P#k9fcXgW`l)ramYZeHhN{I;1mRzFtPiV<0$9Fz zIppKdIPI-0o|zFYw;fi7@K}xQb+)?HX(EaWr-l^_lCJ2}<7)TZ0pRn-dE*+&`)m$> z*`jsJMu8M4bGjN3>prdNiW(bg$#E3&Nl@^Ig-J$IfruMB_B{OL=yPItLW7E_a0nWb zy47-}il#oGMs_%jn@f=55#MPsQfI_8_H5~A4j zHCJ1dSt{?eD2r*>Nnvyd`e%oi9L{`vyHT<$)8^Mb)<9}M>F$mClGfSg zrnk{a3&e3PQ;%~FTL5w!86O9@^R4V|qa3a-jGYI+^5q|JQi8MTS6|oNB)HuAVmc`# z5$<*}UqGN^UDbqaN^Ig|liS|&X=-#-sO0-ZCkJLL1u zp2TjKIzHB}gE}JJV5OmJ9XIf*pG-J(Ms;|UoEJU7Cy&dM#(@S|ju%GQo1*#ee+om3 zcpDw(M|rwX+fuCs zB!z(xG55K_$>RzK7{SQrS`bFhw#M#S+eK@wDL$ylTWf;j3{;dAG}hey6%eT38Nl}B z#p$wYI)EUPx!jBGy15b)vDs$ZKSK32((f(Wg2_&)P>jKuxu=L7$7>JD zxfwnFGsc;3)A+F)$a!xZCWP8W6S^>urT+i}YOJK{Aq1j$npAd=VPzfTXX7473(h&# z2UL?25WYy}bI1Nyx5vt$$+xwIqjSwBL>(z^=;*BdL0@sIcq(?4A%n*t3*3e|+!TTc z9f9wiYi0;W6f-`Kwt_5Od@4<|+|j@^j%y~fRnS)3>ME+_W{3IG8c=;u_{KBFaB!tZ z1CJVczf0st2pFSOZrqYUHhTa&d#Z+m$`rVC_e`a_3d$9;(E2#1hDJi}U-E+BcWh(F zVmZ^V#*wj8$<^!0{LtjbY2D7F>Ddk1K9|&WH6>MjCCm6-K}I`aNXko`1L_=PU=xxt z=i^JkAdWW)97&^O@|Ki1ihm(6Y#lRt=$Mj@L0v~hCQOekV41z0%M1nw1CRmiG)RtI zS{~@emkVjWM;rPemQb|Kj$2LQFT&g8YIoWqikoIVN5=v2kZ?wM;|HB+FwoZ|Z3KF5 zpi_^!Nmuk(YUY9BskPH1D({MwTxS_Lk%m(zCy$)@(lbYIF|g3}AIT6gjy`g_+PZhA zq06t-TDa=c({j96qm)0U!_@v^ z!6jdA?su^#J@5zvzOH&~EpLeTn%nh5lu*d^f1TaVaVTLk@GlC8| zIzy~r-ZOHe+-d`zzEGJar%_rah+Ae6M^_1jrz-%@yVH;kK_?%nK6`0w>6@xKJG{{f z)w+{%p`*6ElBGnlQO6k!;w`I^!#sE7Z5{srPIXMjjCV24_e2c!NuR2{Nz=&;QkZLD zuaaO$B!b5WaX`Z$;YNRLYvz{tENi%M2lH2JDD1K==cpOyr=qz}aHvU@su3N7AyC-M zFZFlG?~P<&$ocX<&5F?K7r(M2q&zsU=qu*2R^6()Ze^#QL_#EG_tND+AykvdIT;vl zwlv|#aCWI4rsgCC%X3O*PUT=gwl0qtgu4jrlL}XWCZ?XJbk?I4USLwPlx>w_1Ax(v2pDYV13AXLzf$P( zfU}03kVSHFZ~JdEl+Ah2dtICS?jr=L$|T8zax##=eU?nJ1Zq zk?yG3i^ZaPuc+1CKQt6qStPkrMHVCp{{S(V@xk$ojQ8Uw=UGqcSg=ai^0qJIYr5vt zJ6G_lE`{nIoV{OZsl^m=NhGEb6zRJ=fZEJd4mdrAIPd3M83l#DRx|D_GGsOXp6<$B(lb!A3?VkGcxN*%sXmlsY0pH)@SqNTbMM~C&;Z7|? zVb%y+bev&0&O+yse39Qv^$v|8$|TP4XmC`uwwDmuVWy;+VTzs#m}lQlI>)ipHX8u_ z^Mjw$p4!yLVU3URUQUtUa;HhU3PVHHPXj}2>ey|Tcgwsi41wf1BQK1qa5Ki2#p=;y zyqJ!V!$z!K`ueD&!YQ^`uV02U)Yn0A^p04gfb1BI437W;2YhE*(Zu)0UD;i%B_#Ux z-$^8~+A67twxw69;wY3C_A-ym2IG>)&(5&=mqwCy#zTnE@a>Nzc0S0Fu%Mf54Y{VF ziaUL}+Z0dua}bcS)he9i{k&rt+&hhIbqts1$@}JjeFqz+bdW7c`h(M5JtelTYLgXQ zr3a>+{S^d-IR_gF{KL)<#A?w}I|2EDB#t@L%*kjs820}FKe|}QtMBZhb$4Cd>S^JlfhD+HLpB7e za@pX4f(P4@2OqYvv%aauiO<46_WHtE6G8eeU(YjiFMvm!cvZcPRt~RU=uNS7R>VQr` zW7rlXoP(2toa(}k!OGd^Q59#{Zc)g1I+)OFpN{AU>77+r>fM)$vu+}s`l#a~ zJP+lPFmr?QK_ll~KSP(NF}lgrc01qlO}nh&&A22t*e1_X(bL-|Sq*h`buRKt6tuHE zh~SK$PA`B?-14dk_!>SL5;z07N@aBXCo^0Uxc&-)jjrfvDlN?=D^0RhhFWm($`_Lx z4ituBNY6iQd7rAYT<3?vOG`Yo-G@EV@_Jy2th42CyY9I~4NX<{k+1bCml&ODNseP6 zr%*xLgV>+SG21xTvCQcjvAi+6e)W^=l!e1yqBdJ|q1K9?n&&aArKzE+V=5}n5$W9f ziw8Mn2ettC&Nbpbl#i!AS!8^U)LX?^=Vj8kRfeI?sin14Rfz6@kW*-mbPXG3OqN$Y zik1gIry3O9e+TVo>J+<=Gx4xI!5q-t9+Dn4*%`bfif_)5scZw3}K!Ic9FkqjCG= zIM5$1C;_i~y|xC?n8HrWH+5Ijs4jNeT3QUuKn~K>J26GTZP*?Te6uJvi7s-U(! z6|J;Y$Z#0#KGVS4fCe0axSn;k>?whZE*~^c1h|v9zBW;&ng^n|`is#WCnc)feMl`d zX#}RnbrkKZ$9YRJpi9H7&pSlyF)ZH)pt10!@LR_h7D(dZ*su@K^ z9BQ*nR2pOOQ1VF`$B+hlooUM1>fYkda8l0!jSO$yS^j^geMe+7U6w0#;u@tslA$O! z5J&ZPOoG{5_wY`;8#AHf1{}PZ3G8%|PW|>!GT$6h1~6F%vSReDy7OYFw=K@uH7&wM zhNVB5>&H?R!2sj3-~ve=I}$a!Jh8hO@?kvr_XmT@%*U1Pp(8;2R;abSl|O~OUZ$b0 zhlA{gd_?Dx;DNMr!(#`)&b(A7BN{h9AnS2EUgQJYe%{MTi)8&Vy!}0jDC{?iI%p$i z9=&w}5iJS-0O^1m@+ryAIOHBV)MiQ;;T!oi`lgxYIznd2)b`$zr?}E|#dST}6OkKF zlHn9&sAW5wkUiapBgoRZ61F_Cir*#maFRsYwEn6$q@!s7 zhx3DsV<$Q1Rq6dH^0d1lgURItQLsCk;HA!swWjSoe{hP!8q0Gv6%CjbBPhjAcDoRA zSKaqM4m@crCLJCerlkIzmEjcfdm`?Zl4?2OsEVdag~=@t9hhSTw{m-D2ak{HNk_%*D^N%_YOv-~In#U6TuiKP4 zY&T?U(ESzHeMdb_hK~1Prlg^XWvH6Ep<$jQ8YKI26t49jP&u9~76iR!8MJH(FV8g?B1U-1LRbA!%wzh7)~y_yY% zU+SI4eU}oLKgD`x)6;YY@lQ`JaVr#&6)mx`gC5h_vFATH?X20b-t2Q0C;~%Ncs~35 zB00b`>NF3cL;X~SiYiDeEpgDSN~N|*`DY=w1SJ0e^%sM;Bl~N*ggKGO(+fb@0s-6H z`<1!$+hEAI17pp|^j5!1`mW((u$iK!x`@`b=H@`gU_#XgvE>a;MK8b)S)khpK3d zjs?JK1CRMfb+z%jxVuUtf3$R^r~S;5u+32nUZq2>RnlI;j^0Si1;;#K{+<^&MUOjX z#Ch?28uaVn96xCIJ(0C?wU?8jYaey4M$r*G%55R8)pI?XoETe3C{QGMK;{amxNIm-LIUUD9TSvc1kS18yC@P~-$`M(aq>Ew+l!K#g5S)5PJ7)XYGsQ^`0P!jf~y z?lGSF&>de67P<{)-F>}BvaZ9QP>;HP_Z?kzbu8b5mXp#(MraFy0Xw%j;hPLR9eM79 zj{IQo_qggE$}^&(eNw0DpVPW3n_b4T-_i8+^FbuCNm)(=ew2I$!l~{-@IHL&Z>V&9 zoUC63r$bym^G!H!*;*OHd1xo$0d!TETcV&dG*qH=Sfr<%{{Svr72Gg{=RQI2xSeOW zrQ+i-MVpZ(zT0XAzNGr9wcvtWDOY`0xb&r@)-7*LOA(P0o<^l{8&0^&#;&6va^G*B zbjz{u6BFA@SB05v3?KDx!-9JZkSSW+Mk!L2GEHLbQk}=yMjxpqD7?~NK z8*s58VQYKc8Yzt}Jd0YJr!M^|^&h81&_@N$X{MHzr`7c!KB5BKq*di|cn1VzV>!_v z%4Bosl+&7M)t}4N3O3!)pc3N$0AFnSdFV%QrKPN>c>_fV+89fm{#FEl4m%Fr^_dSu zICDEJNas1kkPTHk{uB_#A3p_Vn!dlRs;s@1@MUu96G%3A!+{rr=3^U32&^T>KirMrt4Hz14&g8 zNn(A2X$OW8dxm%&yKBtj!tWjJ#d&nCE&-=z?a$@eb6Vo#>Dg03(p_yG!ineVJFTHh znPjJ}jhy>3J-di303ZN3=blvRdQZ@zIJM{GNv~Da*!BF88*M7y?bdH-np#uTRgX`x z<%T$z80RE~B2A~kBzFTNgRN|yuhY*_`#YX*%U)B-q_B>y57Tw^O|G@1wB2BPh>;i5 zsgu?8+ipQ4JS!+WPurbseMOfxJ7dO;ptf5C?_#&x{!q#66VK`AQPy>|^-Foabo$)D z5=j+2n+%bhDlx(11RR0ek&KdcgV27MjgZXd$l_e<{MSR19nkz%i;wBQ(|PMEU#TmS zO3WfUlBCAt1hNkdc_CP5X~@a$4x`iG7^iLry3%Z{aMcO1$Kr+u(XN^4$~mfS_gE*A zSur3RW0pwC1x#cfaCpEO82;Fp*|>r+kC5`$gL{6KRl{80ynuR_p%Idm(pli7nmvY_ix#>RNb zS1;-SROI_k)0_=%^*6;2)6%yM#l|q!@A3j#Z!xzgFG>AJ>U(W{HJ7L&F*z!-`f9SV zU;qePusnmGxIcY(zM=IMHL$uv8}>d@cu*L7rYd!ROjWFlY`sv5$x)-)wF(+GX2TX! zkcDt^JCVr1)ct7eyv%qS}cT<8g zs6fxN!*}O8^cbBMSK~x-S|jz+N3ZIIB<*Ppai=?kr<%G6=DXBfs%4oLq2d&!BQVr1 zz^sRyvghYG89nvnx|h|RHwsga4A0gqa6mQcLWkYaXxo$<)X?B z%y6KdKV4S`_IaM?oQBBVCvUyo9m3igdm#&lRCQv>D^lF9RAzYt36@2aLJ>d;L1K3I z$vkJ{O=5jHlY|{ENe5Qj6Wi5SB6$dF1!+}Kb@j&Pm2q9FVyvCgNDNYq<)q{*nE32S zIL0y0eR>Xun*f=Na5g_L`9gRA(^W6isCwerbD?|6L$YaB85z)=g>VqN_5`tG#y=>| zys7E=Qslo)0d70k$FW|-ft>T( z8v7^Lze&e|D9`Z$E)8zZ2e+a$1Z7BJ#}wc%ekljm^{-G|5^5TG?O%we4<&B=O}OJK z$}{;?pb?&moiYX=f1hj=9K$@~$B-4k279+v%ipr(qUlns%w+)BmIX9{;5jN_j8 z&jU=w^z4mzey}RPi?KnOpgUw!b^3Q}>8R(ZX)d+UOr<1>ZJ{Gkk9x2hfzO=hTT;E9 zH>gRIh;LmsuTY`?04Nf)IyUO;f#$OG^|)2c(No7uG;O=pTr_d*z+orm1dMUpB_{gKJ+cbhf8?My!)E{^l_LH;_dwl6GxsKC{B89_H za6FCzv1uEXs6Lk^A5qoGd8&>FprZj}r!tW=?C2yidvGuT&)=};SO#CU<~liOF@P4= zufMWxINJ6HA9U)YxY#GTR#3gJj?-u|vaHn+DR1)`%Pvtl; zXmHuru)l9*42`5)za{VK&F87?7Z#r2l?LA?((URWPjL3_+kgNJHjoEp$j+0G>Fl8` zhcwtd?{cCC7cibFY0I}%Rke=YY`NR3A**m^%o#Bv$=0BEK;;TB6 zS{datQ2zi8im~BVjVYxvB&sunW60m^<&Fs5gPmyPLk?guOi#$$g=WK=PNx=WM%_Qx z^)(9>ZL-Z2PXUcy8A(=^)MZ31t-2`TtCq5&oFw^dsf^%~tTU>gU+pihS%Z7;(XiQ`~FLw*vY1k z%`qvcx_XKqM(06caI!Rz7TU5z-I#)S?atQ!06zL@0w6=FS1V)6QIpaJWj(**^*PH2h z&W-sQcer_>XTP_4^+po4gf-yV_Ss#-HI?9_S)S0h7gb+dYkDXbmK%q5BW^(FxzjLZ zM0Sy`*LCelsC_~b>MBcBRmS;oj-q((EiOTjHq}j_22r0N5yxOO;~Mu!_?i3-z5D(t z*v6*nLosyC#_dZ_CBCdfOCWfnrgUIYS+;|_`mx}ics%Hg44MGP*e(=ni}p;mK+1UR zl$T01wvZ}i6IH1w;xyi<$_Q5t{{U9pape4Kv2voviO@V3In|{6dX&6z>n?m}W6koq z+V0jn!@);QPc-!^n`Ls|t4$)2-y|_4uMBw@J@md;MvJI8O)c$1Vn-i=Nw*>GX^(15 zdTT{}W3AHDSy=>)BF60~c1fE65<`AfP;vJsTiO2rOu?ECr|bp&>ObWmPauf2yMk#t zBk77s=;5sv51iMvvNIVnnsVh9hF4s#x;nAuV zuAZhQ3dW#A?}+jUP&+rs?lMotm*~Gw!HE!tBH}<6Nxi~Y$F(G58O5 zZoE!n5&BW0NLY_=oc>-;JZFsS(&pgwj-tl8FyLA!_V+}2y~>|g*ENEf>nj>ssiH$1 zF|=&k5k?qcNi0|lkbW{Tr15c19xJkr?hc{fZaud`a~5&x3%wUs-WnTpm2~vu-7LtZ zBK61^0ALe=o;!d?&UKyCCnp=Z@tkYdPl(Q+Ao~yT8uRWpK)v`zc3z^S>F>i(O?JEr*`;E`*TSLjk)Z5J zD(>V6vJaq=p7`HLCUVs zaBx|hKV$YN{avBb(LcLwgIwMJ03}u$3iqY#{S7h-TkSQiDXHm0O4TUS`h@d?;K|tW z&NvwDrM{WPIGHj{IkspZ0lB_-LYE|yrNI!?bk{*s(o)`R)HTqckP=}A7~JCl!W?^- zINV4aoq8^w{{RyenQb6y>~=ePg&H*i5w()CuE$FoM3B{#htnoeg(Z##2pIsW=LeqL z>RAzMyjK1G-k*vdfBX9#Qy;C)YR6<#D<+@1h|Y2M$+7%UU>7R@*goJ&H~kZ zGN933siK}4B&VXe(7K0^MoToSh6L_zz=j0lf;D`+m(I|C&6V)s#$=u&fxnc&qNrPa zG!RwMGsh$Eh1NDO!a|Z1FZB)y$lzoXoQ-B=Wx*I^XYV}FuU~ZBZfoXqV;)cx8orh5 zJM|rLirs75H}g=u#e_<~{YNX<5CGZ-d>-07T@HAqWUp}2cN_lzH7=X62v!b}x>>JN zUZ=ZN%{4p>^zkVw@}^gYWg~Kqcy3RB16uK6HMtGSfZUA~EbT{~zmi?%+gv79uyur# zVjFce3(q604J_(d6=2Q41AslNliveG>M_q8Km3g)cwMslaJp$Wxy@jopACbF4N$Ck2gUl;=3mt?2f@Z&cPqf%^mjT+}sbc@1uY%}}zM zWus z7|`VVhm(+a?pO8F7;>{oGY44X;dH{`Bh;qabnUXsQ*Nc5e~3}lL`0@j#N~zuVUh_x z?2vzLG1U5uFk}b;BH9i1-``{gHTnmlB&V=gt>8;&v_VgdEU`r&a#tC~2`T{~c*ksO zODCe^v&hTTJ<%KPnmsB^Rn${J^f5^qBy%sPnIwInf*4~Uh2&!+8ldS6lmhDpJ~yC? zRuehx5%nY$hUZBIQmzF+4V9PAslh zni{$~T6Ich!7=&_#IAVS-V-M|2lpEC5Mz(En47R7?~m1UuY?QL6I6i#Bb^U0Kau@J1n_!yxgz1Nv;*PYN}afNZACL zWh4RL$8t^wJ`S`-M3NekxBL`~umZlY$|x%?S2^ORfr_l2s>aI5!y_0!)sF-A*EPk? zE#%s>fkvsMVNw}%!`qLssT9%c+LqRj{42ZjjxnFwgpl7RDQ#M(OOgW zpH4NNT1x1xGt@k7vMJy!^$t#UzD7@e*~!W5br@0j9%h}$Au2Kf@_=vQBc9W2pKDr{ z-l5!?oDHk9aRxNcEEjeF@7o*=536ddP|v*g5O0cHBYSsJYC1yWYM3SZx}FMmk(MbT zZ*vkd8w7rF+XMGJYk#h0M~c|unWV7Zts{SGQ!4|&4(ymVtIgIw!Pxy;+S^QI?sq`T zBB=BIK!fAHGINbD8yFfN9_>E7qBxUY+b?&hDyuzpql%N#Xv;UPn9xOx5};#o!6)y> z>!bCzxw#a6?G6`cCxs)YtoNE|1wASlRn$tQWM$ktZ|5hm9@Cx&I)NKxVbx)-73h5x zUeYM{350C7Iylsnf@+8xaFHZrg+W{wlgS+88SZnSLD6xe0yxW$$y77El3bEgbzMhH zR9tH4YA!dbfWQcQAV5 zJCLpr-|MGphh&W%%{*b4?!fF=kf8C;fsI_?cvmhXhP}q-#e5;xs(e`Lt~QxHdfHSo z#(`x+D=<}H4;w~!$vE%F#<8(*wPLX<5(4t zej_1Kmm_p%hCCjBeQILi!xT)45?aXpeLd}3C&pxBMG+|#&pyZT?4ExlC5KQ^+?Kv+SVb*7p6GV&20g#yU18+(Ii<+);Bcy~ z+*R6LTP4+GLGE-_HOU;+VZq$Y?n>a0KyS+UkYC4LUW~a9LjuKd%UALSP zP^ri(j9}*>yz{KimvTzzj~k!xqR7Gpb~~os7f9SJ_px!7p=+)Fo4{$Ms4?zxav8Ea zWDeZ%@ttW;)0lIenDh6vioZ`}2?(Im-(m8(buF@rCakSBu+dZn-14jYN{r`&-xve~ z&)Zo)Q1uwwp{x!!d<*zN@LvXM(E5)DohptEQ|9DZ)Tvjtp=Hd*cK#C!T)i zN5jIEkhRWmCE~}Z9{z{fL$U?=RR`2xr+5CYqMAAyYggilO|=BHMbrVb${eb?qorQDnwm+iHyVpP4jNflGQ@-es^z#Fww}Q9Pi-?M1nwRr zMZbwgXtREbZI7ubF4jw3n(1z*6|{y`^vJ^}6b7<|1UJ(CL3&$? z+UX?~6%7e6ydX&wZZKFokIS5}E5=3$8hmtE=7>6Cb??EiZWJ}3nkwV2EBdM`S_Hez zCAg~Yf+(0f2FM^WWhCv-0CyfdYPo$18Dbx6ca?&LI=1&gbL43{YhySd->ONd^j1?17`eBJmoU*R`1^5Gj zkUS7R`iI0=@X~A&0&XL*_Dr$#klgxu(M5TkRRgT?K@lQ2Wf{Wm{{W=(fyaLuZ>Qx& zj^~Gq4p7r<)VR=n7B5j(ce@%pOsKXP^5b`_cldmC>(jjDymNMXBC2hG(^;OcM zmcdf57_FH-s|+Ceh4MKW<#Fdcain_w6uCIwG)`?sEQ|9S^c~YQu}9W^q^L9sYU$p( z+^SEmUoR0Pd$!|@Wc!XjHJ{h=T;7r65xWCNzvQG65^C8~LoIVsTW7pH@6u%krK-JW6$w5sN z^_7l6^qKuL8AV=87Uv7@ARaTW{{TV~?;-tAadPF7%j-|-t8R6)vC!1i%-*Cg6s4G! zKtTg*ow*@NI0^FTS)!71C(y**iBvCuq;6>JqbB52Dd&N<^CYj*Wj?;?B(&CD=o;ByLt9enwNk?=kxml+Vxx) zNs$xcb3xO^(A_T-F|qpF%Fwr3YX@1{dSZr>Hd=_`HuO+#&l87i-}@Yb}l2x+bCL6ORmg`I|2i~)dn=UU^X zPRt-CqD7sG18|^WYg=~XT>7PiS4O12A(ooq0zeQXk<>F7bisw`V%9)NqXr z#D3s`zsLEyNzG|DYO3YalUeI1E`3DMy)bsDib{C~1B?K3$#A*B$0KmT&XC5y>G~oV8z2=xq?&T3bb?u7+V4Z%UZQ?J5r9>ywgyu^w`BCld&WWxx&*hYQvB zs2%U|;?=axCe0%M07b@=>N;GYR;}sxj;2>EQdta(;2fm+IaYbSD%F zkkuXq4fiVTvOKAw(NRNoroKmAb&u1hm(^v-GJ1@7Cj&ckleg`!J<~sDPmW&`A;P^i zY?p3)I<&Q})$y_*ny$wgiaHC0IF49Ptp}`2)56o5eY=kLj`is-Hda9}mtWdp5QoQXgMPIshAnh375s+80C+(-Y=j_*};6~o1 zi1K_paT>#k;(#5`w#nQ_KHMEtI6+-i^%~t~ngM0}2&Je^!kxh^p5OvD?m6U)a&gAK zd)4}gnavosF3*^E{{TBHv8SHtb6Yj~{a9%lf_hmhQ72#JxDo{RgvdLABR{@0Kc?^) z_Z&=(!MAPw(Hihc1a?gBo}sL?^s;WVOEpoceW=BYEIk@U6Wiw-K_usbI6A*p&ul#@!N$t~1M^VlZDAC32TIaQ zVxXW>^%S58Ay$+y^xKyzR0H9=@&WnRHwB&(UVDIf_DV_09l2XAQ(Nn;)t2C~)W_(T zz;hYwzVZ2(U~~85P2uBB3-x0i&c^o@?1v*mg%zHk>6It(OO+f_)=^TVVPXMEaErjn zBL}}>gXHHLb`PiVb54xVY;jv9WM=jXxR0eJqMjvNt!+Y;FsvecqRqwza5rQe0pJh5 zp2z5zSkGiw&)vHaTscA(PYHuVf1t2gVO9K8(n!pGCX3ucpJ@w@0}x2a!r&h#Sl?7m z__7+tZVPPCz0ueSDU)KahOXxAO&u*1=lP(jxCw)m+(!k;C*&U*$;fb;K>q+j59R$6 zSnSgBeN%Rp$8Wr@`tdCe2-`J0B$qklIQ*bwV;#XCZFgP6Zhm+;#0evCJDLR6-#e+q zd)oJhaoGd?Md=G%KCRZ>S}qe)#)y%mK9X+T$&m6(9Qh;cGA0sFBOId3sl$D%D!%Y2;A{)*Q;k<=P{F05@O;ay~E#9)8$4 z1_WLsW0A*j>)ihUME6wQT906uKS(+eFSi?&G_r}}fqh{SP8CiD;UbKyXFp?+p83;R zFnE*7@O-!aQXDJ-!fbs<`c|>qDdeKArlGCc@vBayqnQCy>Kt~)$vbkn4XvuR+r-^l!s zM6Y+5f`_`!*3YM1Ygg1&vrt|jrLa`W{-Y|1ri8EM4sc%%Tues_#1alze&h}%@K*0kV^;vNV-eh47 z1CGiAyxybX7Fz9}OzJ=URZ&e{4cayQGlb+TnMp5 zQ6JCwE__;uCuHu5)zkJnU8~dTYmMHTDApGt8`MA2sxi+zN0v1y66g7D!x<+9{{S)W20-)k{WJCFc9w(rA{r_RyG_cP znkv{SZb)NaUY&g?;m^2WWCEv-;PQNAk(1Usu6$09r!}_6U69gE4}Qfp9DIS@6!o12 zE#2wm>N==lbahE81v?jWyC4DIX&4`;>92LvCzc#W2)A%+ef_sf&lr2gp|C`8^!ZeVG)qPw-5=+Uz@Wvc0` z3_gOS^rBUlZbH0#5(hZXBzVwp;QKVcWWU|EzIWWIaBIK^aEE#(?a_2yK`lQ|C6akU zIN2;`*GZR`avPQ;AIwLNdD4>0_D-tD<5_FXl6Uy{Mq#bOWlIHQB5V9mQ_Ec-U&B;O zFnH#S9!Uy!Hg{tuza9bA+ZivF{mY5lzxGl|A1M~#pXRkyVT$0s#sX+9HGmNMgEbg{BQ zUm1=+Xn5am%zs4R`h;?tj2(w{1$w^k1tb+PTWwb=c%*5hh2LV!C^!rc%K|wcrZtt7 zhTMS38_Rzr339RssOr8Hu9oW9ZZ-Ax>(@?E+wRe#)gVC3WsIlA3ZqJpM|qy_~lix@!wjAOqWcXkAU+Z?LwZ1`tx zV;tWB=gbN729Hlg4|#bGcOhEUbR=?8q{b;R96McvzMF7z3jzH&82P_9z(SbR(|Wdbv?58{62bHl-9*1{YhhIa*NAP;Y4FSv5o-7wqk+| zYIH#A9=_=9ke|GRPYF*F$-$lVf5i%C$!Tru^|5<0OwKzkF)>5<3cS4TK8b z{{Z%m?Y3PnR^E*?QcYPwU|H0K5yk?}j92o=zv19-BRD*c`g;@V2(teGchsI92OEJ$ zzaP~M84I-OJfKhaZozyhQ)Z5mkU~}&V2Ol=QiPV;P9Fh=H+dgz6RTmjA{~7iR>va& ztMd{`91Z(gp)iA|CNh6TdDI_E@3xvDS81By($5k^5?Di|VSwJy?BhAWIT!?-W1JJN z^_ejQaAlouEpGg6-*1&!BNDL3jpS#cLbpzz>qRr&<~<<-;Ti*HdfgOetQ~A<9%71EW#O>>LhMOEgRDm)w0-@oZV)$tESOY z2~t!Nxl%#k;1375*M&b@b!d8@`c_<_@rMv-0l@tEUYja04H4gk4yT~5;^hrZpQ^4k zY%0A+j;5VP7bQxOoVE!#C!Rq#*U)hKAKLO86b7E*W0Ui;P;M**RYiT5qk5WFt*!ne zH8T1ft42q=xIAtokf5G(+vi?W>uk(pD@>6cdken9*)J)6txXr~6I^{!vsYT7nz}2k z#y!l^EFt7zN3*5BOGU5x1@BeEPNMc!T@*G+jO-> zS6OaY05QpxX{0>1&JNIwKQPX4c)-`1W;gnPZ&ZR%4nFnV>h!IV2p__Q?zLTP4D|I~ z8(7p~kV*jG8ITjVYj83^C$Qrnch0vb>fKS!VaAjI2L4cYJ-9&dh4859qq!&IMN*19 zLea$aXKazj-ynA@?E@J2_~l$brZ9#4X3?>rjq;KHsO=E2XT1NJ?wO>(LFA@rw?bg#y zHlbR5=!Q=P`8$s!V~i>J)-*tw3(nx5RC65~Dj)mTOlo1bLB1;SRMhbQ04y@3dz81h zBRuyVyX*80E>}~T?1|D-OKgJP;126DcD7d4Q3QU8Wz|TPG^tAj@YmXv8RLp_ zBz*FBU}Js>P%(@j(>iWkH?fXOlgMlGvZI^G75uzAKwC!HhK zy4!FbA|SZ917fzlsQMpNCz%U>cG7p`1bfpRLw2FKT(?|nmVLsiutgHH$bjwHk~uiR zIK~HYrn5egZZ1=@dQ6QTU%V~Cxg)r+d0Uz84ahOZZK0t4D_3-teX%QGhO)NRMzKWB zAxf}TCvwCoJBHutIr|<4HI38fo5y>yUL0LiYK8kQm8Go(CUvkL=kqrRV|N+>Z+NgYc#1g0_(fx(j; znrsZKD%YtlQd=ULqNZpO0o605;hEHj{92&vDSA_>ak~BZ5jm~tIT;LU?(^bsEU!LdZN@Ow7X2mEw z=|4NICg$Y$VUkz_k3^lXztf9_Y!J;G(!zZQE49P0$zrSy(~@z=JZbnk#JLjq&HdbU zUTWlf`=UiKh-q^Pv=J*;lRYIw(}%-~cTvJ|*$xlsp3A47_H8bCmA2tSf9{wMwv8f! zGO_f#TkUm;a!S)9Kp~7HI~a)`)?ADdcVqPU&av2M4Qn)?ObA2faNti!)^TJki98G2F38+>ZeSD%e z+`-t%PN&?SJPg=UUApCMeW~aEcpa~>Z zEwuvVIa7iZ90P%#+>Uftkvva0<4lAaz0VtLsXO%mBxbqwa?xMH;H+K6aBJ5m8-fhN{Qz1=T2)vBqvO7t;7Gb+R++_A=| z&JIrv+qXJ<=p3xPsZ2dmK*!l5Pz^LHw%hYpiOwf&l3xDRl$CUiX}nZX#Ww0vOB7du z@Oyv;bCHhxXHI_7nCH(1RC~G7HLgGSj(!ruCRrmAMh)Mk{{SWSD*6SmS3&rMSfN=X zU>hu>b`)S^18^SvbDdYK^ek57I$S|y9>khWMK(eI0J=#06-}1+MN2~zGEGBEI>z~l zB~iGZ4(0>jj@||tPjEC$9K(wn%bIthn=NVNqZ%2n%MA-da-?YFgw3{P*&J^9+Mt1) zjAx#BBb?(&^{%UttTG231P>*?=v*Tia-eIRMfaigwC3faJ4aL;nt8NkM^4@I9BAmqk%Y8rM2^Hz<3*e(Kr zKaxFB(vv{-(%o&Tv4cXPnRW%?lmG}LJduq${{W)pkhmEJs`p95kjF+P(rBnXOK>KQ zCbz`3?-XHK1m2is{(@TvIouAx+++d|8q3IjUMqggT}7gc7#u~{b#?toZlYChTB4v`y>D72ECoF5dHs|X zP)`73_R?}=jBoxlvbFE#hctWFr@smVZ(^SP32wYgRcVT*rK{iZ0VGv$W6J(`_K(av zoc7}w=U#4MjNFLfb1p`nZ)@y~$lVwvhq8NKt+XllVDz#uGa^LLsXXKQv5XE#{k6Z; z;4@6uX(SI%_)u(tsRCAXWpMm;U8+iI_jeU4l1Y|q;aN%Z?i>waL)0d7%P^W^k*DuC zSohoA5=I8H)Vc~TptRG{)7ILWxQK|vF&suq0ztL$M6 z;QkAsex5?~Dd(?2EyvK3XeTI&2~DGehJPm~zmu)cqQ2VkBlG1CcosIQa6H%aQt4Xn zx+x1c(k`=t;c%+BboE6|ER!O@tBC>}k-31$Ul=`!;NwMXSuAX`)c4l+RbnZ?dq!TctkeGSn5RyqmGVq6o}FyhDJ~UU`O<} zNFy1+&pPvY(sdU!#VORsaZTgpU4Y$EnHBC>+E`(oEwpS@ZDq+Q2f1Zo-++5};C$;B zBcjw=Wd5R)04Ns<${WO!dR4VE)19dVl;mz@3JD+q%L9;CM>$MHMzRtiGzk36db+an)*k_L5~^xxG`XH4j&fzZ0HpFs0mbGihZYy!{yM@=eK zOp`oN!W^7F_1N9^lZ*f{oyX3$<K~D(vsZ8$9G| zFD583+U%)7k(PX|*xBQMWfbOa(ujWQx|P6_=vI8PxC-Pc*4lcidWoyzr>I(q6?VkYk-QWfgOR{J@H=_ZogvJ7 z9V25b6h{82`FM_FgKqW(TtQB?7bKcJ^*)@bB+eM@3I``=JBsHZf}I=XdwDhf+rKIoRErN`x54CH=MmFFMZCtj;H!>MY8zq$wg z-TpmkOj;O0BHR_vN_B5hJ!DrZnw6>8rYO&(tLl5bz?^O5@G<*q%HsVy)p10OkdYWW zc}TJC>W&^N`=Z;QUEXV_o{4&RdQ9p}yTgPeZRGbEIqi@@@vS&rqH_eq4=ve6LDgT3 zs&ciIbBQofWXAa99!O#F&pd;l9(1~N5yvauWoZNos@vTPM6UXQRY^6*nxd6I{Ag*K z98;DE@ToX1IOHGguF9PF(6RxujXaI(()}$^Nu*jpbw^tDH4XCJ4^JE2btODX5^cLd zk(3tzg}`rvj1W2b(=CvFskp2bP3_*jhbUU(e(oDB5!0P-d8DVJq9!`~d&(5FlWjm- zoNmTK=iQH0ES&!U4qdLQ}xSrUDCNNmU$Lv;6Wpph(jA= zhB-NL$;kfzt4p&D^0&s6)BCvbU+{%FumS9@t#`X^HL+#BQb8QF$+@B}ztwkDPzLT7 z9I($I{jhZYKTz-*9LH-7?kc`e!SCH%B@a$lr8?9Lg>*7W83qYT?ZNhf+>zXpJpTY~ z10Ng2d*){Poqw3+Q^YqwDHB?)RMQyfOsO0W%pK_CtZImetFX(Q3YWOVr+cM(L5z7e8pL7)_l zq^p+IYB9r49qHxWO3EqOmN{D_li_eMMsuEfax_L`pEpPgHK`esh zQe>viOs@_?rbrk*Sa2}d?fU9vz=9~9lM@cUZqDTXiv81m)6u;vNl{Ml$7&6aQb%$M z!@vu$j&fM!pC1~YpCe~~g4c~iU0SQ!2uV+nZ|v< z@s4=s#)?@m#Awz7gRcF%50p0%*=O#U=)1o~-7Buv>&?tfC1Z++KcPtkY&cL*jo+B( zI&Lht5)7kPzp9~a(k%Lmc!Q|y(%bryx7v$wVY{X-h21Cb zPTgngI%;aF0HHu$zv$>zxW?-Kh&gN%ovX=SImzwu&Z`lKxINMakZy_H8IlboA{sbj z{1YIWD4>p|%Om@;LBn94++^oC=eD~lP|)d^K?C*pRAaP))7>_$GsDwrWR_~DtB@6h z!~pc;@^kWX2{_Njt0tC^^W1+l2Q#rjT~B$oT&w1%_40-Xi9x^>4UxM96YckYQQH~L zyvJARkWZPZ!)gNeP}bKA???2#R5aC9y*KzcGK){@RBmLGYXO!3jor>ZS7it7;wh3B*NNzo3{R*fl5ns6m@rgq=JU> zS}I0Il9IJhD@z}6yUzoVaeq|#-xmsbp z$s>ap8-bMLpF9)aCxU$GuCJ9oJX)}<65uu$vE^Kv-CS06HOHn!EwQKtveg*$k)=Qd zA`JXz9A}mo1Z458zM%`g$j4fBJbj=h@@jDg~ z$k@Zi2PBZEBR#X{NyHl_p#uv;H{ZDQOL)?syg@{ipH;r5(X(5sXz3}Ea7+_^9I<&r z0ga=T9Do4hz|gWj()O14f{$fwN0OP=AE-B4>qJXk8&N?hG0PN?lPe%T@w*&zyBHb3 z#+MjxbANd&S@YdA!UrgV&2{Pe>%_FywV5a)aXfyv@r<8q{{YK4{Hl4|p7`fD@us?WR+B2+Vss5@s(7tljvmLdF|HfG`NCRU zVl?S+xJfN5pvvkm2H>GXzE@ynspK6%@Y6uuo)R% zdBECFon>VBL{v^5JHF}udns$*s+>~Z+y2kpKhrf5UGFt(cbcwtT6thhutAfD-Jf{i z78vX`p8l&OZfTXIxL7Z!JAi$i<9#-ML0E!;H304`5DC zyu5vXB(ySfsD2wMYe3;Zc_XEYXusm=qhirPAdd{g7%mg~KtFu;({kjQK=9aTSwN6( zgSxJQD$1!U=A))sCtbBDu^8T{e3PDd9E=X#&bNAZP3hpXCT41tyc;$>cJ7vQy+X{L zLvEsy{Y?T>Tp^}a8K2Tc-^*hIl2xz(KN!z_BiFn`og3p}a|3-4S~uB!>Dzq$8(m*5 zvbKATRF8(=9^Hhm3(g1}!djs2Bxm{Sr34n_(6i>?H))EcTX|eLNkJJu<>I*$2_3|4e zwChlUL-@sW&UtbNw*x)KJ;3vwd5Q3vaopCL1D}6{?o@L=7vxOv;y3h0b}MZ>l=RnI zaF(vsEXEgVQWb&$w0m2y$o#{;GvBsTm?ABbwE#FA{;3FzZf2*8S{ge+y>#3DBI7k( zT5a`9Z7NhA#GC+de%Sl!V(JYh{4&$-QRI!j9+rZXcCM|0zo}158=1ON$GxFykiLv$ zMt!RP0OHOUAKMzzkE(*h9Bd;{qI(ckd`7g{2*%-GbEc&=euP&Fno_FcEXfJav;_cr zut3S|JACTn97l#)&RRAK+8KLLfv^c~@OosbpZ?%deOAp5r9Mi#9QkK<8MJ$13^cW(!3yjKGZpb*n@9sJ0O?Z)2 zOH`O^GAh{dUX;33Tp+88vKxe=l2(z_Ac?Z0oH2ClJmV+6gDzu3OIp(An;o|ID}#%U z{2;o1m1~t%HD$tUdZRuktesnQYEBfDBLP5B-;R9fe@*n0G|!SE8rIRH$@nP2h0X!H zDoe$Dbk{oRs^X@_EmFrcQMr~8*ldh|GKU1NJ>C0Yog>uxOW)$XhJ#NOeN%6MFzIW< zuhX~6WqmyBn>E52-l=Z1-lF<+@lp-t-+tVy5tM9xy|ax|qOjvcWIOMr39+>DoR@mK z$YW@j(in_k6V-MsHxZCnetv(e9#6)!vpQE%YeC1EyZ->=YCIfJe|pOv#dNaO^%bp= zE5#S85hD>JmctBV01kNCMlwb)bh#5pyz(|f?XnY{0p1pA3$?_vBTGQsm0dPAR?t`7EmBU;RRv@;@p^>>=Q}?rWn7ZE zB=gRDai&YzFl11;7XT~1-|&}-mJ5#o98_H7S|Ia96cnLCyvpj{pgB;uCvJbF9C6=T z?r&0a10$Rdz$9m3aImwd)ZeL-^j}d>(o0Q8RI5zwPZJ;wA}}0`9{s@2d>vKL^)@z} zGhTPuqR-?#Qo)oggt;GGs$hbG3y(%s+?shkR4`7PknR{f@fh2aoD-4yjR^fU)5F=8 z_T2os&3TWqBU&!gqo6v54ywO3)>Cz~3WgC<{vgLBY@F_&3yygJ3=`YNnRCuFHAf(? zPoHv{Xj-Dx)3ns}VzS%SRw!qrkrY!+5tDfcc10rR$T=&>_{KD|b7FjfqCny+f)N7` zWWcJrUZtw1rJ$<(EM>?k09Htl9F!R2lb>tlGhJu=hBf{!dP8=AUnI? zr;d&ayM1iJs&j8Z6g`vWa}rX=5+>48HC~^+qI8<(MudT3UvkczOC761Qe8s{FqRC zn}Tu*;~)+(pFHbl3n9`^BY%Ve_^N^i>C5!~2`)0+CW0D@iAu#VmjEys0k-pkI}_w< zK1`O&IKsznmpsvN;Cqw@AL2YC>%G1>YT~ycSk=LV$tEO61%dZq`xFva^yFwJMoq3b z_bhgjM+AjOZV=VVdM&?)mWGFhs2I)>C?cP{euMkDP*}DBaJH2Rc&%41AdgH0O=Z{{YY;mi8!Lrz$T! zE%-_=<5vLm?Gie}SGFpM@DKA3a_RsC#t45DCB?&ylY=hba=6Sl=@Z4`0ia2W7rva=Op5ZgpqV zDv2nbYYi-RiQ0CWX*V#2LKF?Eh&_YBUgVH|bj~=#&X>eqQGx6@S8*3*JP6vimtAlSkd0Frt84mdgBolhzKLH__^0Jle*QQRM8Leaof{VVi3 zi=)j%v(?habd!9H(K9S9w;(#_8&7f18QsSmV$0FviG^w1qn0bbO-VsqfjvnoB9qc3I|SOJ1hzRGk$^eL(%nZLnN5s-W6=qw zE4n(8;`0i1B1dYhma+s%qgFKXZS@dfm2Nw!!0<-i+O*v}UNOUagPKS`m-a$?iTkOU zMr@Z0eDtPc1ys~UQd*yEToZvQ$6&nRdx1J*q-0Hw&j&6yIik-LEbxP*7ShP~YdC(d zrmUf9>8qd;dPZ55R!n1@1;`9Z2RZo9ooj!k;PnQ@E>kr$8*b!L?zCd+Ml64`w(r+! zQ~EdRH7&}|B^6sv_sSZnY0%Wu>?JYme4bl5E874KPfW}h^UoVW*o_0ddV8zJKxoy< z7rDvxKI3?}Tq)^ju9l}^8J?NxR>aS2&i^ zX;?0UqhOA|t?8wdn36djk})Ym$#hZKfmX<3M{l;3i=bvg;7&_)U7e6f3Aso0Bk9dW zOpw7uI;Pb&!4%ZAq)Sa90k)FZVb45#aCp|1d!nBzOxR^^iU&>aw<7)37|;+s>g8#n zihGr=)ayq)p^Zf(VfVN6?kAI+VD>+*mxrs)SYw3gjr@WE+vlIj9-0N(ee@4g!+ws) zs?^mKB|TqONP(6)00gGb3*X(_jB||WI9ZZI)S~eZ?x@(VeK%Au4&h7cdm`NFYA$q? zQIj(;3aXK*Khie;09JANkJxDVJ$bpg239jmz#{B+_Ejt=W%*_G6RSFk!PHkPzfYpA zeFbTaAY@lqrbgP^7u-A-qZ4ff+?3g(>3VD1Q%PYn*{+6ED=Rsa z!q_CEZv77&57$i&u?&nY2SM~CU-%Hr+BOO2Mf9(z=%#3C&rNj&7)a%*s-}`Qc-*Nz zlEIVbjoBkv^ZibBXpR?H?kOHpqoxtOa z6Q0;?@z`nh{W*b<@af|&HGH4wqDGz+Ym3KdG|{v&S5F+P99cP#Httty#P^k{B&^l&yx&uet{m+hj4YASz{- zE6+IR1HSEM^_X1_EcmiGI!PK^w{z?g(W^Ul~ zoPovIH&t6`quC0mdDPg!=oR$zl%FHl9Zb==0$o|7x zFYxBCv^)+sCcgxBy15JUUi8pfGFcFoUB8PP&`D_&BkO0RAwgJ{a&10S7 z7zCv4SHSgAGhANXJDW=T^_p9aCfYZ5ubra{wX+-tj3PSwPVPZ&z=5I9frQ{sfYz6P z`@a6`XD>O?%qhGS!su>_>N{=9(M@fR%N(0!GRo6$SYKh;;A1(E5_8BL=-*CsY^G;M zBWV%d?*MulJgmHlJXe_yPNw7f6`^H_aKc z1%+by2^y@8w@&{6rZ&qxWtQa^QcrlcJQDi!?>JJG0ge$+KPNB00P;XN*Ny4jC6COO zY}`Ox<6+0X+=^Nf#L3G6mVQ!8)s~Z?u(FC!F)Z<33Meoh{Y66SHFR zj>LOk1LwLXG@cx##oJOwTDniJXe#6UL^lYjeX_|*G%90_LlMM>2b0^Jka7mI`dG+4 zFqgQ1;6s{6Be?ujoT$nD>ouQJcufivqqoGfrEO&qdUu|xDVa%FKqp`X;ai>#K|ijL z`hPA4HfV9$02CYnV=h530;jVH@wLit4NWwI)nIwJK_qT5??E@od z?~LmgqvAg=CD7zUqJej>2rY32igk3S(W?cTrfMdorVy7dBxWz_pDx3*4nA-XK6JFv z#jVq|pvBGh_xzI&kI$r?mF0g)^z|R$Vz)^J6hnGuk(X%$00~qb+leQY#sU5HqVmY` ze%}nbqXOt~N6M1Hs>%_++idMUwc@RYQxRuajN(9~;3*@L+1h*O_UB&jr+rJ(vZ9TW zhPpyF7JCc*5Zu5WKp^Yg?(1k~o=Z;ZB#ioUda;I-fs-Kw=YHUFoq!x?w!FVm>u_XG zeLi4F+J|pn$pV1c!7b|kqf^z>QOig!6&c@@EPVHYQ3V~{d(FnJl)E9PcAtqzjOx(i8| zuXgPTonxx%TZ7Wj)ueNzL4lVklu`j8Fe7sefJbg}3D=&?>77S37;!y+dlY^z?MKwpG7ZuE9()J!2?7;k~(3Dshh7ADv}kb^K;vw;wJbC21!~ zJBvFT_gu$pM`R~i)6vZ-xH47S&R^q^v6k4(%Z_^S!qpNh&M}7AEGZ zT$*8Z1y@helC9*mEmRbU5#(_=9;0OKe@nm(kUiJ{;Er{T8@jxmMc8nMK@(UWS}1}J z&IEh+Mvb7|Y>8m#ew3rCHwTGhhiXUY%T?)_R1PLn<%hTf+qvU9e|!zq#Eo| zqlCS_d2L3ySM+6qs!vKQ+!gf^XEM|VN|=G+70Jd%)3gkCImdlJ{-zE{xT%Dl)X2!{ zwQ!-gTG$AqyW5(As;stUCU5zG0=P_^sT_`R*pC`pBO|8MgYp|6y%JJybYS@%YG*}# zK(O@c%qk$0)I#5gs4}5>NjXN8@CgBk1ZRPRjAKkX3>-snW&qFjfw#H6$95MZ~SVF2Y_Qrew$`a~|2oK|W6z(w%P(Mr68nPFZHg$kEoos@3|V*L_=A zB|Umm+pV!vw6LQVZ(I7kM>|G29zMq!3>cWxktSAdqg)V#(EZ%uL)BY!htvygUB;&0 zQ+Se#5=9*N1jGqB#z@Y`Q?#=V=i^N?r$-18#58xXKcbQW_Xzr*sV(=y{BmNg3Ur;@lL*WDafHiloMSFVcJ5`d~ zMMrq56md%-Xwb&YSaL#^3P{FtgUQi)%l^*Tj<4ODzS=voEd8n1ElhKk{SuRKIq{MY?SaOqawCbxNqnrkzftO_{{RP0hf{&$!4ZGHGEh6=qB4-})=N4@fR+jrw{cD89V)gkYZY@IG>)5V{g_w-3b{ZG-^WUjLM z?H$UZlHYWwqk@7g231#cG>S$P?cjwR?d;z=)W_)XF}IPO<5yzZt@-|nnEh&4+9HD% z_clTGUHV_G1trc}tA%w;HuD##hjDLDZ~zg3$mh3@jVFsdP~=ZDT}*fyPsb_NOzAw& zZ6BLgF;*ISgHV#zNeELB516B6?m6(lpUvkaef3PNmcA#Jom?hHAa|IWT8pB7sNQ;V z&r4#G+ebVl9;zU)tChlnMo;D12mtm2Iz|?_iWXbn;gs6fw0_2~z(6 zhL#xNj#*GPNmm39a3?#++xneXE=Ty^W@CSR2-M!=&7OGp(R?{E!=I8g1Gv9rzQ!j|+$L=&NYm2GELC@zXfA882_M>p zm$;K}yLWNzz}u}(iIItrd`3Jx3k3Qa;T@Vo2BSp}R#P=)Op@<{wpC}QRhCy}s%|$R z1$e;xrF)L~@!|S+*Q_k4@+^zkAAUGOmLMCfi@wTDuc>M3E-6PBPOK6u63rkekgphF zE-)YAR2<`yb)?^{I+NovHZx#Zb^0O1GyvA|>FAqw*&?{4fAJKg)3i-IQ#R6nsaFTX zaqbsgINQL% z#(4XjQ!lAyJ;G?QJWU&FzCDm!8V{3jq+33l+iK~cxItG}Pa{o~6Q=cSWC-#xgyefv zW9LP~b8<)eoN)E(*pTyDlkB}npoXBy*` z3%a%h|O?OZM{G@aBJ$Xvtt>QfaV+3C1e_n(q{VZQ!5bIznv znOF&vC`mZowQ=po1Rnaw!ep4;LS17_MW6%No@q|kK`dnJ>hWKQhf~Emyn&y4zWzzV z@;$lzHCg(GX_{E^@rh%8>%r&TDz(5));QMtqkARcX1Ga1S6uZm)b9ReM48Fth})cz z-;t5Vn(8@CW1?m}V#|E{{{SC*_xMcYGzPh?=FtsRmd#fb&`6L2DqZsXll ztsMP5MO#4e8oI@g8E^=VK;Qj8)sKRE_}7=}`C|(pK*wExeL36RS3{gC)av`KMG%GH znv#N7^vfAN+m1<90F#5~1D^f$f%T3ouj-AD9n|+2!9B=3f69o%8~2^EpzC`b>Uv>Q zPg@k!vLSbj9FlkpLVyDfbDqPT4t4Y_&!@AYhD=y;9M=Fq+jH=wmN?rD(jT|XEv4I~!DSJcVNa07~t1!qpX*6uPG6 zog|giPO=>5bADssf2VCT40y6LM$Wze02N}>OT<4;UORno^dR3d4UxqP;Y#C;-LL^U z&ODt5A%&8;>=eM;Aa<@wPFWh)QSPNwbY0eh*F{Ltq;CWu2VZxoxDvZqfJqq`?c0xn zlpS&3VrE2g)HFyvh#anwk8aKsqjh&s*IFQwb3H!Pr8LxTB4m&z&v{Y~Gmvn{2fyj3 zdQ8lgNb&@pF57?OeUicZ(O;f_QaXbF09k#is;m%tjT>!@J36Z6PS8A@AL-+9J^UM` z#Upcm)gyHQV#%rpvZRuhnkYWa)KvG&-KyOkJypFTnI%Iil1z(|6-wk2v<}Cy$>&-b z@JTonJfMeuO6+xuD@xK$B^yyz#|(m|SqtwBR&LmC3HF>5+qfS(+5Z4hk@3lr8NFB+ z$@ErqZ+F}lRTV|bOPy-9Tz3d55Aq}}0Z&f@mOOix-JE=qb(0&i@|Y!jCO^jhQCMsi zKa|7zbw@=t6Sd7hhg2gN=p&V|>w)c(MF$x8aAz~!SuoCO^Hj&gIP=jw8JVrZG-GV7hLzoKEyCFS0U z$IzWSNpz&Ct${@>lCkuHjFyWdZWlPoJGst(QQDtLb@#t2XynJxOF+B7l}v1$cQiUV z0Vt!X{+^bGimuT`9$2%@7v&6?&OscL+!5o**1uiDo_9k0B7B#sz5P*$^O-$W_f8pU zEz{7}?DhCv$^is0;Y;=;41R=o(|I3O<}(zwcngC-7Eh+uh{_w4VJA;i*GWk&nz9G0 zSVF9*SYTX0`#Tmg}T$kr=g4pU2~8XQV3gpupco9ppaG&E}ruu!HEl3?ppy|AEw zM&cI-upokTmKRTl8h5bGs=xqMZi68fvyr~tKZ;YYR908inBUN-hAJ9IGkmhctidM1QrLDNEZzWn*rdl#�EV3mpzMl$8L1T)U&t! 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�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 Fk7IWL9%!G_f;m89<8CHy3p4{aiWV8dXh{g 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 diff --git a/samples/ObjectStore/php-elephant.jpg b/samples/ObjectStore/php-elephant.jpg deleted file mode 100644 index ca09b7efae002190cd05457614111460f15a25eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308254 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

    VO9pld(=bu$9R=9g^3) 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

    jO20Oy(kzQjMA(`g#hCn=>_!AVlF;m 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 z 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 diff --git a/samples/ObjectStore/python-snakes.jpg b/samples/ObjectStore/python-snakes.jpg deleted file mode 100644 index 434a031b64ec1078eabe4b36cba4274dc53a83f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1245980 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

    T!cO3rORsn?PIOBn;`zpvR0D=0p?My0~+Zd8E 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 z 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

    >YDitBtq1c}bYP3L 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%0DR-G!v5bS{0ki?%<4JVy1ZC=e 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⪼?yI2oL6_TQ1*!o{c8V&)6)t+4ho(>eOv2{ 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

  1. IYL>D(Yz7 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%0D!OIvD-NwSRUzWv{lkwX(=hh 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ÿ́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≤(vh9L3Z8S*pntX`!vATY+`PLS0e zR}N5xeQ3GSTez z$Lhw4im2Ox0T&z`5z3yx>tn&fZ{K(?^s#(;AxifTx)41zEEJWsQY9rlB)d~{U@q{P z<0lxxkWP3w8XhTYa!HZaUPi-_@~M<)4JaDp)R!xVQwu!^rZGnwKdC7@J#bG4oO}V# zX6?qYezun_y+aY0PSVmHsMUabU!|x_zo-3S4F~a4bhJgXnbw`2M;Kyz?^W=m51x4* zb?LfHE}9c7opQ9*_T!JmNCfbUo;wwP;cpc1S}hk!*_gNC>H!W^;1F}zFC_3g_}7Y` zs}C8XXM-UiRqsUYxAjuyIC2!pY?RSRm6SDf^m5=dfG`ZWY=8*l0th(x#&z!Vq7k^$ ze7(Tvm8DWg*9p}GbT0^|TA2U}rDJr#z+Twz<3D{~SUZ#gZE~xX z7LI2@3RFuUxEzmBh~wnvfuQAEv78(CtS)nQT&lWq(Ri{MCR!$^FSbZ#?#CpN?Cc3S z`Th0M$6}O?(0S-P0#r#-wt7OT5eQl`3Jm0^!1>6|GDmT%VMpe3n%n;XIabhJu_#BV zEfzXzTC0^G;})pxLcxnO4u>#a3)GS5W= z2aVDphf|%bqrn3JsqK%xj?*Ljm&XIpB3|2~OSezQJV(&=X{uLm^R|;Bt}s{=&z{3R zMmuWJLLGIwUCm)CMW3g7fo)UM+OL&H8c?jUOCe=Ya!xaX55O22h$bvxYR&!1L$ML&g4Q$s~=s4>j4Nlm+K z3U>qVAJu?IAYkW652xJrl4ocKdhVZN!5gp7&ak`OZu|kMQjIB+m1NK?ignS zf!Jflu|!jNh6cE8-jn#w0lOi7pSw>>bAlR!BKmL%;#pKOfw+%xBc9kF9ku5=`SBoo zUAd#|wy|D9{{T@R{tu6-w0&38c9<5Ck)x&b5*a4+)<#qX%43oPax?b>w!fDj^*htF z<;N>V)bCxq;cw#0y8UIdRW#v)daIB)4UB{H_TctDHT>u5c<*6!gLdCNzAx2!7+i>A zl~sUMQp?FCbN#+`=UpcDv|4+qFiA4Vg#brYTsCva=RdFcX=x3kYqPSvr;vTWH=$A-)Jajb1iehhV9yiisLcOzfgPk^)13K_KVk9)Diiyvz+Xb{8~RR**;R zU=>8C8RvolC&zD$4G2q1ogoTDzMM)7FoXkwq4#b%`8uOq`&=VcUyE6C!vq0=&nxms z{kv$6X}#5erQ)KYouhKa#???y94Np8_8s-k-*sZ`sdc7wd^p0heq4;_z~f8ENK|lE zl=Uh&EDDb2{{VdGMa59Hc%DRSU-21(8`M}OZ!5&EJKh8}l@RWdATA;`dQ z{qNgMi0lv-DdM7bC1VnKECKKOjQHn5Y-743DSa}ujH=)PfD1Nw{@yh$XbN=&0-_>D z#)||oC4uJ$!5``UylCxrbypP*Ib~59k=GgFzfT>ickzM#nqR~8K_yM6r$QZ wB8z~K1f@822oqvJNx6v<=u#&AAQIMw2br8%c% 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%0DX&g)BQSKIaQEOohRIH$HF6a8>S_b zqz$sq743S4*!K@vo_JxD#==bS03+N~WarP{OF{JcWNu~oh^~Eo(E;v{X8N1cGt8l{{X9Wl$N_i^Z0a;PhA?bC!rzS%)stUx8?`|&vG-K zG{~9Zl;0@z8ymezo53bZggxo+DP%cMGMr=_*p8#d}~`X zqe&#*G>##z^mp_}W82vzy1ts#bLr~Hsya&3{{RLWN@=2#Y>-R051jJAVWRRo*_VmsuF@qy=9ZpSms zCV}6&y61Zw?dX9eG;Xy&Mg2FMs$-J6n)7aJWi+83VUU-GG6%b888`zO*P>_C9Xlr` zOgbUZfbE(9-`~0uo8%5DiPb}(x^mHXy-@WR;?kNnhI(~`q{#dMu}K`9{=9r?&Y77G zcZ&u&Ho^h1Uze%~cmmC=5!ct-J@&ea;|+Bzl(2&o!W3sAGyIZrlJaPXHWw?UVP`V>+fJ`W)N4^sW(EC2W&$jR7-{oEI0ccK`>B?FgbEQ2i zTq>!vP>$naQpz%@MmvE1$HK5d_m8MuNe#@yCrs4CY>n7c z@Ff0N_M8wip2X*qkO2oe80jx>?<9BY>WuI!WKQ$e&XkvHly%nG0a=+PMq*fSxEUm$ z%DDUfx(-iG1Oa=ubM6p>RrHrzjnvam6cqC`v8_CTG9EHX+={#nsxaJwK_>@E31X^n ztJn}I{%e}TP<8w1g}0;@u3I>+tEZ|}Kl7Q^LxH<+IUtO#Mt;Y(snGBq*^4nKwd9gq zd)KPFTS`wxbhRx#ZFP0Rx_W9B*#th24&a_!Y90cSkN`YqKRVO;ljsBXmghXT#Gu*^ z6{ps{RjofLKBDv$s+x2?S^Orl2|o!LSW$zWo5Qx!O8~qdp9fg8&5wgGfjxn@1G<%; zI+yC7RCPVx$8D#+*3?m%8E07ec_AL<-V-3?k&%wxG)MJTOFS{g0c%g1#aQ_6g!&+a z<)x^pPM)__#P#=Dr~X`Py$_}VB$5;WF@i8T2cCOr+}IxA=3{L0$6@+$w`3-Y8=`B3 zmW8$?v(Zm(-l~!@a2VuZ?LElLFu>1j_SG{pv*2hGHKT#$FjbvnSU&fe&2@Y|>>FM{cJmRj1oWDF!(C5l-Se(pdjjHDMJ9iy=14G*b7 zK9F4Yyno}mas4|9l9n~6+Rdn4DR!-vs+Jk*>XxKUnI@J|u(E-)2FD4`2q5R5on~Y8 zuAvmHixh@5n)5gMrKE+f+FjOx=$^KklJ)#Xno2jK5dc;-^xdTgYqKkWN~*3DFDH)p z*5+4N&3xh`G+_7ODagZnVL1*yR(9^RB?r{1nn-RJnoE`5o+wp4Vu`#)94S-5;1R|~ zai7#_;Mke_&21;X;U$kKk(z+5`}4X=^_NxG^*v>5wU=w%eNF_5ILZT(Hz)&iU}HJ( zIM$C$#*TT*X0t-}+x1f50n^FgADN=uX`SmTS+WlExjUX;rDVpy zF=V~2a4t66s_Z^cvL!Ega$8aKWVQ6Q7f7XoSz=UzLpzao-~g@>DU=e zc1zmfHCZLp+P$_yb=WE;)28Zbnh9!xhY;?WJw1t0Ib0lg18DrGgN{Dlmm{L|*FhAL z8!ZQiBA1>@V^dkK7aLqq%TFEV2~sy&m@W^Wp9NX@%T^Nw7bpf zD!n_aMEz}Zm_F{`nh%quY0%vu{vT5$-jUtvsb4>tKse7MJ2D1;U1Z@v&!*w~M80rN znr`RK^InLDot0g0(>}Pi(og>CrF3ye3moK`RrzDSL&tHnFc~BC&YJ37OBXX4kwVhe zG?Pc2`Km&5e8BoDx|==T-BSehb<)mtJP2G>kHP_yz?NMvN#%b!7;q@@Al`O0P+V1$DMDu2gn13gaY3+ zqx(-wCC8>Jmf=qHbd2R2&?_XRP8TW;&=lvf9yR68ZdB}ZC;Ov;REX9NQKwJ+F`}oq z$hN+ttfqy%U-{r|QWt=rp9gOr130S_S0BhMj7Ro79r+kdAl3*S1FlMY`4iqYGi?F-B3!btH0G; z9ERJRHw5-SK1do7;u{Tc)qaRhZ*&cPqN5Ngc2Nt(7V2pgM3Jw25WF)1&m0W=XnjxX z*m_%pWsWk?q9~0d`VxTjMP%t~Rb*JBgwxQ<6<#dj7DYTcMgWEp~E<2uj8!;W^(m4w$vEN5cwH}>>hb-HI!*=WBF zB~>Y|{93Y#t)Q;y1~af_Rq{B%9@E=8d#4;XW4b(+IGt8&Yd)j;AxVegi)jG+@Qf`{ zMzf@@wUto1i3av6(Kus_fx*v_oQ^y3tMp8)hGDu4P-AD9Q`^0sclS+67%mQRy7W)m zKTc5#Of?rPK$41hKzPx_n^+tISdJSRIM#Pf6S4t|Gl8wP{{SsDkuQl}$ddm6(^lyq zN(y)*NacoCWr>Qh2%(2OzdRY2XRa4bM%n~sMT|{e~24jL5K|RUiBgQn` zKTl5Ac(OUV9l)cgSE{SssM=vY+Kf<6l~j`}MizOXL^%ovEsW&j8ymlI+-skQ6oTY6 zqu!8CVQJD@NcIVPbb`?=)r)JgNk>OKqCZR_%BBl#4BUWQ1mF)NI!6l?nNn$g1Kzmd zGABd7go??!<4H!%Jd=-3AmbadKmg-AM{MiW<8+K{ar?J{@@;eF^j3@4y;swAx_TBu z)h#hvcHfeH;jmnc?ZIFNY>j1oO{xf4_E!x=?s*)4bDHI3;zI4)>T%;8MicXLKLh#ej%)m2zN8mw9{m3RB*r{$ zCDplTw7QC`r|GI-=7PHi-A^zDdt+&S>M{W!@JAz!XkuguCu?zmX@uD)f=?CwQtScD zB>m?LD4xf4dRjUPXk8JOKO7dusxxjvqO zYB*SB9s(>xhpqa4wS7iMN`O{Y!9JR>z+3^ha0PSE8a7zQ%Y(zn1iU)I2S}mE%3PGD z>;9KZIwH|keh#B|;-YGCxsYutc+LnJ+I~hpb+y!+9&P=k+eYGzZiOOV&{ai8MzGV@ z$yYK+VsTJ6G;M%LE#n^mey5#T*i3_`i7f&5#fH6~G6SoV)cTK8hN6W~Z8Ij37A< zvod9g-GiK9;IQY&8P|&GeQ!S}6t)Q%L#zq_*U#dwjPc+#?)%nn;Y+YxEK)DxuF}Sq z_6fi7yHG-NlG*3EBaYnn&@n!r%)$dJDb0KNc92ci`$cT7+=i1>x?FD5bS7`X`cc2A zt52~5ZOO)4oB}!Ts+7)*&C z74eL_DJ0{;=Q$@^QFU0%q6lMk@8+vu9mmR(EgDxhOCLm znTozEw3Uw{)J}#!Q~v;}S+UC~#yemE+yZq*#309<$P0s-M#}mK0yDqzNMyKN>?=K* zj=JF;x&NsZJ3&?~H6lY@n$pjwy`K9JOcw`?B_;%b6bKm6<2&32y(T`MKZx=fR zbZKv|in^*XITBKp3Wa!Layj0dM=d;!ZK`%@8`BXYutSU;yND+Qlb-zW zIMeXrW;hCP1LL}bAnv7CEDH>BNRLozWZ4@K9gt+?l6ftRjQ;u1@^c#mJ|z-K)3>^S zLEx+Im4f>XG?d1wzPfaoy*Zv;_yA=A;1(bOo;&E@=w=TiWoEBiR1OD(=(fvnhFU>A zLs7F85VAQ`#WFYU)$L*}FsAkHHLkpVI ztCw+Bt{5B22%`F1)bm{E=BKvX#kL@lnulN!R1^6ecT>0!4twame*-QI$9OWa(d;(g zx2kFEVHZk$=jmO}vL=e6ND`q$+YKX#)q!Z05kt!H=t`2fh^Xy*u!1z7S3{8;G#=a*4<6Clf=Vi`{CmNTx2uj;) zt*4GE>InY;1xV%?+70^}R3VN>I5K0n^Uub!dWHloc$foZ(De31@f>%M-=Zv!p{!L> z)KWsVw4SQ^@-xN)WNb!3W`dbcMQfjBVs`jN{W`nA7#Q*VMN7PWTKc6=4Opq_3(LgCi$w#NrvCt?kb!~UgXD(otk3?GhF5AnT|T6X zNVuq^8eYm-eURH`lXycK04cnXD$Ri*F$U6B4 zQU3s^zMYif`1T&|cfX=H+Dd42Z~p*=FQ(MRZn{w2>EjO=b5SyZ88{oTM*Y3H8TiLK z`Tl?XAYo+@PG|@J05|^tWi$0S;%Foj@U{m@fA~SZmQ}pSdV+!66q9$R$r}t1Rf}MT z&$k2r0F8XZuYb|(P|EnR{{V)?>U|zPltb3wkp0rMZ%lu-57R!UjwtA_cBY-!BI3O^ z)A+=X#==j|I6C>KR{sE{{ET2+&>ibV_xzJF=K}q#4|K(@{{UzXk+$38x>t1_z3mUV zQPQF}jyF|PwnhYuf`2b^2aM~@9i*jnOu)vW&jMCr@`(@KG4{{U&Pqu08> z!#`G0wLopnwxJ1ZcPi(Pf_#jQGIfmq0Qz+5&U<8aY;W$r{{YcUN1Vo=G2ie-Y4wkx zZq;6ux2UbMAUSl6_xc>*25#NIzi#JQxnK10)#R1ZIh!5+>;C}#6ldnd1?va#3BPQ; zSz0W`bAqyMrvwzh7(6x@VX_;Zcs;&+-_&2R@3YAqBOXITb55(i+i-~Jn6_T>A*_PH~SPwwgakWmo;TfBCNd$S%tb3xr)= zPQpg~Se?9XYJbC{B)2Kg5W%J>?{{YQ*_FQslC_^jMqhYhR^0qwj{{X$U z5PdZ{ckd%L@JIAqWK!vRriw~7mO8T~PpB~vO}?U^BknPi@y5MI7tt6}#Uo!Egq7ts zXdhGTsOiZ+3Yz2BmeS8nS$2k&pcelCn4m0Y0D#yXo00mBdM~GcWemv3WKzW94=2rB zUp3t=K1)E{q|Z}*XN#w&ubNhAAf~~XhR(%o4cma*jz%%xCtl2d({_6abVa0cKJnQ0 zJ9h4tIqVyiM*jfq4bay}8fY(6)pXmoB%#83hiE?4y>)W{}DFwsqc=`L~#_&MYN4s)$sU)XbR)JpJ!qk400P z*sI$g)xL$l+o`JPuM|xbr}=Iq1TX*+4hIAdCYfB8;C^(qHz8 zvRUMYjwXKL7!S?aw+>{{Ym~<7qqp0QkSMq?tikNB-4aC1r}1zUy+Pr>Dxc zA(6dS9N?+i7urznB=89DoqLR5*cJ<9*CPwfe+!&_J&+~p0ih!<{{Zc=)15^n)$ILK zNncdHq~;-A3wu*2#g%fvhQj>$#=V|D{W9Z8587NPkWCjl{{SlPmOW-C5E=;iL-Zf* zf&4Nms(P3rj5LTD)E+Ckr{WC)oR;{?b$6iQjNlz z2q_bE{{U(3rLU~EH%{Cprjkij8YXb+lBmxz5z6k`26-9hUrE9Jk3-Y)87r9LT0ya> z)o*8Hi*p6;mis#+GQ!Lfj^2?OoCBO!>=oDR@+TYeZG~VoeHEuKFeS9O~&A`)=^rljdQ4pinPIpvE1_D z5rR47k2+#qOeL)pq?-cKJzKrHj%i}0N*beu2_Z5DU`PXn$8LG}8b)6f4Io?lQZpMt zA6x1F02xU@lh9MgMnK*XTX@b3Z9Y$KesnB&`jCvV?-lMB6H4BjrD`i|6_eZMV{eJv z#uS3kpcyJy5O?PXjC058t{xBC9rmVe8QQ;!$0b`;Dhp>&bq&6%n$0~$!(zsQDP+Q_ zix|s!0djxwtiKDXWA#=?4IFp400Vbu@2LL(R1+&*u?kJfdV_N?s+NF}>CF)V8L`IR zbBvBj<8YzP&w-772G(XREVO+HY4xHA(_4%7wmH{km!ImI2&bXQWraay}2>iuIux_F|BBGAdnQpbC8PH;zl zdwJC}IyNRut&(=QmXJjn+f}i#1zn_Ks<}Oh2ODsFd~=;`<>Z-U zu%Xl&WfoD}ZP$7#Iowgy7}wHsfXa+O-c;im=N$9LbH}+gTrH$7B3xK@Q)O$?ih9b?&&=b%pB1 zYq-fGNkw8Cw`V9~eCKvgxb7tWpE||rol1lxih)W@ekcJrOl;H*7pvShQW5Env%Zh1D0o0rp$T93zZ~u_s7rdr%uMp!Ly6A zoK2D}QRcnXQorft4^Kl|HFVVRqP<4gpVHw07zb$J5rP8!@^!r&THI^yfqVX3_eEN0T|;cPs|pU0h*h-l>ybi_4xiDRy7^+KeB1}21^gbWfJ zKH}8(Auz z^Ys4!@idP=O47Z)HB7|=6BIemE_?Dv0Z2T9^RFS3)7_A5zF(A01dY;~&vp8bW$G(! zy6rUtlyJ!Eh8XiaZJZDX2X-^a;0)@|>8));G5nF3DQL?jb=snQywx>S-}7TdnEDEt z{N(2(VB;Q7_tQA|>|r`oJYMQ$;+}aUl1l2S>Z6dcf?3~_wDBLDW5My;@22pgaVCvl zsX;3&eNX8tmDaHS9@|@0TIm|QMX+IT+d$9e{=R+*_~eyWaU%mBz}TBNzjYoX{{V}WU44dzYhAkI zJv8%M*m{8-%7u)vBZ52-bKAa~!H9^3jr;s6fn^O|`cdxG^{Z@pI!mPSA%vg+%G-D) z`+`CJbkvN;uidnVb|;Tz0fTarpHKQ*s_l15iaLt=^RXQ_gM!Jtk7fer80V0DjO#9J zVmEoRx-n_#H&kEfqM)h&0D0!SnC4Lgb4#;+IV6#u3g_AV&UK5{S`Ib0okR3ZwY%DQ z&_hYpD2BS7#BwonCI%JoIQjVh04-`iFuaFCcR!B^?d1Vp{{Y$A-PDy!a;U1Qw@M{L z6x7TWHvy0478nGd%g85^dBE40`hopH<6N!@Y_lllRo(bdH>0k2)r}N=5E=v+ey5Nwj~~T`ygB>C42%3Tb45 zB@yq&=#y~EploAk5+_O%w{RFh)Y?E1w4J%;iY)GAN6am1MEa#ud3t5CQOQvV%d2Fh2Te^PLc%s<59s#>(0I3183J)VYfyOx1hIUVg&_|RI zc>bs_U^`yv)zP;q+szEM-A8Y>&hrIO)33G+_d~d3k--PBKi^vYUnloYECk)~seOr2 zKCD4scaG6V)3gg;6rilCvH(vf9fxi)>xZNdwN9>bQ)W9{_`5ownPfNJUyq+B!notx3-?Zc0TEMNcSHxTI-|!sot&hR{Az- z8=VAkN$N*XIEj>U*xiL(kA*$ zK9;;&ow`cxShF_msuhOy#^oDuIRt_B8raB!7O=?!hPLo7{{ThIm7v)OgQ%&jqS$4w zt*4?#NhFL%Ax2lmeZ%^({vX_T)*~|~o?#wH@_m-7yGAKvHe+vVzIRt>J(hy#`NmXIA*_tkVkBgVbFqsPj(I)_82Hm}>qdNF z$VE6h&5qv+TH4t?x^1nnFQZpcEnta+hIe2|k%tObIXL8;0rTAGKW7hxuYcYg{%Tm@ zPU|;)W%{Q>(|6c(|pmIJHfv!`k*ZPxkbqm853>WIQrWG*7(WS>_+B-$t z>6KTdTDHrLu3#9IRWi}a8wJzB9^?6sa6BF;?Rd@8jM8I0hUU*F;S9Lm%SwGDq@}pt zgb>?mlIiKVR;HE2MNW2&$0`?`AL<{zfgenP!&HoQ`u0W+rJ!oMX7ScNKJZmbW~Zx( z8CUVKtZx=FqX!|odqMoC8XjgNofDykfN-Lk$H@%0I1(0ar8mnrQM1i>p5IXz^nEyF z0V9WUx3$jx#gakljsB!eNvBc@vy~Y*$N=M=T2YZC4#mcE zT3uU_u&vFh4rn{>koErnN&1JVrFE&k)mm*Xk*!4}XEC&4gB4Of6dv3jz>hd_{R1ne z8F`X5&KzkZZSbaNweHm36-CCyQ)a$gpro~`(Lkeaz)aJoGsAyLz{fqZes$_Hac1hh zLDFTtp^XhK>~#-Q+$xuPc2=`=HDzRf#4VL{RF5|@4^R^{VTpBZrva1NOL52;&YFG{ zl1ljJo;q*6bIM(kmw^8ORQLK#)OCFqPLww)Xm2qhIy9{zX8hUOJ1GQXgOYeX#+G!B zf&Lr>S98cG)O%9m>O>uB)txic)NLbMmfFzHOG`8n?WZS!f^m$D9Cyb$Y7VECZJ=9h zc2tO$JHj7$?|rCtN~;xZn$vQunxeSDJ5C)VLRZoxczlBUxFv=FzyN8o%z5%%ojZeM zaMMKB9BiaKUco;4E2^sY(NxP-BnRf8=|(? zQr@8)4jY940uR9hUamIcy^YDpk5_SLg1R>fv$wiobg`DATHB}KFXEu!3VERtJWr9v z?l1_!7##Dj2lc1Zrdi^b8yxw;Cb#YvWX&7lbWI`G%`FAd6}51;p9R>~lft69bthF-)}(g(<#l~DV8tp3-(q{RrB`kN zJ8$>eFeCwe~|A~D+pS!jYgq;#~E@hyCepq832rbGS^25>RX ze2aC_oOkVk+l^(( z)8U2*WG&KTX(S)wAMN%-))0+vLOtT!M^$H%qwqB-aPmyC6^%(72R`HHoF5$XtbUn? z4txS=>5YE_njV&^BkXRAdLQX*bQ4=@YHv4#X@P|129yY;U(1F-aH>Jh1`dnUaPgf! zwzL;A$LNg7iw2B;C^S#G38QuBKAvc#r=At`DOn1!n4@mN+7zBLGI9^bt=8R+(lK+~ zD`#mtd=+ywo%ukwOW*sWOFbl$R^I7d3{oVh6iTEX93BtBZs%TaH?CpDA#4%tRk2pB zOC@!WQqbM0qnh7iq@$fAWf1P!7)9);SApA+IM ziRz94guku{{TT=0V9!|{OjlkDn~Vq#&-j9E0hvR+Dx)>jbyDpczcG!b0mBmd-#2 zlm7r#N%OA>(xuG9@f{8`+z)OzOc>8LoU8Sw^J2Tl2m7gYf|@Zd9k9}p0XSwPVB`W< z2aoTsq4W=^W@bcZA_p*?z$bIJBiSnQcwHpxgVeJ_c(;k8z2sfrdcG+1T^#`Kp$7O%<=F08!m5p{0&k$`c$6N;Wd8iUB@(H1>arYiTOrjor-GXDT8A~xjh9y|H_>lSC2fN>hDYW7xU&7iAmZIfJ{wh=)cIwFZ; zjx^Y^NVw#Gi2Iah<3s-d>nBaL(Gos?3;Yy;rk8s)1xTI$01}g1tG^KIE^2*~g~;xg);@`Ze!032jw z>n9`ahi1*BdJ)QI2}M;T57apxPNJ6OO*JKDJ5rG!K9~R!Kx~cxIXj0rDlv|9mKd5m z)|g$U+HY?DsPU3mZ<|WaoBE4n=V>nv3gkl{AFV+-~JaRat>=_KoYM=k7} z2QoX~gJX)cs9*w$@oK;H;u^bhR`58>=8W9X(z;;k z2emG2q5KL?n@>X2eFc7M#438| z?biy~YHiUaJxa#$$V*{lUIMBfco;mM`1#h1oih_BH~ma7c%iyAchl+Uq7$LID`cqa znkgx0{cEgIMQ<@|gJPVz?(9J0dH(=GrY4q1Mz*lN_SrSlzjF5z+<#Syx}Jc&(&ao< zP)k=dVo6?^3YnM=`$m5@PJD5Zt!|x^;L{*!Zmvi7{{V!Hn842R4Fkd_>7JPDIW5$5 z%Olw785N`TA_3)ABnCg!yyF?bIM!PqBb@zAExq^e?wf8iqc^J4ik8dxXQ!Hd#jJL9ki2hV(CRyd72_yxz6XVZ2nld0@?tE};Q*12h*{5JTUv10hfRG;Zw zo!gH+#t#@vEO>GK%_HB{21c5+H*5M}YpNP*s~lAFdLz)D8iHBe05>Qf9A`Wd3Y~4@ zeL>;j!-^x-;#jfTicF5_k#CNVr7kgsiKecMB0Plx$KU5mV&wg{bB`ot>=%BPKVJT+ zqd)+&?eDI-de;PtC3-}XC@CZ=*_ub^V)N}_2nWEy9O-_S^piSHud~pMUceFVO_Een zuebHA1SEF*oHtrX@f^`KZH0;3Zg&toG44-|HI3Ev4-k18dy~(?je1CJZta2eL{(i= zEj>XiTRkF(b^+@htqOt$Hj{!p6WsmvSse_md{Ml9^MA=%uRK~Y=?<@kmhDW)e;ZGe zBhMp-B^~a8bRqNlj^ieH!Z7qDB(QI~0ezA(64An+ zdb)MfQqv`TR5aA9PPE3JDvhRgk0v*MLa712%R1GW;qmMB-4L_@2})bdbuEITgYkFK zSY?X~BkpFA@G|~)6On>7nUOTm#rvBqJ5b`6XBvGGy`q6@Vpp%D{7gl^nvvib$zO7T z-;?982a`DW`v>9=)sOg&ODpXpTD4LuC7_cZqVMzY~5y3t2jWq0P{o7qfl&j>UXO*eG zn^VD2LtQmYj%v7PMnBWTWGL)-Qb_xac0Ms+mRAi&PQ%pt4|0XCYY3vFqv&c@N#zxj zsSO;I>=YxZV~h?v)c2A7Twnu|Gxx`hR7d;24De0< zDhpUX=s~?cl?LY3kl>&N(?cA;;WEZH`A=Fp`mskylDN6m zMt?gv+?eENwom!zLg|xaxz5!AXdD7KM`oL)!LF3_wDmGq&Xltxn<9~yahX_>eMUUz zf$ccJ#;n5jHSc|gUf1ZGf-$R02fE6?P$?*Xj3m~Ic%+TvcAjKy${%+tjBuyF#!rnj zaKmFIt-Y?0jP~sSR_ObpE*19s9UK+(RCSb5G*LFq%wjnNsXll-=Od5?yob`6LC^vi zLxuT!^RfI>PxL7J_{|j2w6ocaFuY|lk#0es%e9AmkAQGS4m5m@os*Y5%#$6>W2kSq zR5mz5_nU1pJxyGb89*MwgoTMQj52ZGxgwJ=nIbgXeKI-roZyl$InSLxkI=C(WHL2^_vX^q-BLko>FQbJ zw^BiJnyD9aG|}!v@JY{pae@6LYYsQL^Ew$~J{SPC$Bkaz>ZPdjrl*dPsH~OLQvM=W zc@04XD|rl`2J3mRFf1iqYX8yH*>xH63G_TvM|#+3TMs9{Bd*rSL!)_lPBqq1Tw zk#>e)=|9BRLo+OLgp^AR&c5iBgT5?(g!deC$slK68^Ch|iylDKKUJdKfN47EI%2?V z)ztS2>WX^2sd^|FKE-v%o^W>KBw%;ewnx)gZeiBY_ntYk@w#p~TYK!6-9^z6G&LyD zr4_nnXm`Q_GldxhfG`0ay#CtCljzKyJ}hl9f&4Ug_f{F~y)1Ai(IhFl4#`PpO_RrP zxC|drgSc`ALaQ7M@_7Ic(@ZxGcX?%ufH&XO6}e4e*1<(7dcUIj&goFn*rRb2E~^_; zkXRgo#kX=40Vftnq- zPE?Y>3}k+9*GT2!x^UM!YTzDCq71jR*eX0fO>Y+afRetY?efIoN=dd*WGYzgb>IR4 z`;6&HF{Fk+5$r8$D9;zDizve3(^d0Pn#iH0e@+s|bCOvA9>rfP$mD?q1m>+iz=HPc~2YQIC}@z>w2HUD1EE2T)sS zDk8RA`je()1!kHbS9C8Y19*&N$T&XY2RwEf1RV>dM~|j_eNQI!Y9nLEbU8Cz-%wGt z7uD{(sf~>fQ`b)kh@(eS7?8}TzTD#k_Ro*oSb04gr!)tKRq?s!^Fiv94I`SjUUx#37XF<91}~X{{Y%& z>sLt2>D&6!Y0l!M()?{ zpEmFI+xpdJNsL`baWYfw0!qnRT8R~x3my@_E<5M9&XnqZvX7;l?J?rFPpEUZ{{Z)q zF}|wmFtm>lk$&IVe_sCp{2;&W6<0Um9VDiN#7ce^6Dgg!!F4BqxCf7&dHmnl2SRO@ zK+*pI=l*|rKOfdvGHeM281#4hVEs&=*SKFUHz;aKteB^Lkbp^Cs+{3}es$)0kNrHs zr^Lr#((h+&{{ZhM;OlQQv&MeJtE<=hY2U1^1#LZT*1E0Yb`Xhyh)KZ;>5w)C!6%;h z*PUskFQX8x`3Q+Q0lD*Vd<|NO#*_Wmzgq#6J$x^O5a4jDAK4 z;~IM}_5}l2B=D)f4z2j^fIg*ULX2eoE71P{Z$6_5EdKz+VYySux~67m%Dp^gj&`UX z<>VDB@y?8s7l7KuEs;VD%x@{{ZnKv+9P`xxcUcDPR8p9i33iGE+b)CO`+H z1yM58$;j=TDBH**@172=7yU8xjeZy{FFP~_yK;LEGKclHmUw`Es2b<~(w|c=^;LB= z*Q(kmP`OfBS+Kxx8^QSm5OKyl-xKyxKT46&K?xN?sP@{Zo>1h-@-{=zCCz zdUES+UZl8OCiaJdGB&hp?F`uDxbKnMkBxl?>CdL&iV|gu&3@r$%PysjsruLIIV@)C zJFO9!kz6FwA$L_I83N;g0308VJDqztdLA%pk_KU3Cvr8`nio z>(_oFL{(>z5-}MkDhF^t;2-)=oNL_T{>@>J&5t06*P%Ola+8xP{`Si*Evu-yRjzf> zQ&myI^&W{>6LxqdB;&ZpIPOkQI?<2l;s9`vKD~-d?Jc_|lRu;Sj`vfVyT19RDh5d? z+1s=n<0+2k?nbnt=&xv}lHbiQk<^?4bS|Uo>WS;_9ZNF3btU)l)Do~JNPUx2D*Ffr zI3tmPk&aI!@M*tia^t|~vD>tc8DcS=z zN!)m1PxyS3rTTYHc*k_u@tEbZ3H530mTysc;HI^~DaCCv_9V#hbJW%MF+ql`XM;+8YOE#4s zRk}v}mkI`1e~DAnQ@pP%m0Nu_0B_vHWaB6>e%d773_y5{)DOKNh+QBw>Q}bDo}|Co z=`F1~)58Fb;sscS3Y@>RV>uZfeCdp6+W7TC-zTR#py_bg#?=)ax@hWYsV9wXwXl|r z9mBkJ9zuu5y91AR8SU|{Ofd6|*xo6?+N}~(SLt=vtSImDCs0QmRFgs`r;bSjY>iZb zxGx*I10MrC>A{#-5BiI=gZE3ev);W|07olV*efcov&NOT7%60YgjwVmmA4#hV0j>O zlh1MSrQMS8Z^eGzpn;<+sdc21TfZ zh~zjv;6J4Ex8R;nZD+q8RyofiwH?7mqE3;(;WQ{~uALXu0_8fjXBwnlCYCixX%(b6W(m2u z;AG&8aiVnWDB_jQ$#8o}BDdS#(E%fcEPW5N+v!$n2`Q;6q(aDQBR|e<+6<30HDfJC0skg0N zUACU4NomWwk_S6>jE>>h@_5I8oh_d1*TthAQVy;FvrC&mSxVlasch0u@GbL_6hmgY8Z(#G!_*82sPQ%sjWw^&Imuac!ArhG}yBQ>9 zlmK#boP)q8jd|&Cyg58vc+1OpXzr}n8$f+`a)t}klEo@b8^&cmuMDhkcI5p2Y#s)D zYiA$rGJqq;b46}B-5yL|Cy66Xe0r$;FV@RXD@7EP@lPOJpnMrHatjg0K-@qYb_eNo z4pR2v$}8$#sJF{$t9YvArgUXv6GnfT&$I#t3Cm}U1_PZxjh0&jp z9Gqv4KH63IQ+?>A1MpnX8aEcJddB5%g;E;IN{B7&;|)%sy*%(n-vDDfhn+2xEYKE~ zu*_Y}cK-m{42%bMiXMqs1ieZtE(-(ER$GggNYWt1y+AetyUqtAu_x`UZ0?EFA%*=% zQ4JOQvfcOh_EU`xxkP7c?v}krVWxqFeddDeC0ucAc+ijxPr&w%%rlOA_X8T)`i+{p zSQwzXVp?c!efIWB!A=e`4Z*L;PwE>*Jx#7Bh7`1{Y3zmsR*QfE$RqP(9Q<+IYt?-@ zlb0q7U!Q&@o<(dp3Lm!!JE!NZtQPw%9QMiUR#37>Q80yx2OEdRM*#D;935&1cDvm6 z+@;?;2b7=HIqG^MqAPXXZuUep+0N;n*G*cV^*KkB;Y8=^@z8a54u1EAduL8)e4t95d5W%TF~N<8;jR2t=q)lq_dIRw`l|ptq3iWcRqn|X z(py7cL&B18W=Nt=K@5982R?byIbTb4TznScWtT|uD_)ua`jPIe$JC8@94e~^(W*|X zqO797^rua=(ivr_hN)1YA#fH3qo_Dm&nK2d_ zW%9Swrk_uA+?KkCDDBNSY$>ZPAk1R}V+IG4jxsxr_&PsF>rTm-<2nN!0_!IK0M1lO z;m9_doijj|%Z2{>QhUX$$)We1D)NXr;=%ZN<-P3EQwMz_Zu zB~=@SUDCt3_W{Ta_rUHubE}9vVE4v6Jv@$X`23LcQ9-wlsGq^^b#7>J6sWQK>nI9{ zgOEppI3NyAkj%@JI1?yf1Cl?^ctsCj`^~hT`k&TN-72eV>1KvQ97T6X;d>8gKh${V z;11db9#qr6L)}|n&$_TaCu&G5Wv-&-b(Xfy)U`3pnOIqiD?SM<$^9q3067>3$=03_ zjkzz9M(#jT_QppWq6?2gU+QHurCLcgBdFqHsS;%6QGcr(_xU9EI`jEmDi`KSkVl!u z@9;n%4im?vDlfL`AHmQHsyL}VqynssDLeKLw*c<(o_N-bGi2k)(#KnCaEqupT+!9_ zwdLyOuA*9sc?YT*pK7!O4Eu+3o-#f)pMitaG&%Wsf!mX$w~s)%K>BgiZ&%b+rmLqe zhJscS$wv_~A5B|pVNV4PRAh27+Z^b_J4AXN9;c2+1P@X@7ajRq{;u!z{`e+-p0{0S zDymgtk){ADwm8D}+CcA|P;;;s^@_SdRk~XxVt+w1+swirzYoywZ6Cy@g zV{OD|apwSgao;2K*OS8Na<{0QsPkIr!FQB5vEW~_;vH7dO3MELTdm*2ZB>^F8w{~W zDKfLh0eYZtJ%c2YIXL4380TKEBQ4BeY=>d^TI0&cmZ^Ugaju${hPv5Ml%b|sw}|!* z6k$g!PaARmy3Tuk)ggp=2pgVk9jROyKZMeKeL1>Mf4tH`Mrk87Pzg{5B?A%^o_*}N z82tx2Rk#r~rU*?vgJ*hsqqP3*rTdbe9Zzk=$A47XmV$<$vnxmLC|GdEX#A%D5_#Ye z=UEa)#>ylaVtI!Vw`UXF4}rN>ZPE>;=a5lLk{#nO@2srFB zp-YFAlbYO(1~vxiAdBCdRjWHJ>(+JBoAuHw5~UR@JfDROqtl6oL1tC$+ZiW}>vyJN zxzajEjmWJ%{#6|DbcMc_L-n5V)irdcvO230QyAGOSgQJ)@*FuCJ;6Nlj@r_O1Lh6~ z;Ze{u^0Yng=>3QBGWvS(gKG+JEJ)Kp=VGj`-FSal9|O@4;Q<>ebmaFL#M3Ey)Fa zB^A<@vk4nnBpa8xBw&umJYea#BQI{EHr-3#Wh>RBG@_=uDzuT9#}!0u8603_I335% z?%4;%n27lacQyKMGwm^_84I}ZKpTMP9~z@Gb0ZfM z^!@==j}F{6kUjqZ=*8N0HyI{*;;B%R#RQ1EwyE0S<7%)cz6f9jnZ%v)M&NwgoA*7+ z8>4A`tg~k5%Gzs%eVXxNz6%8Tr&+2U-I>?(08V{_K5@@u+gCk9CL{ZzV;}82@7)=U zT1TOJqRY^&UmZPU^s_huX&_fS7>*-#!6Si!N3?wNrk&I!V}8nQ#B%Q2@v<{p>ZNrb z`$cqP(^nen9ol%_RHaD@rr6GKOn`E#c_8DQV}f*mGHPoM;QA>p4Q}Z<)K{OT{YEGS zuJaPalDDE-1yHgM;gpa|6;cNnj!I{D*SRfzm>W{3}+@lKbue~2rbed`| zq3T;@yVhA6ndWCz7|S{l{Uec%4+9z0W_>)&$xl$2Q5512qgO`a!tLxI(O2C1+VfXk z1hxx;)7qtl8c+D(%_M<|4G~j=BL|bE>yeej+eJ zx)|j~7$X?(8RUJB&YEG&e4o96>}cC@x3ZA>N=vWT3k-Gi@hrC$uP@#RH{cO~`@3YE z0z7xpome_I2*YjHxb6YlKw|)i( zC9(a!HSF}!Xi zhRWt4Wf_8f!G6bnBaJ6U78}+l5pa5QULFvmySM3~;~> zFn?_&*M6jtk82wPo+x>N01s{yu%AWw`!$~DbcRz)M3O+H@07$?0~srVTOn8xo(?h3 z+g9swXJ*3OR%6j?gRBw#(eceZo9#%lhokCkidMST{-n4O!tsM0LaOpX1O;GwVDb-Q zb*C;)Lojjh;fOk0aOZMK*>#H2uOgbB%I~(WStJo|A;1pFjDXFI@s=F+ZRvG|OKz{EteS;stuQN!#G~C!84M(L+Um`Xz^+x3921`49WC`vXHb(i zU$8X3F-5}YHMP$5-Cj7^cS}@SfBZm2J#D1ieKiufj%8NC8*<>_;G7WMz}KGZ`5E)$ zMIJk8%@wg5{(LKG^AdPR(DfHb&@{~P&1#XuJV_|q?#4F~Piz1^#FL)dYv`7BJYes( zY8GsHueY*o?||2ackZ*ry6HV<)MG~j=7BfV9OM@9o_zA7$6^jO<-VuI>jSQcog>6y zt#>JT;`VD*jrzmW%Ab5q_7M?1* z`q~rU-9V?A#YC2h7NGQFC5#d;rW9nKaXs^#h6C-dIfadf%lH)m6~@+`~8(J)%6ij8jGE5RIMUK z5{C3*S5cC=_XRl5+b2evZjR3>V?p>#+Rbk?f|bIvq++U^GcBL?Yhu~+Sq7ra?wo{Jsd?zs8cHCM+7?U_Y9GoH+{#z*RRIN zf>~V)4+7~FHj>WcPrBt5);~g?l_~u>wh{uW^RNIgNGBN`yPRud2QL~B=Q)CzyS^74 zAXz)5)l?Ttt#7Xz-BO~Iv;+{|nJ#_rxL_Tljoq?wo;Btt%n-JIqborTaL`X?yU_MS zEDj;3YeThmyX~UBqN19vN@*ui6C|p3%DE?cx_3KsoD5(#Ff{bNAVvm!Gr6ubHWjhu zD9Pn{A<@ts(;n6J`q|RgihE_&=XR>O)W-8u!w52;OWLfZN8|v2K{y@8IMccPLl!d0 zV|J&xzy{YooBNc^2ULZnYi^k9HR|eGTDoX1l(k4Bkjm38=)paLuYJG~&x6OtxB7+@ zvNUAmx5APOcAiE0U+R{K_-}JLPn%<2RzPi35=3H&fsNa8uRYF4-g)5o^RGWQT$)Ll z7LP20n^I9ais^DBSl*f8|lCjr;EAols=NX*$pz8DvKs2eD8&Lc{vs+82h8fVjp zovx#XAa@+|&l&x+9+3!R4pC;N(fG0Yt}V13ce_TE&07l3QA36II$@M@54VC&Qzwo% z@J@vMsicNETH?_E0O1E_{3$I6b}QmkLs2_b!!>W=w_8mnI<^ztlxq*p;7I=I zmIjTSQi?8~ho~-j-*Kv&&0X6dI9Ph34kQqksE{{ZH*lYxvJ@s%g;Ms#=@ zT1jIAiVKM07tnNlof0u>r>3V0J+YKzNR>g(AD%utoMdsO+tf4a%mIw;MG`OyqPW>= z+O|(hq&bB_^u;*c8nzGR2iy)u0qu{zurfZW&N&AbNdEwKO<8hmuvLURaIUKqDdZRHljAbx$%HZ-_12{h!)05(4IxSGMQ9JBM^;e1mA*B>m3JYZ=Q^wCE%*DMFGHpHc zwR4aF&ePkS8>Ytg!Qs_D_&{z$kCe3y*Ir`1Q5zja=ANX2COh{w&;|!M0e`E$KV35J zrq;jg#nkS@-0{b%t79PQ?1CfubyOsyxZ0~I?lfe)`H`^bKqYWM$r%_V@CgG}$umgp z5RzQqygfa=)j@69Dz^DoUu}k#Ym_t7?~E*>Aa?{S>IHoN08a;=`TJpj)oMYY{#``6Z5t6f#n-)gqR^wi+V z82`w)il#z_DHo6oL~UnG z=bW4j_R^g%Co3xqPRF_JB;NgpJno#!Z-Q=rD|;Vx3dhlqJuOu|720{EnYKcXR7ZdY z?64%0k8cAX0C&?^LLli42Q|ZBN8R{UC9Z_Nuh5=H!eiV%n%!9;{*Us5RqD2gyuCceKxfw%>B6T9J3Ai)2o&kVr^ua04cI?0^04d+A)Csc_qs;mYcG z7DoJb{LzD7+0=yvnhS+g)iPDp2~ZLv!fsM~lNcG;a=2`GA3CGHs>aA{k-+aW0VHp= z@Ved&5=!Y=4I)%gz_knb!zu#tvD)rN2;}xT;9zdirRc5dI3ewjw9vX8&&ugtdpQ~1 zI#QRZENv{1RLf5m=Ve(67duYv!#wkhA0OqdT~Wr?!VQ1`v=S@=u{T}*28!cmhFK}i zR48CsDv89X8`!SPoOrd?MR8iH^O-oT7JQdJPswyIB zvF|*Qf}mp@01i9$*P+0~f#I&uE!+NUnLD)>X%*_LtldDBjI7dDM_9)(#?P=YBS-t>dpN1J&sSG8!ZzBhSY?8O-#E{3NbTQR{ZA`I zVnz#f*Wi?Ry^7QKT6#X6dNj5Q=0Ot)&+OE2I|aBz7$jnQ}-%ry1NZHs^pd$vv~Bpz3{4v7RIvO?kZ6N`z3PdDGV` z-N`9M$|=_Zu3tKIz1ZyKkmIiAF%jW3>JAj{NETe1Km~MexG9 zld37?N15s#IHE2ZaF|9ra#S`vWF9k~+DoW)Y`EleBa9aM+B9G#UtV8jx>8cX{9>Xc zNm=ki2Vy|Md~^Nz9(c~NvStqy?TXNcb4@#>RYzadS?;eh19qg1f=HXw)qZ(=k7ytQ zFgVD`)A`wq`!F$|HGpX^kW8}z-U5Df#qln-=xb;lW2BZ(W)o?582rPyIN^>t?lt9p zm&JTp50LL|Pd&o7S95hwQXrBtDCv+Ask;MXcG_36oO8Hv!;zj&y#`m*Cl(jvy!jY4 z9nT+v2p*|BeENyhaMZOF%SUiWaDmcO82}s2=S+{y@{;O2EILTE9B;mh6GDf$tvtpJFY@dz4b|8QX_R_f+u;vEBO%iCL-B>D*ul|+XdS1_Uj@fm{iA1zD z53~Fxaq5cF-f2=&eKw@4sWa6Dh2&N!+kxH>EL#8^aNgcK z_wlbEjn;7T7-R86j^?QC`Xah;?X=P%ewpc`OARy;!*Q#1P(k6^B^|TI)jL>j@8iMN zblCV3J+VgM9uE6)d?JTzeU){)rbP|dO-##8JwqzbIC4^?mdF_+lnMwTamdG=JJ+#H zCm2G1gzxL_iyVTtSN(5qq5c&0)VGQndO;vRhAbLTa#uO>4o+9N$KPFvHbrWC%c)qt zFl~(htclfWBv&f+q^qTpiKI{!2ITbige#n!oUd*?=~?sSm;T=8pyYijQfIDMBidVxJ6AE_s@7)$i9TS0VokqSBvaZQVUrR`_ z(+XINEjoz95TtJR61c$RkK0+7ss*-=CliP`b}uIljv4WEK4Toe;ZG_Rwvw8oZ*k( zjOQF{V>D#Q*SWFQo(-Y+$xC?pW9nT$iA5#3A(_2Lk2#D*jE35z9>bjc>$*lBr-$S) zJa+_ar|nXKO??F)tUjI7bsY*#1tfouje;}92m@(Q0&&Xc9CCXR=TEz;9(1tk*4A4!H~xsk=bdz z$2O@T4jy8y>pE+w;}e#!RMC1eIzp1uglMD5D#LaUxC8E9FgzV(^^B>bm5lLA1Fo74 z0ZKJBtfql!vts%{9u%IU$xdr!3o%$W%2k{Ia!+>PWGTZ89|u|g08@3=^!VeA-@Gz3 zA2!EhMN%mS&U*QK`Wp>o?Q^S^K}eA~LlrR0V>m9&*}{|gM+A?7uS3!?!N56=H)Mc4 zHuXl5SdZKxR3D`u!YL(Iw%n?oLlPR7TzZ2a;$P}+8v~yljd=e6r2eMKl4nJdECI10ai@Nfz;umw+F7aB-+szx z&5h1!h1L@a&-ByQo(dYfFs!C4vLt+?ZzF(q;BYuO@2w}ZI!O&QzAz5gwT|`dt@fr7 zMKTxZgsV+cTT^PMk|a3%Gshdo6-Web8U9%CoD7XQ^#1@|gDa;Qv>CydBD!vv~9s)4VdHdr$=-o5w547TpRDTnRayEcl&n@))Q3hRstAFUV4UWOpCZfq| zvc{JS)GV;LWh$tgjJpB(Sdc(H$Q<#lTwM8F8Pa5c8yakzBDdr4R(4%CQ00Du-@cF5 z+F_$A0?G>d%fj0B$=(plt3O2XXQ0i6%*C1-zA$ zy0hpF{!XB%Z&E0#;E!ao%Twu@BxBw7Oy4dG^}3qxy1_enX$J zR8<`ra;CSXo~n84;%QIwW@5xR>_9&4pqvlaUZbb<1_&74e~?H!BobP80bSC42)9)g zmcG8@_>w>`9G}a>4DD?8I5;G0$K_&VeXdB}NnkWAjy^3?tAx$y@1vILdbmwww69c< ztf&HnSqLl#2aEs-I2@32bFZgo^#)AHqG9s}0YG|f&+3+tN&F*Z%<504Hfwc)k|`?b zO;o}oPm-&$Kk8t9z?CORWJ)pKVer5{{{Zl;C9R5ASgjQ?*HqJ5#6OAJ>l(^P5bVxB zmUtxb?mS~z-~B+$Y>6?tY+^|o2EFvtc?a{^8LVr!bE#O>%hYOUe-lb6A*E$~l1Q%6 zV~0bq{$GN4KW%52GPn_EiGOt)3aWZ~S}S|iuFpXvlmu1V&P$(b3=jwD-$_pBadDZ7AN#fv zLHFeC*$y{(jiR2O`f=B_cIaf1fr>#@v(Q-fdNG0d@JLo%XOW)c8Z5m(UMsU%<`rQ2 z(jg(L=I7AiYi9(KFhS%2$QpAS z8SjL~M&S&tA9LzD3xzavRNsiV()y1c_HniH2P(hPM;!U&4I_z_G}1abW#>3RM^P_akwLs=&W{)&g7~67xemK> zlJHE6KcxxgP%z%tP&1E=a!-6`UdC-s=WWpA;1#v$KB)9_*=g<-R96O5{JB;FBV=u( zA;WRNJ@d~R(Z_3pY=kFnmKSUP08ZcUQrFn-G*uG>M~iD_NKh^?>IOj=!SmKA_=$}Yu@5uK{_Nkey|j-ibrdTvy+VPz;Y zf;(f9KtF9gA5jn*Y$5&k3BZDsIvTj!D(m))5o4+Hl9qBIIzq_Q}S8{=1tc zO#E@B+#Ot>{VwB_v*_-j7Ykshih`t8QOqK#nc-51KqDK6a8Eyd-)&2CCkMpv^n2e5 z%;hCMrq_#gVW_-ZBcT&YSd@ii0XqUu^yBk=wb`9QK<3LE+|~g^gSDh&Z0;qdM;7{C z-;zz(`kL883s(!oGSedzc36l>lOvT=mLmibSHA8z&@np33{m~1@?Q1F_*1fy{hP{n z)AjAY_+_aYKU#y4QmrNo5jIF04v?;=^uPeU%+ zd64B}VYrgN8Q=`y^N#t}WYM+O-E`|sG*PE09al@%-8(@q;ac5yrX}QBSs`SVW5M`5 z;BYb6@#k21PtI(?kz_bBu7?SNTH#8QroN#?L0bWoR?Lp-rK3-2Y;YMdl_R!uo=&&C zQ17}>T-xBKJ?`Z#Ww~Yk9XX+?jmpU(+FjRzirFPG+!N#-bcM|xh;IaiAEj<~c%!6- zBX4_~L@^2iBX%T>#J7we*CWBwQ@1K+81)j<*qyz5p#xim^R1SD;Z--ILTjk0pbZ38z zFo?+y?MhoLUvcpQwI9+it@o?lo}S;;_A7(jmJ{!gger(m2`#}GJTK0CVCh^Z%eymQ z`@{EjZhh6po2H5h_LknA>PRJ-XoaM&Br44&0NB9UYKn*P1OLd#X9zmGoT=)AhA9w>p=4Nyr|xH>tWbeVD9{H4h2*+cK_jY}efSo5Acewxkt zgCD3y&l3hR(YCLhV|d;F@;r-n9y1is$tlA5|sq3P>{RFf%5XM;YcDcmMUa(naJoqHUQ zsD92&*&Vl{PhgUz@1U;N3w0%?f_t@f$TR`H6>m)=0OT1n$Q`nNM~>RZ>K|T5)FP}} zbhWS+H~dGUsoUkonI>_kmmn$XGLf*%Z?wN$g zRMMN&*LrHWoh5fLqM=1ls#gQumN?omgZpbz41BmUC6_p9j=ZMFJ;e{rP{vK)D#E7w z8$guQkW{qp8RMJr8v-a@yWh^dv6K z7OI{Wr813?tS51084SmeNIA#rjcrGdjg$NNpFz86BEk7ac;Cy)OZ{8w3tvmuy%kL9 z6m@Vj2o*44jX=zY&*tN3=f?v?aeN=Wf*|+RRnV`LRzz=fg`ca|g1(-j({QMu-A-Mp z79G+s!=34p02uB`~*E)Nv!AnxL2++|m5weeHEtf%+?!O;>DU@fwgc5+m8PLrU%BfqU)}U0kQZ%g4#BH)dQOD`z-Nv z`rp?SwwacmqSaXl4zjeA!bSi&+{MAjZulp;AdL&@*#=wWdt(gJYKMCV`XYUtGj=?n z2tK6A*LOo;rLLtnt%9Xa(o~`m$oS-gfywXNUXDCq9z?AKQRPW0UB~ZJ7voZ`BC9eltj^OsL--_&tcxb*By|CY`b>f;k_os82BQh*fCiYms!3~eh zH)ppBPi*Kh^yqOj;+c_yB$n%~+I@Pl3&-KiET*<%N8a6v!PgzV>sXqHv~BnZIKp%zV;8ohc=3r))v}&HKu}w zI(aD4RK|0;Kp7>EJBD+Uk?#9*r=fG80~x++?snx3#*XTW!8N$o($T#YGo-A`2ve1G z9OZ~S9l7B0PwF%dx1EoRGcl!oJh%#@>F|bkdGt*ms5a!2rfQziYNV&4%E#&|mE#~X zY{vz2oc8W@d1HN$2G`0az!FMK#Vx`Vl8S1FiX8e8Z6BE!411e_oRO1(rGulyVTLIS zi_WX!y_KRN{{VDasOno4e738LboG*w3&?}kZ&Ff=Q6MApbAg-;1CD#-YlN;7mrQI| zJd|$?j#7@7>Hh##Uu&Y4>1?}GR5W3xW!ml-41{K0c{v#4h5K`3)Fjv91_FPYu{$Mi z)i0;eL2Zhl8=71NRBI(aq6k$SmgH_M0RtmB(sT8Hwzx5#NgMZo?0!n5E^hjQT+?(7 zt_zFJZGz!b6VsG>X=5IsbM3$+?gN0ppUMw>ajaK;Lx+LXBYtj1z#9h0-{ai@&^>`Tk3w zaqzCULv@yWMHS+bmYWn)dZFT290rhW18HITNb~v~VK+7m4EQl3cCAK@_Wp>?kZ6&| zhAV5ITSZAy)JB3hq6!`wrvSP(?2vLXlY$2wi05BH%Em3uhG!iuqrajXCu7FA&Y}F0 zs>{_8PgJb5qPC5u0Ru4nyR))PWE^CiE;zya>*mmY)gO^7qHa51M2*~iXlzkLwY4Vi zPxN0=Uc%q)-8V|JQq)K!SjpaLdn|iS4?l0q!RJ~1H$HhfquJFP;E0+ZcdK200rVB! zFxcJ(sK%q;*%Q)OrLinA(ob@^JZ@*GXM-XXOzz!**;xK=N!I8809E8=&L7gE4F>B; zuZp@-S_j_PcS}yV=-!B}uex2V{Z$1m!lDmWq|HuANk}MOFj!+GjEvy-T=A^==aU~k zF`qGvzR3jL{$A=P@Wlw-X(7-(8+;epZmDrF&VoumO=a61N}(i7QS$jGKby8&818j& zCx8civYh)QUnRZ0lG#C~VD^p$A}gm(U%HN}u3Db3p_19XWD-jj5Uxq{?h>g1Lk>RO z^}W+_9M(Ly%RumZjncv34VR9e`hV5*RrGbHj!KGZd3Qa*n|jK-vSSei0kCuJ?fH)y z@lBt`kmN`Mo@kPHC)0Ii@4YP((zA74CDt21Qrf7k)}{8?AVWdk~qFi z+h*(W?1P*8$FabD)p2a<*IRWJO;xTL{b8Bn-H}f*Y!xq$`FvzJUVo(Eof{*nvMAbE z-M}5cKb6@pCGB|ZD5fQwsi>f*tb*Z7Q#ESJGC~dVpXn1u$N6c$a~@9P!3SR7r{TrZ z_bvxs?q8p=YkAe+9pKAF z^UodwK3Q-x%$&5)HcsGfep3yVjnc!paiE=5KU;dbW~iD9OH_@Ol~P#zx~^Pp!BLV) zCp__i#-ATo!6W|waxC9=?3)ZRKApN=^{3Vg{l=;>c3VBVs5E5)$Vk|{7TTlp9E@a~ z@$sz*`jq(s-T>ORw4iR1NF0@U(YIUu71=JWd|4_ZrY90crAjjavDjw=gTNWj@1Z~J zjh9wyoFqP*?b~$&9(N69gMN4XlPx}?MHO+9@hnN{poK*|fs=SRN0pc4lYx#qdC@Z4 z8)ExTIWBb@zBb`h%@aJY{z&VjdcMAc44+AZ;EG9CNH* zk<&7S85yn{I*&YjB_8&@-cHH+J=)|dX(FYDv{gmvu=L#J*U2Nn{+tuzQr40~PXnLm zi8c6UxmVORS9s|`rl5kdzmp<~wv`7t+CRiHJ>7utbe?<;djvT9N$bYnD%j^!EH52D zJwHlS$#kuPF(RV{Vt_KpLFLFfIPQ7D#xa6*&BA1HHM4)*z4#;ZOfcA7DQM-brjevbB#H62B|7r9l_K#wmgE*Eg;4nW|x03@CWA8l^q=k((f zXK8WQ*o}|imT@4F=$lj*UYw|jX{~)hMOjGt3pBDae)K|zq4l&!?nxXi z0a06FxuYy{!1eOPBJ|{cOp}bMW1q@TJadeU>K{Bg69&picm6(#iB){OQ`>Eolda)J zED5u6GLKe6?F+#q4DS6gthu^wJ`)6Qd;Bs@Z)W}dm7K!u+t~!t^%_epRC89>$pYo1 zieN{j&*dbNOJ#xY`AE*aFH7i{xme8_+%`4G_CnXFSX?T9hP5;<^V2oKBP%eM*chHO zfxtNaua0r8PMgwOjhh~yI2X_LRD5N_nkzR+PjDA|wY}iE3VLa_m1uvI*(8FE*eL+x zp`F!FWFsB3lDu$6Jn7z``2lp0jmH4@P#1fb zQCuaYimv@hEfO8FEKs}cRv(r;_Q(A{t`3IWs3?D_TS?&92ahht0 zX~_`Fuo)D#?tRBN#z*V==!N@|HWlcguyC6F5A_PK_;j>yO!83EJ2$B&TWDfP^p^uU z$l;Db&UKIhV?lF|e(Jyl*5%jH*Gng>Lt8*{7>~qmF8#M1xY`H=f%ZCH2pN$(UhTA_ zGysOJ)r!?G8r4fvYm!n-o|tAQz*0_o@z|X&6d4ZBYk~dKY#sNr#>y(jx(MQ-(Hg63(Slb>O7$mlyp=V*1ghKRoZFmVGsgMom(xyF>~GDDX)gBjQY ze^0SP5gOvdH9P2^|nF|Y}B#KvSMcVxQ z`hn9@^t}{GR}IEm$*R_z%^f}j>_BEx&yGBg*Pb=!vG5&~>6wf7%~kEc%IPZY#RS?q zE}Figmab?V%`tyNYAI@G40dFW=Z5!v{Ecb!3<-KmWXI|N6DhI2t%lnk$$JNOc181a z)Yj#rT8fCHhB4_D3Q3Tu$si8d862Fb9~x&H23Uz2=n_uE@%gIh0?ATtA4`qyip>QC z$+9RGNr$M6QWKHx$ip$nsy8006V!`Xfy@h|L`Dd#;vmr*m{CQ&3gVTn!Dq zDP)FQe37h>&K%&d!N@rM#s-A))|R;Jgl@c_h)s%*s_}HSlt)=I-EBdfff;8Y^MNPF zIZ{5uNp&x)U4$P#6GPnI&IE(_D!M5hyFyXiI>M5sE0sh|QxiB3PbT1=h-ED7a(Ko$ z9FM-5=(!Me78xP?=E<=_>dku&%IuBJ`^wv+EA021OpQZa)Z~!Dc9Z$QX9R#(ZVApm z=|4L2{WmF@uYMe@05lf|SD2qdPhgJMwKMEAl+={3O;pgV!dFu!SV*Tkv%7b0c+LQ{ zKufgHCR*~%cHvBywltVwDSgiVOO02+1}41j@?!8?vY zJ~aLw5823uCz2h_pG8vp`@sy@I)LG6F#t@&|6|O4I0j}suQs8A#iyZ?~gjw%EWYTBp+^9Yr1t0 z#BPHuH_51J=Awq4T8on_vMip~^%uGTPn?t6u+~#Lj2az>1^DC>eDQT21Oo8B2qUMZ3KE1Z?liQ4(Tme zmK$}}X)NYR5QV5^H6_+3EJ}jAMpq*M=f)1PdQrx7u9^or?)TVl%9n7pXVbgnmnrQ+ zrlNX^pL0(d?g*{U;0^+V=Wl_IPQ4G=%eUJ760!k%Ee-zgQLQX7)_xVH*??r)NaMPb zkB->y=U#)SlGubNk!0?Mfv=Vueafa9w7%QuDt9B8$@c+_F*v{@z#cL4q_c5jkpA(+ z4`nu))E8SqntOFM&MJpgE?zI|PBZy{AmpBPkvu{<10%8y`>1OL+5+khlr#-CKA^RgbcbfuyRuD|aTvCf8MFsoD_N*+Am zatOy9chZ?{nVG^?efJO?KZG~29_PB2Md+gSirKA+MGvl27 zwXxL0-txjlh0Qc~S!VjzN~)>lSZ#8=tXel|LNu-p6+&ae`6Knxa0dpKJOF!qBSSqq zWV1B%^d6-*Q*@=WT7fALx29cW1CqtKD&zIR@v8=wH^KlB&*H2c<`QGv6w&EB)hz@T z-OVIGBC|fi+m!N74nS^CKVhY!05Q)!f&CK=mB)~r{{V~V`^}Tpr!q}d91?6*oJp{r zckCG5xSswo-vb&;{WBjk6l7V_@JJnxu^x(DDLs~IzgXwEUgMq{WMa)6K7z^WfwZy7 z`-Sc{gWtZL!NraV8R7lbid_CIA-L#ksKv_C#%7RU}J&bjTfdgPS;HlIzg?H=&2F`Y{87Q5wzz}hzU-Z^A1jcsT(0uj^u}bbr<0nz{JuOGCe4c8$GNo3Y7HE7i6el2)|Q#_~I~jvN!g@13~( zyyMP|(adnhT_~}2u)fG{e(7&rY!~U)l96cb^%71hq>dEXBe+xSAPz`8oM)bUYkw2! zu9D%Yfd2sbsc+)4O?;+b)5{-HQQR$7>veTKT@-$$MNa6|k~qo325q@5k~?xp>@la} z`kFbdkwn7ZQr}fH_faJ6?zP1#R@p7ofBZ>Yi z+dWoT&Z69+-F0*vH3~k* z^HHk8x~%KBz1t(9S|+g5sPs`ppq1xzm%Fouk;co1iVv6yRo1_zcPd+@ z%A$#_w*{$=GRYJn868`V>(93ZxCa9qjb-)icu$04xR<%4y3WDg)mtK&N!)-4^i0#y zl#tU(EwRf;8Y3U8CnYjS_9*+`1bYqT>0d9qCZp-FHqbK%C_AteJygrR!n~z|+-u|AdTeEbMN)hTC3|D1IE!Ab}N)W*GPvb4uQbp5i;HC0V56b5dI(raf!_3`>RtkOpzc`R%7$&~apNkU7>i=7kgn%;UAI zpQv9*I)ZMPYKkhVR=TSgq5lAg0b&8m63RgZ03(e50B+jOf!SsgJWU0lkZ+nCsRy@o zkm)a@{Zj+r1EnDI^iOY|qA;^+>nw>_e=dv#(W zDt;XMQZTq$_u0%cUaEn2T=5l#%e3wC1|6JH~dRSy-N}Tjot_wz2CQuXZ83- zD_R@d@$82iWDfHkzEKZL^!HoS+~_ZsB!;2@g04ox7-q=;D#@SJeevA))BP(I^9bfA z%gNrqL}6C6J-4d_-^3|uC|cDdO|jU>O9$KT0ZswIJ;BCvsLcA0AUW}A1&uXzzbYER zbQR~)StO?F=B#>@q=H#m9n|xd5i=YH00Ix5a69+bm6+NjAW>BmO@+5AD&|W>a4U+5 zz}xEwR#zh=I2b#LIR}rOV)ZG`COlpk4r`xV**mFqju0d_>bh&K1W6=v)d@yuoK$*uB<0rg~ErNZNk7VsCa z9r--z;|G9C{{Vz@>+G+|(l|hNOBGcuW#X!&&sAuZkU;W2;_)s{=i_K$yEz!``s*JT z9<9!CHg?@e3JrZtT?|suQOu7sp<^4V3!GrFJ`W=}@sq9Q;SfZzn{n(;$wZL0^p90j z^*Uaz7it=dC?v;9U6x8wk_!n``^oG{;~qTd?vcUb2MX3`l)Jin1GVZT{Oq3dU#e~` z5h9)D8BfBOz6)S+V@{#`k;8kcyREAAOBGbcrmdz)J!RQ8Kp6#a z0R)kPIX(v(^Z8icdru`5jo#Czqi?j4M-?qh^;Jb7N{3}#td8UyMyG;(+lkK{pBh`K zb;iZi-Hh-&Eg*sh_xL7DN!)Om5nUhDsj8x{s3Rh0r*r|~@$OOBuo)QdjQP<{J05ih zAHb<=eNsEDdcw_Zxyw7xQ1tFpNIf75vW%5t*eA(9zO@?+IM6>XSG!9p`|_(Ou}D*^ zzPj`Z*&|BI3X^wAluCIiznYQ`+{uBlJQIRX8h501DCWhJA}z>p0{ss@=uLxI((RI_ zhw6V%-Ku7i8@)qRM(ycAQUf_r#?=aaqsKYG`O|T_BH#Wow|X6d8)TK+JOx?T7dzio zTPdlnuMG1;P?I=F#@|bkoy2Eo_!uPWUz0Pj*^i`KESmoP)H)W8?NwRe>h7GSGQ%aZ zom_|AD&kG#u_pr?uonOxz>Y?%jTn2uaogoXT|&Z9b1--{q$GXQKz2U#xbbTW%zZnJb-$-+>nLsXezD)YgjdcR8@N$`2*(3X$Mlvo z`6nYFmaq=hUp4BWxLxpz`XU~nmYUr#H#)mi&$ckL7H!|sj->DhJf8UjwsnPri;bws z(}{CjC~Dh}`As@k-8Ql8Fp28U z{;87U<7H?bqxy~fKHphMS1dJCdM@V#`mv}Ra~a?gPaZbs&ZDGE*XHi+=zK8b3rv$K_sSmXq=wsElF65O1hInJ5R>oN)3i=+)i4maHm`*NN(->6lc zHPhyf?=5xCN^l8kmIyW%$yGnp>&e03lcDv#iPW$pCIB7}`;G9ZB?n-J`Zuc~o2l&) zLswNFs>~9dN2*nc?el;}c<;%^yiQC~7uq*LZ(ax{&65@0QVjJxd;68htxROhE|xQb4UTEwiXWeU*S*%qy_7vE@GadOMN9Y z&lylKq4H^r+?-qZm06VC?D?wY*y-QIw&YCEd7^slB-L#HMDHtaJ zjPOa)**{s~bmI8)uTefe0ITaITIx!hte#B1gmjP%>K$8~ch4b`T0Buv>zjNGYUpDEzJ(&y!IUIgXCR?&j+TN*p-kUM^v!Xk?r) z-5Dow9Pj}lc)`dV<6C`yF_`RNl#uN}ZSU}<`|h*l?z+R*G}oS;qPJ90$xevXL_k2v zZ)4;Fa6P{4XYHVMXG+@U|55Z%eyIjFi?HF$iT-v zyXcsGMkxo{Cp1RD4@&;(bh*OXqjZN~^(E4xw%KsB)>)-jcN0w($0u-I;ZGX@J-hRt z&|MZsbjP|!k1Z4H?yuo7-oT{nSiQtWI_9}LwADx|sVbqT1{m5X)T+)0xw~YHjz9yP9~u}1qyGRe?7B%b zw^2KAv)!TVlI0rL(zehf3;d-`$DEX5H?a1KE8w9OK_Uk zY~#17T`c`I)b#gSi@mH~BB@nJ{5&$2jZ-H9#xg>jIY4vg?W*-Im75`S*zE*6)b~bf zgPQB?wf_Lq{{X4+i+ybk!r5}9sWl9v@e$z78O9%Q{G6Z7j@TI2s_7VRZ%%xZ0c|6X zjsE~uR=xT{c~EslrsG5^C@Kro#Otyt5CHkg<&m-58Sly0mcfQcG_|7LXW@5RXxoDHKn@$>iBGo!@}22DheIJ;cw91g%z89g=8 zqMEWv=BTHuR7qrtqYD}w;5qi^oO$^8()mA9Go#_Zs4=!h8Y0H~cHe@AG~Ve0d>u8? z)%5o+l1M)pQ!$qaM&=>&!=K8dxIb(kooDo)+O9`cVer_%cjcsQUg$)oz4N1Py=T*n zPim{66*=DitZv9cw_}5lz?|?$10Hl}a)j+IJA8jne?@-|#ijixOZ5-Zs+z~@iQ4-- z`wseeHmx{a$8)j%k&p-jBe*9)%;?g<8Jica-K!iCrHw+qzu z7J7)xM{0rT$;sW23j)2tJ@c-f3~ro0)jW`$u<^Yu=jo@>DJZD~!(PK%9Spg;H>h|RhG46`KnF;z{wi?@l+%1!S6%}C)9L9%s z9$lk8peHm_bk@s>iyj_JL6SSZ_j~i-WOlSxqr!hseOI9B>DQ$wql56}8Cj)> zGayvPM(#IcoF4r029!M#Ea^;glE8o3Xr>0kQqjE;LrqIyqoq<^71@xo6=qzLNDbHk zHvazrzL)xbJieOAG2c&VaiUF{stbv-pe?Ff)lF+uiux8s1Q?_sy1C{(gm5rE4t3~y zL=TmKOSFKK?OnG^z4k1ji-oS5%OrI+^sK3Z1q`Ul6saeY*uXw=I2ZsOJveEQzD9!M z>%Dv+nR^7wxpjB(n*^4lc!q+Erc6#((~U+lcb^1j+vIVbXLUJZhZ0PwtTBSke{Sfk zu1Z!}X(y3c?H37dkVcJ(<%|;;&d{o@kji`KgYY$p(KDNu1I$9j>`vQNtIZo8HzW^a z-M1%4%hL4;)fII#{Zbtwa~WWgFF1XG6~+k%C%48lS#hOgnkD5Cu<6;S^HVLBifed+ z-%lkfQo$Fne^WX4I|4XAzhS33aAOIQbLDEYcPM`6(=<0puJXqkB~?SJ9m9XL00GF! z1a|;%bI!1Fdd4PBT)qe1C%F8To&{=2KCSvsX;iA2=&Na^i3`g-N>QCi!zpkHH~4&`WRrC>F@+?HAfUj`0RRGX+zvbI zE27~|i_=`}kSM&-lu9lRBj`jFu-G; zN$@r3aA5GHcCEp$f%tY+@e6+HfBhxt+kZ;gtq;{Ug9E_+iPlidhP4VVPt}xz*pQdLd>L` z^ZN1!jy2>uXHgwKGc+x?ylx1v2;)zpI5n}})-viZs+ARxSJZW1PRUDL0avPuDKbH2 zBiNWyp?7=7&z(1m^;F$fO&L+JJ)1U%Zg(coWQL+IuqiI@eRD^8o}!M=)zVbKArz6U z5;PvrRoE*L?jQl%f-{gcIZl@y*ANbOq2iJO$DYM$pQmfnT zPdH*n<&Yhx&frgOb*e*Ot3}XNx-RST_UQ9^Gn#y?4*zb1RTGZRFusvjL>$5b6 zB?^N01D(5%9lMc}-&ns-eJhC`Ga{Z>c>VX=^HdYr4B-IQCF`rM)RIM5u}4uSrfQ;Q zk1-wFPFo-lFbAD(yh)7k_WkCnx}R)4xkdI5rj&MBDsOh`s$b%ikN*G%4L1_T2mb(6 z7S9+yKhs5vs`adv7sV7sqiZ`JQQaJLD5Y;$&vKf>CAN@N`m&rnu|5kf(%VCV2qPHh z!N;9oP0?n_lTRcj^?t)TAx1jp!f_PS{vVu>*RZAL(sxulE!31%VIM%0F z`okLsCHQV*%i=n0Rc-p8$x|WP7+k=23C(Nh`|{dewxJr5tU$s;BxTk^gS2M@3$*Y& z0szjfmDNsT1EguLXbsa(0*Amwa8=V6mL+?9mnDsMgA$~!-_XpVa@oMZ@yBdx?CQYb z{f|qvG?!m*Z~0VV8vI`8Wp`$sYoUhW=}|RVi5oG<7FEMA%8&*!P~`aXdC>Zt@@I4? zVITg{9swtU74g*WqMjz&vb3y1i9-^k73X-s zJG^M}bp}H_qLdPEYFWZk?lvfLisM6Wc&aLYiZZb&F8njM9@840Q8S-+Y^glwT6p=G zQ^n)M8^pVdJBkW2TG=G=fvu@BqqkeSo&>3u*-LP{NSji1QX}+#rZFF2qrOKc0QUz_ zFeaN6Iu`&9-J-FU;1hT0&(eOQvsTj01x>=LxrA)9E-~nk2GuM!w$YWy!6O~9oo5Vr z+XJ!YqQ?r``=#9*M1%pd*%DdfqN#W*sjJcjb&^KP>}+72%yaEMw)gnfPJVp&&SV(k z<0E~GjgB``VKl#|xKT?{JViwuiBJ##GF=J-jPZk>2VuvZXimYe_k;pP`>4bOIJUM_ zSGx73Vq~X=Sz&F96oeqgf2+6z9zpVW9rT-;$z0aSyKGf_rZf;}AeZ{DDqI?dw#^lp zl`$1Zs-U8Q{{S(c8A%qxuc89ycI&R2tFu(BwXsD{P^WZc zgxeS(;f63nWaA&cj@*&c<1 zKhoPFW3_$VB`-ch+R>mSrNZFz%QUl9D#sW@RKc((BN!?G?0Fa?9lK}_#BM{er6XvK zx7U7e)lkUiG?F(_E%i3}>Wxe_?_m5_ZeV3=vZBGtnpg|9p_PjvA%2^k5^G#_i0q3C z2>8n)u>)v71F;;R+l@y>5k-R5f*awE4$bzAQV~yQ?3XvrX)}^$lDmw%<}w1d+RK1QNm6Wp+0h0D+C%gRKmn zl?*|%v>PB3zjM#vtXb%*Ej>pSeZqkxjw-=X6!FbXPyt+(#^4Vea5>}7l8-IoQ8Q?` z&_5T?x5}$Q8z?s`qeW2=qmCtsP)F+T!N}mNkT6E<41ZT28p`^Mrak>iSGRT=swc1) z!Vzbdf?IWs<{DWnl1~)W^>Q7KV2d7;W1NC{P(8yQPPFo&gcKJBncUp@QVq;+vKgxF7OSa(u05w}9bYV~URDz!0b(%1R z)q!^bSaG|Pjjfy#P6s}8Nyax!uNc@IZaz)lWdx}ZKTU}yqOYES89)+9xMNPa#=!W& zAfJyt_|vW!`79Gf!S}Ta`nJ*2Mv@UtNlkE+zoZzqsMPVENmkBFdqBzHgP;%@O^j__ z_eG2y1$b+5S?8x{B1r?sBP!qs@r)dB3C=P1I#W5?GPL%feURv;*F=LAH1sUA3s~+O zYoH?>uWW#wvOcVw6u$Gk${R{$`SGIECx3QDi7=DLV|3O zCxXH>o_0jt+br{J>YD3C=FMeWB)vgAvotcovjA9yKK0M^kIvn{C?6W|4*G^h^+AOk zZY19NcJlj$aU0v+Y70dLqU%j1Oi}*;$HxAg&$+ORoQ7|Z2yStlV0PEmvN9sh0l4wG zvI-=-VX_Grk-JzErP^cq{ps;2p$r58G6NR^!Zi zfwKKNcHeK0>YKxP5u5c>thf7}MU>l~qk9vFXk_&^9!X8)WMmJ|l6)T;&ku8lvM}XA zzgjJqccWK`{uj_VjsF0lav0#A8}lA}jE{{qflYBO*qY@`fO1k;eJZzIQDqk~YprP& zfo9%kVC3hFZ5Y}zd-6W|SE=-@e3=?L$S*tT@a$82-y7fPkeT7EG^je0r;7DKddixE zIfWsQ&|hqx;qCZb<+(o}eFh$tk1o#+6|l3|armi`)GoOJTH0zTXr`8EuPIYXgsG>H zC|HK?=nafu@wvDsPDS+A75%Ck5*@6asD78~sT_!Vtgr4MoKVzSuXC%b&LeQAz652s z#t8>^ZvFhU>{)Tz$Cm=)P1kRqMK)-FfhDv?kW|uMqlD7>NV{4f;+?X@E>1E?Kbye= zO>{n|_D8wS-%+#P(`R_wUWuJg^!KM=r?{;f)vZg>fYGb2){J1NDn}&oxqnfw6Pt!0 z&ihC-<9puJPog1k;YaD~ES)c3Q9ZJY_+7b#W>hgLGs%wZ$DVKp*pJ&zbzIiX9=w*5 z&du1}AI6nHY1wKHoVVJgqJe4YX_^NnNs*57#1ouv1QE}FInFdMr!!%X8G)>DHNC0^ zX*($iVq}sT-?#Dt`d!FbPw#(Qz_aiR>TI%gP~X{XTlP~vSTE4mY=D5kvJ>gwdI zYKIWBkOGzE*dM-o5!)YaAL&5YUi_!E!N8Hfzp|^Jwvw}dU3CgtFBH^N6n8tk5z0iu znZv5bv;{0Ng;phj^XQrKoDHD+cU9Tunn~*HRW!(`ldF1QWQ?4t z#|_5-cgH%>$j+ENowoD}x>y&x$6fTj>S||{)x%!qHm09@4p@!LE;-!Z2iz`nKDCJa ztX9Ip2Z%Sm+xI7m{91M z%`Eok+x}JD>g271^s<#>o;8tUo{82rV-DronBX=*Ic#I&+BBa{h6iMkzO6UPm%Lq+ z%ed0mr(1;7>0bu+-M?!cg}A`nMhCT+AMK@?(BT;w8IgiKW(J!ljz15SA_3eX^`}Vb zE!0$kvf(K#ik$i?fbPcZWM{cJ?nviH#>L0RjgbbsR~wzSLm@GGZimpPiKGck&my#e zTiBd_Ra4vo2Rw2}(fWtf*nKshzdGFGU^I)aIKIn`a98(KQeJKeY?_ACHFP*|`5ZHF zGDmQ6kilrzOfXgOJw^} zGJ81#0C+joVZwAUvN=cqaeFJZ*UHd#&bYJ75?5E!#|x8@92_|39DsN#F`vFpfhJ@) zyK9F-^M^J-tw-y?2f2tIGiacECH zk)4OAEY~UIii(12IlVS)vV~Arg#;x^gSM7^kTIBO}7$<5*x(jsYM6v~M7cutJ8K0NoXh*>lFo6!S=VPea10>cHoa|ep8>h(BksEXa{t5 z$+9-6`V_aaX_k`7NliPy@}ZGX77_St46)#T-09Dz#GWIj4IqL?2fxZB5^qH&>b{Nj z4@F(7Cl>gPpE5^I3kP?02Xc}zkXR5Hc=CAGuTzjp`jd0*uuI~O3>hdB(nXWYz6E{{{S{d@p~d}rt9}Lu){|p)mte> z$$dbq^ucqrlpYvkvhWYiylCmu&z|f!h!C~ZFPiyHvDTNJIoC7EUiGhUpjUdPf}dnT ze+o&;WOME~XQ>(Fg-ps444ulJ=D=H{(P1F?BEnRaLdps>l3}@Tw zI4R2zG7q?Cznl!`I=Sm&Vc}(N!_J+G9TzkZqT703s=9XbQ1P_24ufZ@>M2=r0r!$l zP7i^QPPb+0nUg}+Lo97!8*1jg3H9|rHq%8iD=K5P6`m?ny_IEfqjHIRCy$4es1B(enofEARj!P|M2BBzDFycL~(ledOf(9^0e2r&E3{sH@iT)>Z+uoExH2ynvI2iZs zx>CVGcDLLlrMXpH>gt?EVwxEU5r8=i&5zT9LB@1k%*;NWk}&bw;_}@@^4-l5eyFlA zx%$Tm&C*}QYUY-vg07mB5YHt$A!U(N@fkV5?%qe8ITY`1Tk@YkxCZbz_~BS?Xm(9H zIg*wC03Q;`NjgKbF~k`5_pxuCq=A#ieR*EH)UapHjtq$2=ds%sjfo(7c2+NO*&B3o zOMAND+s$LFY`FkrAjZ&7mKXg#?VKljsE~u8qcaVsIb&iPHT-F6-7L2>d0O) z8NmLHr?_A2PLGMw+#_~Ohsx7cd*3&*EN^3WArti1O(pu25awzvzD2d#Cl6%qgucX%s_- z{{ZlpeSnDVF;~k4T1!Lg+i94`F{O!Rjx7FD$8tj)_XKB5O4BsD+prrYo=N1YYpn6z zVUC8b7|e1>>Bmq(!H{PPcJS+fI0ubl=drmlvMJh21FvuRK-3m#`km5!S?g0@I#1J* zvPE%Frc%Jhpb|p-k_H#P3FEe$>isq-T)~C02YWZN=RUz9O|aa4vDg-t+g;Ow3V9qX zvm`shD<%ov!{h_UV0GBJ{Z1KKIeSY-vOIFr04lWj(Z}mo2~kaC~ijicemcDNir-B z#Yg1H8@nqEaov1rJXk;yIiR*y)|=w{+8bn)xR;QAHdRqxl1HglvQtA$j%8S$Wen18 z&mizf@-lpJtK_z4L`=jR^xK6uUv)80UrP4VNpI=OJ4!6Mc>|0rTwv@`+>?y<$2l5C ze2JO>k74dNS7no_$k(L$!>H+Df}lxhwZ@4ckx+Y=JYb%Eys=zkB%b=s&c}96ELK1u z?|XJlv@IpmUsmS4&^4;2dFFajSK7XyZR#)(_~afB!O)&O2Kl_edVfV{4V7(W>RPK= zxKK+^F^Sm>ZILUSdqxim2>|kW($Vz>%loW$IfvEUY=BS|Z(dpNbPY{eWb|qm{u+4{ zfPzIhECA$S=bR6*JZV0b?x5_WE_<3=L<$wRWW0_9+M%ZDn{`~X+$tz(u83j>bDhAi zV9Fb}>)7kmVa?!3>73HwcH581KzX%KcDB^eHAGaiS4Wbw)U;8OncIMN4*(PG0|cD- zI#;M;!I>a*eplY$n*1q2OX{mFeabt0iAOa>NLGqho)8Rjk{Bw20NOzr=bqiQ=p_PN zL5}gWC>z+CJ&&R=kN}K2E2eJ0?>$xST8QW^NRb_sc_>L@Jwm(`Qb*;-2R<}7T`omI zmAeO^qqiHWgn4YOcAbAhZT4v^ZnRZ%(=mCLoxMk79^8-)?oM()ajMA+4KVO3l)g>) zS-Shwls68kyi!d?Ei9{ z?esJgS>*f`d@zKaAth0G$RKtY03RP3&N$MR808@RAc6{CANGN`Q2q^8N-BCID;X&% z;1Wq1@(Lf3oDjqFZu@H4`2H3)ruHE*81*ZqgX_mg^<{Nr^>f$TD9oOo^8xG#R%8I~ zTpwxAeCas4fFFDs{7`@e-pQqIi>B}LTk6u{Zjhj4Z+VQxyCgFX;1RTE$9*}{=Z1Ga zMl_8g_g68F#@2)B%U4OuX^P^sv==I*YKUO`L*ZILpa)-wl`Ee3T(WRb%p_PVnbQpONFu$3eLaqRGTJ+z(^3+B-7H~k7bFr;?vBE8b| zeMahgA)=9>q`3rS-x5Gnl>{!}%bXAi&tTZbytnNKtiKh)JVu`q+DWS#eUkzQv+ogJ z$mSofR96|AORrMHZH;%NN@Lk6+m0|lI^&$2bG!7@eJiPbvMA;^avwk|bp&s+RCWIV zT19`Nsi2fwrKV}jrb!5O937zJoVIsk{@Oek(N8Sxmv*p@`;btcv)}P4ZFdW#*BC0N zwMM7}Kms>o@{F7gbHLza^RGqHF!Axmh(X-m=q+*8NFwdkJsTZNcgksvyZ#WzEj+9M zib8mik9HscHjkf;HHH~He`tGyUB{;i#8IWn#q~+4rGiIVipBIMm z^T`d`{gs~pFdg?*FV_?*u#ptsh?IJemW+W?;NbE08%N0iYi0-dfJpBtt-;%JmWj0A zGS8n-I-05rq{~YsISv*57?oQ)yzXE#+Xp;j7#h*S`ieYz$D^)}`&Ay>o>hboAX1so zH8avvOHW-}76#@8BKAq@5J3P8g#k*Dk0ACM1br_O7@Fu${{X#frIu}aJ0po|B)B~k zRJ9e~ihY#v&VUDiq6=bHTJDlJMIl;mE>nXXfpQytd+C(}?lEo75PHp$NKt_tU?0oe zka7VdUpMK!ayMkZ5H;JH9-qy2v!EX6Ql7_W>Df1Q{cM$$(ifU7( zLCEZNv($Plxwy}d6oCD?y7ehci;ci~rYA^TE1pk6V3iWhDVc*aOX*4%8^e6$E>CcH z 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 Fk7IWL9%!G_f;m89<8CH zy3p4{aiWV8dXh{g3oNnVfTz?0+ri@);{(QtB>5EIwQ%i;rCnW$MPE7=$8@5kqlTGf zr}WY>8DgQ>a0w%^?a9d)?W1L2uz8tv>)92z*;L8WG}ij+d11DG7ziDLF+QPmz~A2q zgamFJ=eBjL)T4G@IKr(ufI5%e73=faQ&0}I=DO!wLo^kX)RI>~^zA)DtfOkTX!D$w zkb&a_<$E4Akz5AAn2F(q;1TNqc9zTDrIDHKsJWPe&1*BbrHnm+2`u7zYCx zz}xwOB%NqwH`2cqR=Zci(miR?7CR*mQj3*UlKD{rNe`Iy20RW)U@vKmxch$D!{9CG8Aa(O)9U=j)B_SPp) z%a`pjux#K6JAV-NQ1<#FsQR=0?YGS>?rMr>20z2Y20xmtKs)&bM*wFy@24M?3=sXg zQF7|jV9=xNnZ(EYM5i6xRYd7NP}^*jY+91bYg)L=2%Jf>G*G2-00fo5K7RiI8ZY{`ss*-B zrzX7>{4=Nn!p~h})U?#MYPcRNBos2llhT+&G1%GLj4nOLKJ&oytt?)n_EtJ$8>^B# z+1XlbR?#eYzfS7z-(-U2(J{qxmfu$uN2toN1ltZlAmC#-!8qWZVdZpL8UFywmR88p ze(2CJmswX9VLXXMM^$@vg_kibh<3 z_@|#+_)g_C#@0NIooE{1)t75{SOr})Id|Tw0y!A>d|+-MV>siClc8gMJo#o0XwnBZ z3Kxie&gn-XGJcL+FHtJ372;^Dl;Hu5pAyCx5)`oejy!(Z&?U|57$-y)ej9s!Aw>t( z`Yk;bE#_Gkw$oiimhZ&I3$uHjF_Po~jrjy)7|uEGtZdG^nKXgfki6HUYmbENIL@t{ z2g)w$E9I$e2|C>??D4>yPRYO40Ps!-cOAlV7B#!av%OF9{>!bK z>ZRK26ej3vW!)>lUPJ?NPS{|$EI1hL+n*Zqvq#hM z0K|(I>m`6$p%KwmOI0fd5lY|`!6OgdfaCLgmHUlsK1YaM4TEBZ`=bXB#HJrk^+}^> zDWtSKlUAk!Jum_i@v++?HVI;L_U?NeYcB=o9-Kw#t%k(>d?H%ob-mq9Jbs;~lCfcr z)bz@L7_lI*Im2ap4+LY*jXo4?>GHQ2RF8J6JJs{Iuk~Fl4|Buy4iBE|Xx{Saes@B9}Vp=fiU`irRTmxz&T zdkFa~L@sD7IT=x2I#Ow_K`LYA2~Ku2<4D@s-I} zz-`QNkb7`*jOckWL5(A0z;(tB;2m36s+~9TmwkC?uA;ro)GBTi6l}zq3}w|6?F+X9 zf%#4beD>1)S(tzfit_+;i{AU}si2LQi+Q28(OaHM$nEk=FX7qdUsavoEEr0w%Wwc( z@y>K?c_qgUmCxLZ9jn;ibRq@WO-m0~!_!?eNgq&Mt*I47LnN#tX{02abFqJv*uv)+ zAa>K3b5GN{d!061Z`A$;ZK!kbJE(lR4vXDpZ>W_PgIyJO`}Gf}B$LKy7LbU_gY7^M zAaX$?vFvq`^ruH`d2&3;*E|b4P50Yx(FyM^-OD*!{+TV}&(s9=r zLTldnlD+bNN~oS;nbHdePgjo+p%)~TfFnEH`pNZ?xk08a)|I3<*J#sSC~ z#&hRZ>C?Xt3RgF=6h8!v@iqSdd|Dvs{<67s8%ZPqbU1P=4yW!o3Vr+Oz5H74Ht683ywwC&W{NTuG5-M4SqTT(yFY$F z)|~kLC6O`)kX#*itMRoGM5EyCii)D8;EZ~ngAi4}3lLm_0Pr^L#ys=)yl$39 zn51w#@$9T4rs@fbpHO7ziRP=ZlXr}%j(95^?uY^~BKht!&N&B-V`0oeba>}Z_Ce(Q zdn0IZ6toqm>f~Qjx?-ZI@Cj<_8a6Th5@{Bp;zGXGB}UK~E_QL->@`fUtuZpOUx^%U z4j_vpR^pLFPXq3imHwvC%O6lvOKMi9yiZb$jEohgMnQ=To&X?#r+^L!=S}@Fh;gtW z#UFcc=XN98(HJ+?UD$eKs;KE!zOH&yX3AAG2%%)j<%w=VJ;ILT$k6(pOLaatoC|^M zcUEbmVP?OnpX}4ry+hPBRnxVO3Qhj1Nf~ciF@v118B@kELGOc%>uy{a=4(boW~gtt zLJg`%smTcx*LZuU!cB-Idz;%aH0MO~x?C1d9tV}q;?xzjUo zX2;9LaD0)v?V{+84IaqMm4&320DA>03Yv@6Y!x?IX{)XBOpz<1?2%M%BqlfvJ-qiL zu{u$|Y{$c$t2Zof06-j2an{O9XD<3PflCChVM^hErRrQKsgcT3=5p@&eklW z2Q8l(-pZY_y^eE8Ep09_xNb^y&~+suCC*C36%3T^FQ%b?mQ?KkwlE3L82QhWt3{O7 zxY^u_v*@dc;UwlO*bCqMwVyif>lv`zlGzo`se+aTAt?$g0yeCp`pDqq0ox=4tjM}+ zLH)ypo=+mZl)2%&g@XF-9=Muyri!zuYAx}g*(f{&36UJGJHNO920`tg8e1qsjU7oD zkoGM1%!qjd`dhy_BU{3=7}sOF zKmw@q>VfBDwKUORq!9I0&hc+(D(0v4ibt|Y;0n+vB^pIjpPZkP2aRM!m^GOq^^a$pn%18l$or+@G_wcLM%@ zRMfZ+p!kh6@%VC?b}1sJh}&*8QSB!TwTZakeqV7MFCDn#>(qTP&@yL)5q1UsYLhsz zh3?^_b$&mG#ckh2)KIe25gCn4f;lodWl}jKka_q%HJ^i&a;KHBjT#(9ySW^YmXlBc z-n*cVpu2Q6>difys^*Sr4@?@3$aiFNAKE|W40G|p=SuYWI&`ls@Q54oPb1M5QKg`s z>n5;I4(W_7*w(aX^(YMWIXREc! z)zh_c(m`^T$r*o5MKo{x$qqN=K zR~viFYQEDns6{#i8`fpvizq*w_s<8P(3c{e_k$Ynx`%i2gX<&2)Hx;j)&1R4)_Ux;bHj=;=uW9c@U}|pRc(4oB&KC! zxs(t7p5A@^fW~OwUv^d$@$IN~aKT;!bK=R%+AzN3)w49s&$BHLi?#`||%aUM+@V`{9O<(aaf7JES84xGbb_LGi6@IiZpt0kL>`s9pZ4NiZBFbgq|IrraazxaxcC zm38#@3W=+UjL599lPKjzz@Q8OFmcG|Nn*-#rx;K;#_@h|Zi9K--C41a@hNt$)xs2; zraHE3n#0r*QoAgQ$dQKi6dpF0WWXnoM?bE0q0)YZ%IUNHrzDcvt0K2M`uiw+;MI3D zn!P2`y=Puxg5yOEsU-BoZ|Z+7FvlFPk3F-uzP#`4A;w7{k*2jikKh7v*yVbmsr0{3%FFRs&;V~0dymCcxW(R9jn(b4OQp7=dfI5G zj*du)0f`&80zv@X=K}-IG7pU>^!{71$?(pWNHl?bZ`ewQ_J2cjsIAokD4ZZWDM`HXPr&44Bf9RkFJ2zMsEV*{QBqS}LlD z<;%@cIdA1LJgSZeT!V!pg&!wbl4A7?h_%AP_TMAd`-K?~EO=$npqke!eOwe!OI2S} z6wc~lPfRc>7>2?wHn2NRtDzp}Y>MkO zOdFzr2-X;YGXmKPK;&iDciXnMA(D1N8!PTZz~IHK`7h~Eb)Pu>%?{{Xb{Udys{hJq8?tETEH*hxam9bBOdROJe+j?4K2jEKoS zfxz$f)>dav&5frfC|$4Aawu$n0)o~FzjO^&_x%xdJ(fy-nYC34m?w?5%Dan`8=Qww z+#cfuw|r+>9S;}nkV7X>h&o4o;(5E|rO6uK9sKv&u`Q#}pFxKKP*PJtFs3Q0KKz#!#7 z1P!ORBRu0ubhpKskK+bwlUL+9ufa6U4S$$hdxaf(8>BDP4Kb>@+o~y}C`BY-nQ}5! zfyU#(9Gr3Bjcj!sX)`AAAl=Q}v~Fl7p4J1fq_tj*uuXWQT3h|zmfaL?(MD;&1c@*q z-IndRc{v&Fq-BZ4?UraXkZf7+?4-IX``e}+x2GbfSQ?bm(>qIrCm{>)OL|!0i3LF9 z9ys|;qRLg8LOr;v(9cs%3JIMzmN zug7?bQ>n*rc-zrF=W4G?rS4me-66flUv8+j)-F~LS0rx$0ke+ha3|pF8?C|AW63UT zZWaf>Apx)k8?|KAjP|H*PbBr_8JZoSkgQ|c-Mgt`-1FZfjc@dGF~*2a?>kA`xIg+_ z@C)@N-Tszp0aHs2Z74D!SlKDj#0WqQwN7$JxNh^<{OftWOtb$0+Gf!iJFYud1z}lQ%f@^rBBN&*(^chB$0qX9E@XMFv|K%C`$RUhm6um1P@bV^H+#QZL*q4 zuB4LZYGrPpq`AE5yVh2W1!UvgNjY4OMsvqwtywx~$Lb~tyreWcbdw_lmjwatwZwJS{SMO_f9UI>S^wwXnEw{amf5uypCf$(T%+q8$FhS%_Y9B z*wM`_5;8xl8v2SKg=PnbAOd$|J+rBa%*vC)VA4hIuIL4V)CE%|*4NaN+n=d!bx_MW zYJ^9Sg$yuz@`U7jM;IJq8a#bJC6cl3$R&e!3fhL}e#&K)&Pv;})zDR3Zgh3GM4maz z#Bs4|2JCG04=!*>`M}1uAB;fA>JZ0VIgb4-6qkz&-dJhGazDDMqxyx^(o0Vz&!>yw zcdY3Nc-4`>5sc#s2ZD3E&wVM>dW3@u?JkhH!iTuDNCQLkuxU?Lpo3N7I}D7{|2ajE*zFT=Uyc^<0;jgHM^;AHiR%%F&h^cd!cD*Y$RemTBrKEj3fu z$QaE@H?>udlHqoo5HQ)~5Po&$W9efu=g~pO(#N8S9>zOQ3AaK1(2Pt_U2N0Ah=_S< z;}sD>7#mmAIp4UA>)V5m1Z!g_6AKR!k7Jq)V3OW1+u0H*_1S6yyLF{cRy}XwE*EQ6 zI1Mq6@am4kZwm_%hGhdJ9y7)}X>OM3Gh=+NewKr<=D`+|n;13Lc0HD0zDr3Q@|X>B z0>P%3x4BiA9>i@@MoAg(o^*`zI|5I#vF48coc3JCxN;Nwp!!~lj;fln+LY#1g>yG2 z+Q>*e^8Akc_R+d)aA1(+J0-2KjQY|5PD*<{HAT|XO$|h}WV$s>X(?k-FC}o_(7bzx zfvw2$dUj*oByu!{(I=ntgu@Ls`=$Q?U-XTR)a!jp_(-a&+VN*~J4o-`zHyJ!fJV4t zbn-Rfmbi{cy^z>I>x)(;AYlmZF|_dOK-F{zQt5rgR=l_9PNi^Tsd+H2(lb`np$R7-oRQA+SMp zkSm%&k;TpqCxj!^ma2P;M6lc5?-I((3YAg}n8rem3Xi@pNgjFe9dD(_8{^1p#-|UE_x*25i4mC$}3a~&lMNake)=5j_z1Otu;!SV-ZqSlI(E+CvE>(zHRQBKK^cR+(yOYwdUI zjTJ4v-X>O_I-gAhQWf_;M+J72Yyf%It2%U;(!NRE;jz`HZhca%0PZ0$;k|CQJ9{n#k!OmO6l5 z9g2jGFgPr5Ja_%EqqiF(7WhWoF7#7bmQWFg$(x8=gP9BUx3y2w$XaJyUD6)kOVLw+bqf1eEn5*;Ily zy0HAfsRuk`Aa~Yd{+Wf%44yzE1I*GkllxMuohz?K#e1Kq?lxIOlNjr$nU*Qc*xd*m z5amfc=KwDm)eqS^GzTlDzyTGoHGPTR`zzw(%QH!p^-Aj3c&5Eq3NcwxY@iCcf<`!y zmh1o-Jm7Qp)_+u!Ir-YL;w%~l!1wM|3-t{|BMz;#SS_|0YpZ_^Nmad?HsMrNnI{ex0MXTW$83Zg;7(jq89n z4oN%{ljNLu)~8m;nl7CjcF}8S4R4{e%Q5^? zSGLBd_5T16gVTuMHq;06Z8^yDG|*-P4bIlJzdxd%4d22ns~*1kuSL_8GQujNyu(pg zSJH&GV<>=t2Vs%V_tqcOA50FX6tQ&nc}So}!8QmGIFI zQ{NA}Xp?tgotY}wIR}n&qT_ujE*2|dncB7&VycG&zxf#$i(V6 z(+yboK$h}BHW3Rel(6c4o$Cv=Q_lsmfcq8Ude<@UXvWaHP#mT)fsBkFCt3`{ar3?0 z_Vo5bcuOU^b$?G%Q^9fR+l55lt4}nMx=4t*$T-I2Y~ZSnFhL_&vGu$Rc}b8rR{m?_ z*+U%e0id*BNOf&TJk!ZN&Som9vOubFg&>XKc1-@D@tlop%YgW7$PUWhf4J~L zK>q+xua%Uvja2CcxmWqnOeZB|8*q%k;Bk<7_&6NtK_(+xu31I@0Jr=6BHo@iSo-tz zwN|w*G&PGEi9I4#nU)!!Dh~2+21pI|&N$AS!onJx>L%f3okn*HB9310koF6zn^k($p=)Ta?SK7Tl6r_^4jXGAE>7JrC`yDKgM-@yN zF}pmTmP-=b$>6cUIXrD7ch0@I#e2FeS}xoiHto+S0e}xUq~Pg$l|2PbeBo(m-4F^{ z0BM?DbCxT~f3|tR_}7HTf@lG0n^-kN&?2%M+yv~lTq`U3QfiswifV}>TA1EEKdgX| zHn12Y2flI!bK6li3^|z2ieu!x%~-Im_MHUMTi9-t@JAhMr3_>lV`$@mlnTywXFbZ0 zF^&is7}f{u%Lfs?IOF5x0MO@b2f7)Mk270o<7=#-KgzC{DHMLBfGEQdedY1_SMui{ zI@iy`>MWOBM7$HR9k%6ja`hT>bd(KrhMBK+`D$T%#ihWEg;fhtlfWtD`>+RdreVv- z!)eXTX*xIGZ*G4dM8I&rDP5!bQEQ&EJB8jQqM4l`GNdHBxd)F=jOQdVBPUDZ{Z}gq ze}G&axZl-F@O)Ec)O}I?v!`rUJ9IJ7R{CoktX@!~+DO_$uW*?ez~O%Q)^=air!4q> z7MBt0!s8yA0NSPMii*ygkMXn>F-=bqR}5lK6_64(_Qx3w_tvHnix;K{W#2}lch4)N ziQ0>bk0)Hx*YwrmUZ9z%=;m}t-x&qT9AE;V`+rXdUJ;VIJZbuncLPvw&ujkxggv_~ zv-Kq;zDrKl+G(zlW;vCZ`gHQ)yM|suk~tmDHR!sYEE!Qlku!u%&dav#f+(Q5^$c{=(Zghh z<+?r7#T156#Nm$YXMvO0_yp_FV04)X@%&oVSGVcP1dS}1wD6hK_lu=$lrz%7NM9_{ zqr|cXjejof+=cP`pPgcTSA#sJ7UMXFGWQ$uh}K@gGrD@(y;TKO4erZrdbIcEmY4*M zINPuiNh_10;7IY|{jHazQ75_eSMW6Vw2SK>r&KM}wYQ21nvNIYQYIw4J5JI!9AxDC zamEHTKc}YA)C`5UCUFNOIu%R>Ko0hODPqwi5uUv0Q1VP{@E`ao7%0%x#KsQ!4r{mYS8&+U=BXwTh6UR4|dwave$TrLo6g zcpMX?@_M#bKZzfRS^=u~?u^!Y1#NL|wJ%Rv*EJU8Silo)X{27o+AMpCB>w0~EiqPGCDL0U>3hG!@LnXJWtoGZjbhYu+JW=kKcc6}=a*f@!i-eX(D&ie}?$4 zWJuc>aC;a7$0yZpeuw(2Pfc!nb>8P}kqVIX*^Hydy9#*0-gAw^;Czi~b_LT)^$|&5vYX-U<&qrL=!j zuM@Nsw+QSvO3>wuq-{Rd%7KhyoP3|}trMLo02Y!*2KYqqK(Jo3SJm&Rm-^Z1u(n}J zWR4l+6XD)m;EZJR&!6e|&~hNh%phjU7zcB`qSHtK8>G9Xw>UjTRw`s#^mxcc$rS@; zKy2W9bKk%1tf;f1!hhJ{$PR0~);6xxgCt~cdwMHNHJho`8g_e~Ei`C}6X3d^Q#l`T z$0wX_7~@(|hn8tRV_%|O!_`2)bbnG3+#x|NT2D>+udD5(Nt1POp!)j7jy=z z0EOCwf~){J{{TH|JE&@KWJ5~@ZFW7?^d3}Stk!B?mFcUUX3CmNq|q0SBD?oDEKbp% zu`R|h4y`NVV`LcPL4TrJFZgXlT~ru}>8KrY9m2Qb0GUQhoWzAP>#=*L0qd zs}@M!`qn`^cU&F4t%=b6J4@B|@xW;+A(z$sGo+EQW={REJ*+@sj@j{~IyOEwURT-O z7$5_;JR%l3?EAi3Ot@~B529}(RZ+byz1t#LA}4b@XVXr0{{V;FIUMPXu9t_4PKq$d z_MzJOKbp8b`=i<~s<)1(Xour&*q(n*LmF*iA^!jdav5@*f!uMcz9#r>$i^B;^#1@m zD{D1H6I4qY(@O*}F^I|QR3zC}FSs1~f0T^lk3F=W942=cVz97-<^z3a{m1(%rA_+z zcd#=x7vSkdN%RiY1tXDGt!QB~E7 zi|w`vpe0d%2^zPg$T8}AXOMvT10A#2>VMQ;8$u%Yy4pu}kM|$!xuE|55+v&AYF4Dv zTC18$r%>(crr%UB_U*@D22`He;Aw7+n9OX?vB>&+sikmeDM{56Ra~hkscu!2@y|>j z)5Ze^@3$-1E=kXk$BlYi-m2EQ`0sAE6S(z645AA0QteL_Gsi~^w9>(eB39x+RB{GA z=erL8jyTj|=CDX4aMID)R#pqtIY2iNNpHJ)(bCr~GQ4tAmx;YIk+__$RO7cj`)D0) z(B#Y{haXdDdHY}dA_lotU8|+OMM)yY@y@c6=`2kdc)%op+;BUQ&y8mF3~`elfa>t~ z)!TmQg2yxsVwHbU#ZLqk7ZY)(Vn|urX zmY5d0inOfio23m2Hi*i_BN(a^)pCIc{{T}Rfd?mG{N0Xqtu#5aORsjxL(Nu59g@zM zPW5RK)L&BVvO`&Fo~|r1?Uor?qgG&lKd8QNbICs#)4hMDPbMoFF(QfF`=OkzU8<7# z{-M{`g^6n*wlqi562~wqpfEc`ha)U;&vW_#tgLKD2($;-9jO*6!4?w=DhisrHM0E; zdfqBo8ma0dU_CT!jeut*NdqJgzMq)Bc)T5YYPUrrR_eOmhT$JlQN<;~T0#s)qdugO zO@MZh*x-OS4hMYb5aTv8rKfLXSR`X?ywIb+xmnlhpGj3%nhN?Wbl8!Yny4M5R#2e+ zZ;^xCXFhn;mPqmB7EfiHjMp`Rt`tJ?L-kIs$4_vknX0RLil&@T1hUph%|3icGlsz3 z!*k={jOfSML@aZ}Ijo<^9D>Ks_+ z6Bq;J?r(cm0@EtrQA25{j^|=H*KPW7tL2yYX2s>2bvCt3Ine z-#QB9CyJiacodZRndXKG%B*8==F9t2B$4x<@2@q|G9#7)Girm2Z`>q0gWVH^O*&kE zm#ULDOm*#|p4(F_)RlJV9b|)P5=j#%z+evw6+u!*3^F^7Za+65#Ma`-K?iU>6{hG# zuhlG?-RoPQ)kEqkih7!l$5crvt(=I7jsmFBfKRnlWaRny(_J=MWS>K0FO=1~_avir zpnN!5-U1M`{WQIG1&{Gn5iKQ_j-j4Ph~zSFWBp2=cnU&}K|TOF-ksSo2bVmwH;`+e z_*vzrNpEB|bLsAv=!(Xzw%@LXXE>SaS7b2BpK35~>KJY@$8Wx!#OgZo+R)M)r`Ni> z7*87}hfm(9uJom&z1xs{$9>mz4qVsae;+rUsp)=~Z;ou@j%m5^@sFag}7#}=E=D$os{z&flob^E76 z6_DDBG0D_+^$pz%+6p z@0^@%><&*Fz62j=1~c#ck*|McKI3Vpq?)ofhAVw6lggfnhiX*-JgD}JW3c(p#=P?} zA1M#Wi(q#r&=r2793j;iCx7UY^sv`e^##J?4GfnHTr)ZtksEZ%_y7^kMlpko57SL` zjKdC4k~ly3?}}PWrP50;rB^ih35u9#Jx6~*_j43Z*(Bf` z=loo8q0gNh#L0?4tEYNeF>rS2i+=i}(DzQ9YkKs=)OB(ekX&L#KZU3~Cc;3*cpE_D z1c9sc(D-m9nf`a{73*Eqqz6WTcPkBjLi&wtq@rqyiYj-iiCT(Dl!)Z1loCvY4oe={ zJRjRzSU*z{2Y@2f-OoM!ctVd1N098Pq^6eX7CZc*o|x)HwB2aBg8Oqc_NiYBGD03` zW!sRBKP!71zA=R-zZ&d3p&X2r@!1CcPz-Hu%{=#8Gobp~+b!0rtX@(YvKDBjl>j5- z4&!!6=VyEl2O7>8(n-~hY?!5tbFTjY1Wy#!l8SHNQDePz9VNQkeyNS;j20Blg(^lF zc9VcIq=0$JCmMIHNi(Jn#d&Q67c^QgroGlsR@@D`>?`9)&=;R^Xu0-o&KR-U21Z9e zVWm2pt~n-bSLpIluKu_3yA)JfJF8nCP+e-KhRIcNYN@JN)j5;-jkGfY5S~J=20`zF zdCswVYv75VBp?9cUfiR`cLerNu97z^m1P_f(mm+Xg?EubDO2hLlnC8|aIN5;H4;Q6 zm}WdwP@DE}J@2wqhnXDV!B^c+(oc1_+^Q@VN+}@p%CggsRguT$ArNI0XKJ$Z-^QJG zOLDQKk*%N&5ALC$_X#B>s;i@JLZb82LY9I-84SUsZV6Qkr0xuI#IYGrayx6obts zPR8kZPsxIScnzQ`t9#{ELL4d|F&u%|daTRRXxB@f90D4vRdpOBO*2HuC73=vKtHb; z$D9&R-$%lKh`}JbDR9>T`^V|_TBqso>C1{Wi>R*j$t-cSQcp6mf@s&0kCFeD7(nylA z-X95$LaL9=l0B#39~vuunaA2l;skn*{@kfxzWXH|_od!S(|uL1G_@5kA{9?*j|x?{t& zf-pvTZ=Jt5(4fsTx^n3JG96d^%5~8O!DF)2oh@vnp0O^=Ls2-Xgp_2Pc-~Y4w2sG- zz`^56#WqLVb+;Vo(m^EcXbcuT3P0(dlep7V(#=(8y4tBg^-S?QsGOsFWh?;aIN;>t z_10q}>G0*h4e*UXU6OnIdm=T`fqJ$|ezm5#3c9GSSE|~HQld$O(y2zvb~wnwf^tV3 zax;y3C^Jk@hP?S(@BaV`iM~+}(#ssl(sEZpdAQsvWtM4@rd;}N`}U?m#^q3?WDIkx zW=n|II$fAtZq%4H3)M@7=c@jp?X9PHD5tTO zPdrrVwXsr6AZKi$+ya0)noEs(v(-ZlT`KG_y)NS`BO%_!Snx>3apxnpn(5tGrd~?oN@x{h&hmr^d-> zjKu9C&))a;TJIL!MQI4SYMP3kqDl&hD&u7@3X)?+Wbz@$7+OWcNUZoQQWF1YV7yVQksrgQlM2dn|3mi2tQ&83_RgL#*vCwW6vua_b5gXM>I}9zTSYw0U2CC&of)MlGU_uBC|uZ5r^Yar`E^iXh% zhlm|W+U?O-8%IgkPeW~}yHps~K=D<25*1b_Z=5(>kUR1?<6H6LJ~*}L&~K0Gs9QSz1D&HZ`D`S>H8?VbzRcqK}!fUzmEx;G&^|q4|&NTvG)=)sKU*{b9GGiwqJ9< zv0axd*(;-AwhE^DTDqzFpJ;-*XfKwUiui<#Bxq(;{$4U2!*;Qkv^P_ah0m3u5Q?(IZ zit@JH8c|Kt>Ft89D@^n@_-H1IM-BK`lsht<9x;QGH)p>GP5q&Pqfyy&fCkfVrsyH* z%UwmI_=O+i*-FA$0NmN<818u-4CjtC%QGaG9!PQCTImG`4fh@CFwb>Cbgk*z)Uri( zNbRTj#Y{ka09U9XS3}auyKHM-&xrBlE*uw4cy=JMlS$T>v(>P>ndBFp(!mjX9hHqq5_h# zV{zErHqxMsk0U-n*5*EABZT72XbaPS#7|(T4#{C>x1|O3?a{+Mrr${IRE;0nT{4}B-r{A|_?Rxs{4i_V(^%!&Q5z{ zSPtkTuJm8EyK-7f8b#?mY;V0Q+U3$WYllSD+PaROrio`tWk-pWh}}#3otXQJkWP5W z#&whQf9&C~xMWEdsT;tmJNw#@Xr_%<`%iULuv{s+W?Kc`qtqcKm)h(}3$;1H#u>Nc zk}>zwy$dz@*~h2hpR@hzBYoO;+PN#7$iobe7QfHshN1yDr=+F4(o@N`^HWGv0ITi{ z21YT#$Ml@}*SPwxr)0#|WJeg)s9g{^?Q73;Y?n_VYo181{-~z=1TxPK0s zPq}fqS$DQD{BS_qz!@O^hh92QuKLC^{>{{z&h%VeSHbSKMg$qy;qbVE(|mevj;%jW z3>M!?E5+8O;HWhU)6RxBUh59fqXZ9jdF}gaYbm(dkp}qAE;pxcO)3c;FP-C*J=7f! zY?eEu6JIFdxXZXx%PeL>Q??xkoM$J$BaK%Bp+}9Wv#>br_kbv^(nq4m9Zk^Ebo~NA zE2yuvvrQq1DZx})P;r6?Ex~T*C(e1(SnkDwH|dF_->1DUj(XdbQ$^D=T9U46=`R$l zjvq2l>O(YYeZylP&JGWJkbGl0Po;RmS3fDbT0?-|_pP>0NauKV!@sh6+WNBCI(Eks zA*i{>2$iOp{Xs~}klTpmw*mk-$L2Xb^p+dqZ%u3sqbozNv2EQDeN_w-I!8$1WYv<2 zE)_O9syJ^n*5M~nv}Q;`$aYGld2nz`9Q$@;apzi?5Jiy`Q8}%1#@deM-sGO>t#BJG z{{VjKtD{id?^4mzwZbVI(L(GKaofhu$~%BbCxU!uTKGLKIGXsLbw2jK!_a#ulOfIE zT2Ea5uH9OC@jd>gFT+c*snCWDqyhjWZgI7b2b^S%28GkQo7xNvbyb?WL5mK2mVKl% z%f~*6h5rEQ0?}JAr56E2*h%E4oeO$|j03okKsgu+Mtgm=yV5h=E-}dj=_75<^mkd= zl13;4MQVPUdsWWKXjp40>WYyBkr{IAgewrLMi=sdlgR*qtlWtPYoU?AN8#BTe~SHm zI96pmY*j_tw$Wcok7~J7nssEHM8E$4NT7}vJ+MIb6P`IewS)CGAj*3Ko>`<3w|h5b z6_d!#n$Yi*l&<={HJYl~Q`Iyx)K$V7MOdW)FiBPQww$tpRE!coKuOkCQ<^lHovN#P z^jC$(Nc9ojJuQD$E32T6I&0Mw)h09!;-O2FEHD_1;e#B1U=F^Eo%IeDL?a#vhejp{~2n3qb`nMLjTV41r46IT+&&oaEqgHDmNaiaY##?6(Z;Qxoa}s8&mpP;z6n-^wS}50Dmr$OqM{Tr zsfY5caIp7#f2dc_8qggVGInSVE*t6uj{I`F8cTKaNfUIw-YTTCwDC1HbtWW^ zQknHzlenqv?JdXK<6fUDsC6s`Ga5L71Os=z+bnlLGUYFo<%PQdHw1d9OMACPEVR`h zkD97gSd7v^u(Z2#msU7c%IC)f_Ru=FSI2%pGk_$j-uNEtS~il}7q_aayufa>bu?F~ zt09pYi2ND?#v(UtR_7q(AGaNV)uZZI@Q9Z&qmouqni#w zDz~P9uXE2i7|QXDYfqUxIPuA+Q&=}?;CcmiKpOo(*tL5Fblo{yX1Q9a;&q6JDKRp7 z!O7eMW0oU~cg{XFmywSP+AZsV}!_3Ek4&U1sV>ovG}RJ<6RJ162Ec1!7ku zX9NMbkb7e~6eGrWhgtv^W1sM*Y>m_&x3$Ml4$2!O2~|-T0%)ZTAqs(llp_NrfgI1# z^${4HFd?y!GQ=J*d*tb^tproE?6`h!7tB5{Z_!b%+&M099Z7A6@V1_t{{VO?>tto7 zo_XlRfJEG6kWaJ@>;mU@bIz&Z&g@zv_kjL1J71!V_!~5#{2i6OkEN1XZdTfgscE2w zqA8^OOof_N94Gnvy~K6{#);A)Z&N>FW6W)g>bCMhJA>>NlcbEgfCt?>dM;S{IvbS= zR@vS<*;Cc2nWJ$M;{bwq@DDg3=f1p`UFwd&>P^6WZF@_LZQQt3?uRRN3XgAETzabG zd%0IZKT2I})pMMzk<>UXvjtQI9H|^*k8$#K?qGant!6t%n|d1o>=20YTrZH6BE{9V zX;o&5j-r;Nm?K80Ss-x4Z*s(bb;0NU~gQ zjSTd1yvAC2f~qQx3W74_w|+(KtT**cOoo{xjjaOq0{Gb}9xUu`nFi_oW~;YbQLZZm z3PmcYi`9&_&T{Oe=P&tz0OuOZ>$n)Wn9%hcNu8#9w$(MDaaF(OnAG-)BW2o2?Nv<$ zHNMwpxLTTF>eIb8^o6in(*)&2fwFQjkG6H>os{f=e#ypO%I^1a^xB97BStE%fsEyfWMN)GRBMAPR)xm z@x{EbNGt$$9>drsVwAIie=9xpjhgLSc%FrfPe$+vw`V3LDnaeYBLj~FX$HZP+@`WA z3)!cFcCf81?s1~f--QCtU0-d9M0>8)5XB^q0#vAyO{=uT0R)#I1_1c%Ml{YhRda}Y z#a_dnRSP5nqi&>WZu+S$!ip5T(b_F_FgrXj{&OHW1)X!-3Znpb&l(h&Bs;^%d3Ukg z5B;Ih4FW(LC-DzYP0<~O^ob4XhMJ?&tRu5|7;JEXzNj~ssS z+I05sy&#tFx?*%U)K=-c{jTja_Tfx}XpIy~)e(>m#cYv)#C&kP>2IZ*mS+9<;`@S@ z&3SBslfvos-obgRND9zfW;g+uDoG;uJ^TXPH;kO)8udA|wSmGzz$U%>erS$siM9`t zhAV!eeJ-w|YkeHG4|?^P7G|9>GqwRz`Nl{X$??ww>&tXruyfns%NS`pSma-E?hth~ zqsnY0jJK+l>^inPje)JcR2bwV z_j`|Y!?n+HN`#h4{{RaRSe0fGkm(`s9^=LX5sdqed!G7ny-?%KW|;X-)^7LsRxzCv zH#gfk-CoSSob=6o#r^=oYTUqD+iMV9nFmGQ-V3har$f7Vi{$}YslO*d}U?{f7wyZW7zNx^|#7l zbbm+EP{jqt<8Y^fIinKFsK?ZavBvM|3UTK=ACs)y&#ZC@EipC6^Fkxa6ARGlS_iVh z3Z$t#KB_f18x_bql0Z)&fEZ^O2hN(1^#>hC9iUgLqlp5Vv~7o~>Zq)xic>{06TCYT zv9Z9z55G9U@&=R3Y*<+0ZDh6QPUwOm_mHyQ<))JEX{#fusFj*!DIccNoE&A(1Z3?5 z0i0_a2QASPCLj*>;{H=lEp zu-l)M(2Ch~2<%VvT1xw4oo%*@`g$@`O->__E(*Maaez5I9E3a^pN(dSGFdcpEWeIT}7$qfhHnx1cF9!*>-b{%e3HrQRg_;+>DHqJKJp=?md50 zUk;W+zx2%wG=;5qI^W^b&9Kwd6lC>ZxFip0_#SyWrfw!w%-7RAtl);7JNl*kKwYH~ zS!^`ce^Fw)rOu^GhE*#Wn+^F9N4Id#+@Cu1F~bBnjl{~3wtFV4oBN{2s-{bHv=Exq z-FUX)Lu|OxNNk}%62ywYN6GZ%87FeE&$Ny>Amc&Ec3^Z-%;Ls0?g845JAY+K^gW8~QMM)( zqV)x3OT|S|>i#4y9Y-mWFb;l6^WQnqq57sAX?zTa)0J@-dmTjc+IVbhjRG(7xH@8M3gPrT3kLEp8wbVSwV!HQTeMqP-RCKnueRCue z!c30H4h91N7!$$pGv^r4vvA!kA(;n2I|@p|2>Zs=%XPlh9iHi9si&rrFcP5>N8b|= zalt&7B%Jbj&yF>KAeuHi9r2F{RTImHxI~rB{uFItvc+(vrlYN-wx-E;z#<5~fG6_A zaqd1!<3Ae6%f^ld>y~FZgZNJ4egGcbmD=tJ;Z|OLnZ-IwJ=Wb(1r#y_U9s#kp?)8n z;2pT+b_cd~?7CDEi1r&*k8rwXn-pn%>DzBj2;)g2x6N9y$ez@U5fS4Os*D18mh%fn_mm4DTm$&OGA)XE-?c(>+ffc;NA-+l{$IY19jX zyy`nOhp75;ma2yGB)f!J>H&y>A!2rNz+)NU0na`3t}H%XQS9T~bMTg$W*;forDeOI z+Q(Nm%ayA0Ni2+H42la7xekGHOOus3IL~cqbr@!b22j#m>gV)Hv=Di)gnE;us3jdoPW7@V0RYvXa$OTUNE4`P~h4SQ%fg;`ZfXv6|YszpPgjBTw1 zPWix*A0?0s9IzbnF{(oo@<_~h8i$sgF)zQw<%ArvZG1w0o ze&FNWM}dwrojzdk8c2CzXum37!ym$cX1GyZVMruVRqIJ5G?JuyPmQGD;g~k!GmiSC z9a9O>Mc*vcS zylBHSt_uP4NlR;3|g%C(lCl)sp2;YqeL%wbe2pm(-FH@}z+>caktp?D7vB z9&@H$)M1XHoq{L&0kTkFWy--UUSaYTmr&j9vBIxvw%ptXX;P_VQBAdY+5sDbf^rXI zo^hq)>#sR8V@DfHSzGUW{{YG$!0J9A*BGymv&Hcstf;D}u8OXeJz(-X1x3g>2aMok zoSY2gkaTEegg3>>XwW&a$HHy_sqbhMMOtU{CC8-cqFQR%Dyh{lBWjmbSfmZYux?lt z!(?&+(u1eTJZ|a+5FlyOdw1jCb#TXcnhIuH`i9+jYPhU3BT}(w7`#jpqX7HVH!7g9 zAAWJ$TJX-tNM(7_Dz;0^0Q?mfP}E&3Qd&`Oq^YHjB4e62!$v}2lg=4OBX57xOGf6( zHANw%c!kCsJEav>x_Hf&7}ANY)3|4-h6es@Sp=utHG+o>eUv+0=JRd5# zthrQI$xQbuC@H6BGXDS}b{PVbf$rltBoW&OM92DK7j+2c@!atkb4yKWB>MY~$c$<5 z&GMIhMN@39nw!w2YM7>Ga+5w~r-(N!!@kxdf^&>xSyFWgKJGkW#5intuT<1+Bm+t` zFj6GRQAyNB*H!?NFtD8^jZQ{!?gJ&)0OyRH_8zM?7P*d;v^1WKUW0 zp6P$;dg^(pKMW8CKGEF9Ph|HTlfmPid7+B!9w6i;9OJC|B!v%1bgjPiX{@1Iu=Mkw zlA@vZ6CO@M9PP(!@^S#y)bcVse-YbNTC1Jtzd*WA>isuN(^JI--kz$Vp;CfP#a%%= zRB@2lBkkWkh}JjLxX)&5pH$-4aqmlq@Gkb_tZ18Xf0R(pW)-cDAe(D#H z>f7B}rixhgU5@olLbY^u`)ypZG=>Pq<1m>209vTvlY#E=NatG3pTKYK+oWxE-1on8 z-9d!7?3EoiFtW|Q?`gSPYmtI&f)Tl~oS@@|JGjZmKW%43G&@sC`S1PIkml_uqMPg0 zcG>FbD(Y@%4AI9dg?xD{}Gxy964tNLLIeM1mPBR2CAa|Y^Z8GX$F`gMWMd$BZ4WY8-(_&ecM2`m(N}VcI(TD-idgZFR~$Em z;0!Bax%(5K^?s)i$!-o<5fU|9cQxDd?ynnlFuAFH)t~+=>4PNoboTi&u_MM9?UR5= zBc4Fc2*4j2e;#~skK*LG#_(O`ZsNNe_wI_`jriLP|aku%|zrorc8_tUeU^z zem|?*w2Yi)>!HJkN*PBUa~HaG#Vy>u#w;CrOzmQ(j!S`{tVp)U3tMIFni?aXQ^Rbm)G ztr+ecjFu`r*vH15#_Pu#0hf%x{WRJ;1U*iz&96w*y+uZ0wbI8=QYqcuHv}}HRQrMN z@P9#$b-#(3FHvr%4-A|8r_=kcA>5=tQvElErkYEY-CR{htYcXt7%Z6}kUua4j@|Gy zS7SYx&kvE#@|xA^px7g1m7nRA@|r5&;r6wjf=JaW2Gvn^UG)K~UW>JOMtT5f*DsV>E_`M~d`X#*XntcoW>NQqaQbDtbvo zic}&000CpcAL09FARKBj`gA!?Gsx{VwE?!@DD$uaHF|!i${VC~^r=Tl1x3pnHaen~ zRaPuXJ=8D@$DJ(>dtN+7M9@a#@a~4$H8>wcs_Lti-VULX=SLGm)6&M7Q4tYF>-n8_ zX1ZBRoX{W1BYXKM`E8u(F$)Dp>8`M=f=Y5tYgGOvx6`xj6Uciz75wpz zK*oFiy4J+ckW35QOGhKJP_jZQO_rYNtL2vc({(iy*{80$EUL282!l%Uvye<=@JxGi z=Yj{E>X2s$n^^Gz(s=EDtD67^ONOJ(o_-R3S~~W*Ra4vScgtlQQZQ)d4u)240Lyk$ zk?q0TpT4y+Mod!}@?sGR*p5%JuF8*nZsMzLwAZ>j)m(KH73#(nl33)CcS!hPHs`tH z;3ypDQPLXt^W$N0X>*4PBzCro?1p6PYqhrLa-)Vl_S(pjrWK`cPtst-vySC+%94A5 zjy&#tLD%KSkbx6i8|%KU`9A4)i`Xpt*41=$l~mQ8N7I$!p&Ft`R*!L}cgE#)VYQHs zLlKa9=S;bdVQYW@k$<1|K{hsB`gXIVZt$hhS?(6dqLpEp;mgN38$dV*hXWnF0rRZ> zrr7cr*3IOj$R6AyKq#BxH1Ag4m7%M#%Vg?k{{RAn$gM1!Jxp>GpeF?G7~>pvJn0!D zjg(<_rjBpAVa^(MD?s$si>W^ga<|gm}D+YtF$L zHR4*z6}eSGPg5OTNld(%^b7k)1G(RTKVz&%M(D?hBhH)djM!y&k)p{_D;{2*mWI=9 zvCnl9f`($~lF_R-v^H^(-?;=2I+r`A&chtjmWK)eH-p~25@RB4Sq?vj2_F0Ioz~8b z>6^_|R`%)zc_dX}vL?wKZS0Gl$LBxy*OiV)FeLjt4%QvDk?5Hcx&CJgC>!cGPyYbB z^fwARh^pEsi)@OjK+aT*s3dYo_w9cNBay9a9;)0an(587f@_iUONd{Z{YTPN@-=-; zGuP3=^BFx7`D_-*E>3p0`Em}uCr-(VY+)l>IU|Kvut?o6>_PmJ=3B2^*IudmZn}E- zO4Or7GBUe7vyW-P1-ED8j(FUYIhc?)B1VGOU0rtlQ1KYaa%J&08)^RlJ1xER{>3HI z#Z7&?I;AU2S}4+-MCu3uW;s9e3C=uae)`Fu>lj=sA)%}s5oX82gAk8vw9DvkrZ=jd zt*ovef~BRVkW|I$jrozcZ7j-p;C^6xvExbM<}gd+%4#k5`QvU-i(KbvXx(WDMHBS= zHmUd4-4>vh^r3C?##j|ja%A8WfIuE|jSHva&R{1Qo+EV=ZrJ>MD=@~xqaf*b=lqq( zn$XeNZUqGuO(d!zt&oVM^#RDi;fFZ{9De+3A3AVoQF9EK6DGDl|IKQ?jPXGE8(Vmlb&LhV|GH)Frgr{bUky(dr8exT|h zW4GMymle2%ZS=B;!X2Y6h9et*90QIqj(OIHOz9&MI+T|OZ%2CCt8YY<+K#}e{{T?# zR|@`|xL&`9(Deo8rNnPfF4HfwkW&~4*yoPq4C_0f{STd;gmUsDsYB>)50o1l@`(8X za1CFa-D7&{N^YZ`3!h4@RU^|QnI@T|NM_Hbx!ax;uHo5obB{WE>ReonE&Ee+@BI=j zk523~Pc_YYA7yuHL^gPh-q#eeM#a`Zn`9+GE08=NcRz4X14;C*w+0dLWZXNBP-Mq@ zfdkBa(7##r9jbdpyVA7Zs4f#tBLnI03LA1CQ*LlK6$OFhV*n2Rw*%`KSm7IMpLFv? zjK?f?&h5YUO<(qZ>U;6&n(AJrzt-1NQ#<-|Hr&FYLWFo!@_~o6j5a%y<7cGiX~%OP z+_=}_u~68xJ5g~zD~0A-+f8knE}y*5O42}TK$*4)ji!QA-ula~EVPnb1@75X1ohCx0D!QA)^Bm#gzZ!q{N1}AGsn=g`io?L zi;VkW`5Sy4(b@+Hx27(&ol|(Z*WG$zp48Ew=T&|!C-vJrC~g@;Zpgtt3C;$T>D@o= zt}SxU*+ zKwY8DHMQ;mtMs%#gj03vkEne=T@|w5^#ShX5Pl)<5-W)}44BXWF^uDrk;ll_rRu#u z>?1=P;=BFM``e5C6*Cfxr3PXnIAUSH~2BW#d5fNgKG zhPurm4x<-lw^7YY8&@oqD-yD?D;OZ|mjvSg;{)~Av6HA{JEa-o0j+5OkP0+YHLvrZ zWW2oFx{ldz{Bv5MxLh1}*f+>$sPGfKEA=K$btCrw~z9(=}_TX$hRm>HBh-1o4JB{rT6J>D^Ww=XiK7@g6re2&Rrrm3A@C`@5{4 z^#!`;rY;vc>Qnyop(zWt#f$K)asiKzdCMKh-ItNzhV!5fk@82pWNdwQUEpfkL#C4c z!$JIdvFX03uTatS{8b*9poV7jYDPV$B$C8u#uN~H>o+nSTr6XmlO;ycXZ-%ijT=>_ zb-K-9=_<>G$EB_J8mfUV&9&8`ntEm))htJ~R15$?IL>}T5@zMV==zUKXnSk9E!h76 z!iL8j{{XmrhA?-qH{1K8kHucEDz8l=*;Fz#l#t1Wg>$sGyCnA*Iq%zpjU)8$P|KSv zjE9mmorxrJnr0dMfz}6dzI1m-btL^lwwe0FqxyQp(LbeGcdqnd18(JsQkSvItlm!j{n(c7vZx#`whMFiI*hD<-wq}XCBB;@unml> zlC`C=SZZo%CP_V0O#yaU7~!#yKm_OWae{dm*Ms`MqxEytn*26-G;c<`@3+Drnzx8* zccI}7+2*W4ZWl@zqN`{043!k+y;mfuNWba>9_)ZTeCZhQvNIb7LoC7{cYNtP>{S@@ zTug$Gr)7VAp^^YEku5Z$C5D^Tmne>)ZUlc&86UClucc1}j(nLBv^lTcy|1zZj*+^o zq_2vWvJoOjbfFC+`b+~XO_Rs_2QP&u2t#nlqm)yf-k`V2b^xGTD0zt{oIT_BqSJEH+?04ij=gRWky^zTO zS>bD*m7%otUr^d0qrOc^YIxQ+m+<5iW?Yp~-yfF*a64mD)l1{aF&9uESiWzye>ETu zQG?LGPD^dINpq#Bgx2~I4@u*ZfL187lfWc`Nc(8rHPT?|@c#hqyK3y;f$oZ0DOB}0 z@Fz*B?)0#lY2(BtqZ~<=!EQ#}jQ#Kql=`!;^%E>3rn7Pl@27QVgW(c6NuIT)`f<>f zIWKY3Nl!?jB$|3z@Eq=47BC0t$1B9u zGTdqIt1nGdwF`ky6w6Ax8$HJL{J~TXa;N+2%=CQG8{X%VORJsh@a&OvfD+T+h4Z28 z&0)DUN=ZvuOd=CawjI+_lAwUgk81;t-ZhPsV-m@dHERaN{oU4tWx=2h-qxGcRrfcZ zIIfV}VE1f+V}OdXNy!og2QBUdfN(zjwXO8rMof_-j@$Rz>(Mvj#_#)sZ(+JV>D%qj zb9kkxveME)O%OE=>9LWq{txLZ$!{3R;At+G*G5ij-!pd8Uw6~osEQn64}}prc|^3` zT6%W-Y^Sfee-oprhtY~?qQ|T`KAUH-05B(k$Qm?#KNZt6sM~`h$(N(S1RbnFIo$+IEKc8FZzyH1h; z+|}Q37M#tvnEwFcBXkvbt9zuekHPx2Y+6{>norJDXWDB~-=NF?XlPYyH5JPdK2Q>$ak z_OTpiY6p~jV#fmMC~RuIdm;{-hp26EJg-krRa8iA@sYZuVZG944Y;A@djd!uwSK4c z_FM8iOic#h!u`vrs2<;iF=Ps@dEzT=MgU8K}%NEn|cs zIlyP<3)qD`_5ks(Y0zEay^-<&uh9w_+;Jq8)nUP9$!TJ#s~0cfw-_dlC6bo35F|#( z8(d(L1_wFZG56NLLU6;8)5ta;jxUrMup*bI2;SlytR0PPu4p5?SIJEkHS(^lX%t3@ zAu5wD8G-;$=I4TV9rT>tK1t;{F;Ixrb~|&o`DthMIAC{#0WGbzztw5)q!;VW1k|=_ z>uhxtlR#vh>Lii92?)r;uPvN~8+qN&j&s0kgdb>ayk~=sFk8=kX!Pv7c4SQX4bYx1`W;FDsde)YvnaBw19_GkC z-QIA#pgiNZoa@bfM>Z!)hsK;8HU`4Yxe_Na-@9(_OsFi@N=RV!WT=7%bdbbmK9gje zw!jWS_&xN$MUlBO?ZbEN>YEaPSK2FdS2=5uYboiu^`S-}hQZ`xuwc0xPaXX0)92(u z=Xh?raoJkICElsYMOSc&Iy-#SFv~XVdW$P;UU)(bik64FE=Us)9IH3K*&csLl&0{H_6Nx}5q76fN2G4Br>fYV@+z<07rX{&Vkx4%bM zbmhugSnYI@BAJ<-ucl*64{6W07|+IYje3u!n%s!wl#&{1yBZ+fq0K*n*1g;jYZ-lJ zpy|tW(A`C3H54*{@w8L=k)r}m`2CPLB=&1^W#gD@3UP#fUrMB*qAJQ zEt<}wXg937ey%&JM@?R7eND+zGB=h`d!9H4kN(g&<5|(dA;=GDC5-#s{{SJ~8SzN= zXp5Avpy~^}SJ*C>3yt1MDPvgS6*2~dfV+08;6#y}mDGU$q+% zxNbHn)nw`mT^%)1tf++4s;sKdP}`$&d08bqj``X7cpMDpO~s7z>pT(Ln(zMr30a*^ z(iJ6d)QKpbuA&Q#w&xUkVD&#VaYn@cSl1_h57=-!omZ#zm~f_zy*;JOc>wEf^?kkn z0HT+-dk9kN)>LyEu}yDjR#*g;K7-)Jj#Q{$JOWUDn!i?X%goUjrrG3H-yPLmBdf{J zWX88t-g=%BNNLuPifycw`x0oz0SrQumN;$?9CM)OM8{1YBYxoArWd{Sk2| zns_yU0=(#Zj8^z=Z7fw&w8k|JHrEV6Ic7N^oc1U4a(L~m{2bY#k@+zSG78*!dnTCj zBN4yvCY1I?hRZ$Q@beo%mTHy~#O>R-84ZkZM`4WXc=haQe(Y@@{zZGCmsaT7^;t(|M6}bb&OM5% zn~@|+m0)thLzV#KFWa{}PP{kPjFIjT16NM+njfM&MyA@$ok!O*!!*?qH0iu4c+5eZ zsAGtOaoB)!#z5yv{VD`GFLds48clL{y^tL0Koz}PSJtz{)z=Ej>M0nLFQ@5{yp|wf z;zb3HN81_it^T6#5?GJ$G1lwmk>dci(GNxR-1m9XVs8lwwK!gl|wy44oejunMN|c zai5)8ok_zT^5j*AP^5EDHiPfhU#7Odz;yj|mn!O+OR}QQoO@yi^r=4p$1MOl7w6a(nZR+D`hKXhV!f>o zJ;GmJsn+FPRW!_TPTMpF{>Yv=_g) zAxXCVJLuk{q_{OTs(Q<^6_yhd$rq;Ou*_U>@y0kf?VfDRyoZN339StS&3hX5_d&;i z40f^ngmGfCb=ONtReGzT7b+-OGGml_pf~}YvPRa)2jc+zX#D~BfXyt$+T$X&vA;H~ zBWM7HS#au3w(1(mqOR#m$gMEK;buj|F2lGa62Z3NgB%>3V_tWnIrw?*%d6eq{{Va2 z{#Bk_Bn%zXQpHmHqob>WRhlqXE%Am70)|xx0pE|54d)t>wcfr z7G{?6s*|azYpz!tRZ>`=&}OQmiE$nyMjy>a;h)BH$Rq)gofD{a46JV z-sk|>vjTwNy_SNC zcpYP5>TjoW+_RqHh5*0>9Cz2B=~_B$j(bChB97PYm~CAYmj3|sIe#@&*EhSvQqsxl zQ!P9jJt4n4V_cAl=bz2nUV|U%94EKO0O=d<)JY!D(gHHC=}V0QQdd+}J5eDeq>dE} zBNA}!#_VJPi~+~*t;|oTG2A?6w%R>~?vZ3Vko}4qms8eM(n|u(Q4*%(BR(B>V=I%- zJOvperFPRh%Ear-S6kZ)JNr@e5y!lzZH-5raltnkvaB@;ywO4|Y!EI11ru{^2g z9A`bg+6?K~FgN!F?ybhH_wV}i?5&F3QV@6ul9m||(cJFzl#*MiB^0cXBIR9|Z~-yG zu10cyZZy}Aq_xfiQSaG1w93k>G2vdTcDr`)iB5 zN-eI`eZ5f*a|MV88+o}}8|ke_@NZhGkkc9vp_DvBWGkF(WgrY35s&<79GqhwL3|;) zYktU3V~ECx$s^q~x|a1l=7OT4BCQg-y*sH45f9P3rhVztFqJ7)=|$!;sU5WW;l*1AKE?5kTZf;AQR_a+Y9M_ zkBsL>A#3#evOw`fE5sFai!P-ocP$U2meTlAqeqoa7+?01q62kjHP%KKjX>?(m-B$o!P}YF(%5tTyT>>e|Tx zcoiB1X)q_3BybLPk{FSZ<4Zx+=4(SsT0!S)M|)|On6`hcbiG++sI*iANWd7RT9(+z zGse~j`GjP9x!_~;w?K3d;+b+>cX1wF&*kWWBlQHHthy|F=gn0uY%3=Eif?L8g@T3{ zKa}>tA3D_OtZSe!V&0GqY8%=0QX!&WXu98gw9YBwTKMLuWpxT=-mJsIxX#cHdHDco zh_S?tjk5C}qO5KsW%Fes+-fHLV#XyAHX_eSxK$qB0APkmMp84c~+gSeqR7Hu3=0lw4>Mj+heaPGJR%cI{ z!&hYvKdC0BN|==+r>ta&o~E{)K%Wl3nJ_{O05no|EtdNM( zFU4fbj1ei|0tWz&a5&BevvE46TegreUdFu;UI;e6^!=)W^>?RxeI+!MK*uC9qm9D@ zhE%}Zo%jQf0JlI$zd9FG`b#f0jy*F?FdMT&emfKsDB;PI)mi4W~f-b zNaM_E0~X{s-JBESoixljfDC01#UBs=!a`wvF}9>})zryvsDkH~4jVq3!T=jUI2*h3 zj33)dGa45VS!ZxO+@|5y*3(YGFfOTC8rD=ul=X1Q7~)A4&f$T|o=)6=2aKI*x1!?X zO*HYimq;nrcA>>0eX2)!iP;EQKA%)|9U3iLThgO(jirpg^H+_nfzET1N5JDcTG;Do z=e6g{TdFe|?IK2i(ITFM)xs`&(3Y z(denVTm9neOU0@?^bKv5r7)EBHYq?059KK!c}mz~ncj7b;X+qyG=)LOE?Q++Xl!*Px1 zu61+LR8PKWA8U|6BO!(aY;pkuJ@t*37fpi{I$sjYfI4^N`vuWBhLDxLan_dGZHl7V z(vpG5E5Pg>%0T%m_M}B`)u*Z#qRA~33F1=xN zw%0dKUM*HSCn>jWC#vE@pXNrS@;|v9vN5kch+h*lH~72%0Lm;ZEr+!{ z%m;T0RcGB#nYwD4hT8WuxcZQ^AgGC&5>-_D3Nbu#e%L>4EXaw4*u#_fAnDt6ivFTg z^z3!=QCqrL3TfktW}N>3OE3T&0f+p`-#z)r)^-kb2QEBm^$-EN9l{@kUAR?0OTL+X zO1WR=pqHiZv^47N3~~?_Dr^GVSzFxW3NTkVC!d{c_Bzz&c~Xwcp)ma3yu zRamf71ZsCM`}YCL0#}@F>_L9`6^$s-^U z`gYQc$Ir*eb8^EpRgEWoUjEANY$NYorB%kV*+VTGy(Ds0mD>^Il)xacTyeq1;gg|t zILwA8vygWkl|Tn}@S5ExaF*#+K^lndmdPL`rIJ5-ouqA;9?g;Cj1YUBS+OdP?h=d zU)5K*DJb;EM94e8tf~OfH*@NWgI)?OEu$uV+HU|p12SWqBSM_IJ^;8z{ z@k=cwD=Wx`QN1E&IVX$~2GBXqIl=L-Y0>hcbTOS4Tmjq;{{Sf&b7N-5lm}7uYptb@ zD;3V+U0DmMNS-$;Vp7|RInG=#ax!@XjblUBgp7PquxSoAZQjC{LdQ`gtKOZz-t6)+ zQddbL)ny&tRwgtDYQ@iNA9vsTX(N=;Z{Sy9>bcl}&|xX_)15xuO#r-9nzc0&6pCpa zC#dRIa)5gQ@x~4S&wXM2x-1w(l4>!!!)teK&5z`cSVO3$Rm16}EsD~$O?H;9G>2h~ z1^!%Q`9N?>ag(1PuC+REP{fg=!PTkWqLCWJxva8A@Yly=Ygo6&Rx^n(V{8m4IQFWy za6IILqI9gWF?1krex8V+`XMVcHIdiDZLM2f6G;_O8C-f`jjC9FW!gpyew=8XKciw~#P>{GDE2C8Bq6fe z;EtLIuN0Lv3{|RQA&hdKKtVTlc0GnTAKOelt;c4F50l@NS2g^(Ic@&{P3o;M(_HEj zYO<-mIJdhpl^F`s3=zkVzwM>J(y`7fasfK`ulCsD)tvhpTsHRQb3Hw8y(G1j*BWa) zP9tOxLVZtBiNM-e1Dr6=8NoVJrpDIhOYIRG!afb){*mChE&|Jn zMLgfHRyP`t?DlIr^rcf5Ng{!Vc;EW#~&E)eA+Y;SJC6QG=SOryu=b0Xq*o9F^yf zj`|Kn?v!aC!fWzNIBu(MUqk8butbz~a9QahkjWHOyFjX3^0Ou|RAdA9$8oFmKB#0T zQ+%gk^GK~AQc=&+cUu*Gx}qp(u2*$9j1^HGiili0MYNG3=a9p(A-o*toe!=+Ul58f`-Fa=*+*-v zqMJQ!%1d1e-6|;WR<`CzYEm|LQcm?%Un2k#I63dEZkhC_P;^FlNYV{`Qw7*R;08r(MMyb zszi^`nt43*z=D~)wR!&>mT znxNCDiALoPs?6Lm&wfwz>sO@ocp$|Z3H!&CsFIHeKcHwLxZJ4dYAsZhvq;Q+VIlP4 zTn);_xdVXPh$D}_naskEHyyBKSZi#JJ8pLLRBU9QCnavqn6lVlp|;z|rKXrfUG`3L zu0N?kP%(zVB=*KM3_h)kj6T5jl3C>zF39&+ml^T2m#ce6SjLwc`WkAxYSOz9u*Stk z)i@?hU?QFd&iNd7#+U1#P`+q99HxOD-*kDlTL6Fm2Zvs$`x|OhN64Dx+&+h)kTm>v{{k^jPEOq zo=N`z?Pr{0UMuP*lbZPX)BxLY#>(v=Zsd7=>BpHZH%dxcxY4%KqD4)~T$WOxpJ(-b z@Oae6(Q+aTaMMxpt@fB5%6(XDx4ShR9VDwxC?JSK3vVsy#tN_uhZ!J}IXUCbpXr@E zmpW2O0Q&9fglO=Qy={K#n_o~#MORT=ZOqF!saDG)Klp<4f#;0!4{mh7Qs^3`5;^V_ zMuUh;n8Qw`TS;Q3>R3f*@e10RQgSyI1Su_?HsS8hd-gkZuMPw~LPujn$ns4z83E%e0NOi?9=^YVw~o@# z#_~B+X!?Jr?5|HtByhBDzl*67#0RT`k8xanZ;WxCIM<+s?P$_HH~znZBTm#57%u%K zSye%7te}dfnI`mOlB_5(h8ZmwWBC9a6OQ8mc}J@=q-Vg^NF319V@R#hiw=cR)jvnR zoL{WcK}kjYHJ#&Fo|rGEoDdWMGENCO2lH}rbRjz!(T@=FGml;URm-?2OQ!ysL-gMF z9bLAe?v#^2xkg}D+mam={{T?)`u93&E{f^oXoQgC?_TO%)F5v+X_eC{aE7{<_?52P zQ4&(L&Nz@$Zpa)d8@FfjWEIO2PXk!^AF~(qX-`pk)Z=>>Y94QD2y$k!W@(DSzUocZ z)zNiJEk)<3*2OBq%jr`>WC$^UTfSHm_rM37;OjdF>#m&)J`BO-!v6q=P~BSfebnaY ztDjEPSZV4yd#LS~CrXH*m19;|>XZPX+#LFDcAT~_$CL4wpF}P%FbzuDZ*ixJCFzF_YXXr=#ljd8C~{wY7%p>rq^x#eNkOIU*|j`i!jT1GoG-OOYyzYM!NAYk8xNylX2>4UL)tvGL!VuhawBYX8^Y%$+}$ouU{o8- z6y@49+$Ky6j2vur;R9MRBIA zig%HM&QQK_m1aDQ_)(FPe^M^D^yVf^ZH0ujz=Lfde2W8d?5hTR(G4(wLaw=4YoU}< zMsF0>SmO;Jq{_=95Al}gaNU4$pT9hL+~1_~KG+Ng_eH=9KAh}~*~`B7D%#c3bk_=M z8cONn--RBcHeY_zo=5d5$0T6>+VpbtsNN1HPu<|7HQ=f%omJzdrmdrrNbgfpq>;{| zop9{fD<%#|VVrPqbFUB6KA!2g(!IFvkdou~onzc_!fn$KCZ+h6Y(wjxcaIAweMX*ywoOF}Tq9Z|1810AImZgm~S37w~BA6cyCIpmvUAl&EPi zx!Apb72KTj{{{U5CA=%EYU6y@FD{shvS7NpqOsOKXG2fGdSdK}~ z2aJ5_UZoZU&3-s955C)R%@j`%o!`k+S8JD2*iu?ruA{8CM-WpgAm8eMISky4`|vpL zjbldMpM#KmM@Vg_lYH!>#NTAPgRbqpH$?JRLiKeoR~ypQMF}+&k>s$1mcSeGRPsnH zeg?BK`gDF6W;r@R?tN7R)^|l$U#guiJ-ns5o*JJ`u!vMQs^o$~t6;9tkV*5#y>0{( zwkE|91>=i5UH4MpAG5eaJuh8&s=QZTYHe`R(<7M>y+m>YyNoh`NjS;v$0J@-E2U;) zOzx%BgF{+x$ZYwx{M)7%QbARJvfE7`O3W9UPiftM{uLs!EliA_oUW|Idx5|? z$JqVOm&|-m!-o=5bv8{_H{1UJ#psOJ0%<)3j@{Jq{{Z;5MFv`I{E&;Y9{GZ|wJe1ON545R_ul18`s zCR?YTrD=_v`G%eU0Q83@D?^X*R|UI-a`sC#1QRrrX)BqfPfZ-GUZR})K{!wWz&-i+ z(tlUOhli6C*pF+SGpWEkQ4|GO?1j^MhEwC2l8-2F`&T+@uAic#ElFZ}CRs3RS7=rrWjP2x-GxydPSI?QSIe=;dZ6I!U2)IBNfDd4aBfWI%wG+`=iq}gn#c+)8zk)&X z4+A6T=R@hgRpY?W=VM60dzWl4zUlTb7x-m#rMc8S6gA&CqHJz;>mxfla;01u7L zp5sU8(un3RcNIx&oNQONi%tHEtw&MUQPw>RQ&UgsBAt$R8j=PM{EgredGpRSnVtJZ z>X>=PEGK|0e+WE_RKpFA8L_jq{;Hum;(}72$2xYd%r?uJ*+^(Mp&QmbWbPe_=YfuO z^|$?5HeXA;sR5Hu0*3we{)?Su!7DHQRckiNnpkMyo<~%fC8{PQ(2{&ue_GA>O0~({$NpnXhZw0KZ=86MTY7*`3ncRP&TqRiq6Y#>xt=6o8?&ziAcH}&^ueDX+=Lyx|-S37ZyvUk*Z>nNn)OzP?(D`QmMG)#>0SL z`boe!&b=-)@ZLx)1^cUxz0V_uwf<|O< zjtczNjciCy-WSC8Z*sT8m)b~jvuBY^>B#P$8*7al| zy(|&fV*>|i!0(^bYwdYVYKV*~XX2SaQyt(ay=CImTPL z+an=mL6SEN_s+jReNB-LY~aKRVFQDE+4S_ZdW;(aHN(qZtJ@aKAiV|~Xyg&D0Nk~mb>tL=OpK|w7|5>vcU zxtUb~Qz%?2^ZAFkoUeRn4sKM2Ip^K1H?Xgr_$V=!6JwM}KD1csD&~&aS6>xGP3V6k z)ZHT~Z*+UG0_PdWINp&Zn$E_d<1WX!rHCJ|Qb6Uhh9ss@6HEU1{l~W`?3EBn)a~ zQKuMkqF7jb4KNqR&IR2krTg#Ic~jW{1Ti)M^`0Zxry}HJQJo1WGiLj(#R}Bx~GUWaeLn^T#9J-%iIbDE`*yCEcGnq_K zV~xW=J6_=f9pquWo^=Ed2UA(PmY%Yf>1?ihl^lp#CvdWsZbm|k0KU>x^Zm7vm(%Ak z_%GX&xJ_Vgnt%F~J!+@x33X$qbPA zj~auw{)vd<6{xHG_tQHKZJHLKtEfqt;;ItOD{K*y%K0eV90TV(<6E6=!UwuTxZc3z z`clz^2M1rWO?8*i+Y?gSrm*!reQbibejrA?(d)qU>EEe7d z{{Yn{=;4IZSWWw*TH2nTo#VbmOwVkNb%HdhiDPJwZLR(y*vP>-@^xIsxzV~(K|S`X zR{%KqK?>fVu6ljsOJiAPHMEnKr<$k)cK{aH*c=j9XguROz=AIq9!NjjLHv|!C-+)> zr;@HKD`CFS0Y^_;V~L{tGgHqvd0tK2oOS~T8v5j4eN{ihK;|x@C6ny;aZ!USKXcN2=4ehtWEDsOV7$^a18ug9I z>AgC;y=}fhn4${DY|(?AgC5|)hZ(@Y(izc*#6y}z-zb<82|+i<^-RB|zgDe1A2W*8 zo+8x=8p_TjhE`$}?pN=@$?uHuje7a=9^RbHW`7e~`cfg8!22)IPLukb(s#)!MZc(R z)KHaXSeip0r%=3YYz(^)qyjnP@2@-4x;8F5wYBZKJ^uirq^>3C#)7GOidmkL=Wnc~ zX-m}q0K_C>n;1Qt7;~IszdkgdO8)>;&U={m2-v_J?QNi2-Jtr{2s){>yj0TK=_(#s zYL=!(^$`M*Ase!A>^~^RM%-}P*BG(A&VD$d$RybO&|{DRRJ+=xrTl#~HtC|3ShwU{ zF+GWO0AcaK#{(aI0}a|U83tQ}%e%K=Ht+bOhicd&W$605(?s;%fwy2p!TgTyyqx&% zIOOE(!b#D5e4{QagITyC#?6Ddze1Q3wZ2k3-l<#HeKAXJGf0#+DTEQsC*V5!Mhs&o zKR)5cGCXTb8y-^N_*?-7_ficDS@PpywWC1WmdiC-tiVN)30Rb2MleGtIRp$G07)9z z&Uuu?WZlH>dw;^92-?MNy_CU3ZX_Oaf*ibxt zQYLH5p4SgWp1QZ`UCXGhGS^hp)mrWIBy0>vAR5cIr@x+?bsCX`hU~X(f%T)rdX7O zDJn21CTx}9f$ikugUA4p&bBh2kAxm2X>^r9w9vm=JgjK{0DWWGFeZ-SYKn}>Wu58W zH@ZiL%cwg+2ZPvPkK0)%3o)@A$Rud-DaOYiK8f~FV_WDItW?%Jy*t<6mSu}#v5pyA zVG?64k@;J2Bi+av(&!AHJXt5qc$3XzPXH0Y_f?palF3J5O76vHu~ttNB{jlIl8OXz z$b}rS&UrW^fsQ!iK6T~3nK%;Vx;SEZG*0IGt7T0qqVt>G0d>dG?vI+js9ZXtzDucM zu(dh^E4Ft5j9`(%oD31qjO$l6F#NIz-(}PbQPCR+*{5Cu?C?vi9+u zaz=IPr0Y{T+MB`HY=p)zwunn1NYrvVQqol;gXvUyib5k~mMjN300WG98dsvB!5%jYE($!dUL(QDZvC_ zfq*&j&)-SA7Fguh3H`|XqqzKgrYsS%Mg3@{qNKOW8YzJ)op!WkM6j366pzc;oM-go zK$9KO$*${jaCzxgpDD<$X`1TjAVYf)t>l?wzv`OZB&{D`>t-Zbku7#ERmW~ zCi5CHx^Ez=piKT^Kn$ZAnPiJRM}}M)_>Xc2KM4j$q-p;EE8PR!uQb#&Rp``GN}vG} z2Fi&yeoonA-;7`b`fEQ2r@sZ+43O*b9b&iP5;`O_8@_9Q%?!|*=xAyxT3BizNo0~( z-YkU9TwtHej#nQmr@C$gF~7yoLH_DuJ|QF?5dFXEogJ^LW2ll^{{TiRb&f*;F+~t3 z3Q1g+%AN=v{xnRypdNPEqj--dg4X>p^^zN9zF5StH7*S#X*|pWws4@M9J4OvI0HQE z7yT|*1K>C}=Ihl}%_HwE0crY+X}{QEpo-r?B(%{;QdR{$A=r|H?m59ZZwDY~%=&9O z78x?-W5f3vdwikQaF^Xxbi3Z@Drl`0;>lSrryJG@8d(koagp1%_Rdc_UA;M=5Z3^R zH`xaC<&uIr+Ia4%CGh*Ak?JlD87C*-BBAfjN9p4_VBlpl6|mqqGEH}G7q@Zvqc%1a z)~1)FY_P>`hIO|@th0j+M(v7tA-m%w=Nb4qYpHajE6J2whjDbdpw{Zb`+Zqrr#3pc zD*`cDiAFI;AAQ+Blz@LXpM&366G8)DV9@0YCI`sTpw(Y0J)(lPrn-u)>S4EsITAiq zmY8G?8$H{w{{RbWA?aN@QYpKh)>9HY_iY`(3T1tcxKqyrw}ghBYYB+Vr9-O`g$s;h zBzu7$>#ZjAj2vPlY);k3C%RrhN#!bf&dGSRSze9$ny#j*1$-?u#u2wUEKriolDWah z<@@O@o}(lZn8%zC_KW#R95{e3@7r`Y)AxR~Yb-Rwr~*^U;fRhs(iBn{ZeR&w7#w{0 z*I;!gNaqOw8vf|2Bi7SQaCBo=Dc93=xB7uYEj4Vek)VY)#mnb8EWmu6W3bnt$I8TM z(Utdm?g6gKTN-&;x}y6-kL$q;|k@;9LB$3Z;1Fc0Qkg>S3H%lF>+<-R& z-7NeOXtUK`t1Uv7R@yhLrQFE=L6f^MUfJLQ+?`S! z3|Qv_5a@aT0CB&akb?zomenvxN7NNJxC7v@jap|d8mAlpd*I-P@0^Vjsz~y8EuL(dT0`g` zP7d2CmIp}C8fuZ}O<5?YukqAYTd3l3PcStQ6=akyO2qx~p9j9Z?^4X4?QAi9(oOAu z3gR*j{Gy(t>PC(O%?_wpu8z6I-b#q%z$n zR4&k|GqBu7KyEhh0dC_N?sb=$7?icI++OMk?6!AEeH^lMg@G$AbyU?i0TR_ywA9<) zmmcD{Z2th)fG~ME@2ldytjK;hO?YPXbsN7*_}vBVXyjT4Eo!N)xkW8)HuqO)8zG}* zBi0A-%Jpr2Bx_HpHaFV7 zRov3xt2%S(`&h1Ubqz&yRm4Z@)KWuM7@`IsI8l(Sa=HHh03%s_N9YD&NX(44w2*8Y zAZ^WF$gDe2O(AubnvOV~;#V=3Mk5){SxX`IgN%N|Ou3O|vCy@^l6!x`=0*nTbiHTQ zRa7G6AfTR(77p-Xk~u=KKG=@f7y=& z)BO)G7bY(sHdfQd!%zfz-u{VPi3?e#G5u8a>BBvqisk%XwPq`tT5`pX-1uO=?{M$) zoj2G1qs3$`j~MDK6exm`>C#BeO>=6~8pHI5>FwU4p1QgU>Og7QIYhIO9ID`EY`44B zOntdI9BT~yN8`6vQKtbw>?n5qQ>D97`&abWr>%(a+j^<4RLXWNe0qULopo}vX|ISI*pbG#hwBaHX&raqYZerV*9cSz6! zj@#brQbsfg+r@;VxVJ&w(w>W@>LZe=YPU@<204@f0u|2Wm0Krn27jxAtgf0E+k$_% z2|kpIMPMfO51N({X(F&T{>3XPhzZ!BI1@sm6y~0VPlp&lx}c`gP!=)zw7v{ zYoly#;RC^VHsTWgd-v5FB}_Mnx<*@#OIb!R(P*?E<5qC1sT~Ir4IB6 zi==uQ%Fl43j+Xc;V_+o`&9rS}k8dEiw;z0D>pjy+CaF&33p6I#YrrOv3m3^nm4H-)PI z0QG>XK`MC4;F1@DGpz`p;^an5ZmK@%X&hs3m!EVqVmBU>yauMJsgA-XNyRNAHtML< zc@)B^}A&by;%u2rzSqUb7t5vrtuIO@uPskukyZ^(0> zpBhu@pv>HCe+ovE=Dqm8juBo9U9zjIuw3kQ^>m_1psuJfsUWm*G-Z1>+=7G#B>d|G z0~|v$V=oSI<9}pHAGjqKTI(jT&|9q5isZ9fV>pUo>URIhFW(S=WgP0jjNJy4hhG`qFi^wiWvs( zX(W-js!hchcGpRz^#1^=?e|JrYjw{2C#5w{JWC{!4b8V1Bn<5XBo4=&Wpvz(%-NXt zxCXF-Y<5ui+Br?i_$qpK*DW_sO(kr?otl+miCZxf?PXKO;!iuV?HmmAuWe5cYgtNMPh_)KFl`l7Lpz2zijYY|>6jv&Eh?~cP%Z%=HE zbgrYMUfzRx2+f)GO*9qHOMNuDbk!~BUFVv3f_^5V8R7KW0uKRL9!DS+&JS#9ZjJRD zC1J>ojtpSB+Q=P(2ynW0bjFtYwH3d^qNuu4LSpru>fL#pBXE7EAP_)JqZr3y&a)-z zvF2roC*7;CCOUF4LrpLsFLHQXUkQ2T!&TuuchmdkH%bW-siX(dC z`lu}-GCE@OQ(OFATK8%>S`uNG*Zydj;2f~$u^Ao#<2w0%RI&8g-;0dWVQ#iaBSTwnCt8LQ9N*4p(=`)VCe^G5I8GSa}DMJ&*H5 ziOqD(jl#hw_o_PX>1VUnih6k_^i%OvYd#0!4u3R+pJ`FsJ@dyJ^gS|JW5sk3I1V}7 zY!#x%bLY{h28-u~pDX&FtEuh~Nmos2TB?Zz^1%ess)rcM62O&RqrvzCO}E&NU%PlsK7@ff*DRhJ-&2Ep>I&gnkkxe#rIDD-!w-k zCO0@IJ*LlZy4c0Jg}C}o@&{%L!84GhiRT&YbI0kgSA&g+@7>JOS}1eqs3SK^lh`Y5 zl{2eBLoKl(A*YpyD9)gAfPP*$Jow`}`PWU$nC8158Pydy(HHuumJ6!&OZ%VG&Wx&g zs$!vZ+loi;;cO(1;XhCZ9`Voq-cF8V~nos0^1f4 za&T}*AL*r8*E88GT^rg6<6wY%+@sqUPxLiC6+K(cHKw$njX(^HS=0>dIo-*@1Owdb zPA<8ZJY;4#J*M^ojrLa58@(UF1a)Py*>tC@hV2APM@)a1f-w8WIaB0rC%E(DUnu%( zF}*LVNr#;oa4i~06g4=C-@n2!dnN&arB{75lcA#O3Oi+2PhLb)#ulC`b}*}*rzMwg z+^MyB`?r&?spY^VA@WEAhuszdX&>(OzR1ERvu1?k>x&hy@niUnT$OcZlzM48h~tds z5vKJyA&z@^@2?%ya@h=oI9{#x?yedY{{TrTPqDM@3c#*&AOrsZo^+hi zWOT?xjyDlCKyQ5B>w;iVTDm5t3z3_uZ<1F-DhSJ`rmh&A9k2O_7|RR*FnQ1*>#vqT z;kA`_b$Ki5h zUOt)CQd?&|CiK^M#IeS-bJVO@A$J66NF3oAhH?%uldnMDx?E+!$pz0XtwUyu;QS8i zL8E=p7fsqag0}S?uc|CplUO#1rl$oW--DDG3aWPTyYlBhu93s((8S@33r~psi|m#X zI3w9HoYMaQ3BdfVBhftv(l)Cl+6ka#qo$Rbc)enh$TsCYFiuW0{qxSfKZNWSm(4^T z{k;+rJ)o%{j<=a)sJ2e&PFrd~>}1O0g&ZE+Pq}%)&XO4VXYn3={{RF7x!EJSj`Hzd z-iqn?nowj~>NZKs5)f_4O$1YA%gv2{&rkDBhlB9*B5^ zFMvsC_V+A)egFUi#D{5Hr>MiV7{n@8Ec3oW{b5c?1A~*SoUBtT zcqH9^0WRlIUh6)ebsev!ZkF3j%lL+5$bkXaq~p|8Wh!|j;Qs(g#~9YzI$i)8<82?5 zuBMPc zh(g_A>I(-^ib!qu8dZ=9rHIRhX6_doi82wq_`o{M`j@3kos3zJlNsY~N$<6G3QUb~ zqKvLrPpB5VO;ufz-uAg-jAB2;ii~d05@cZUoSsHPj{3*x4a(`5&ViCO*6TIDKPo&S zR+!xvZ|knGvsrF+lK%h;lB$tmXO>;H3&~b}q+of#@D8ifA}^KD>JhTJcEI|8eN`Yk zrd21>&akz?L3E#~lvM?FLXJSFj~OoV=W#*@By_z%b0>pF%$tuj1KBz1DLq|LW*BxL7q?&k!Pz|e5|w0~?6GB*KdbHT4duE->h z?>H5j-hx`omS~Dwpr}l%$OTa(#cuZoyva)QjFTcc+Hti=z+%Ib-#ly0$1e$N{l>}%OeT;L0D zW3gWC6t$|9(o|9jfn{{3o+Gw0NIQa)f<8$cc=*-wCm2}%JlP;jHy5$K{>4Kvm;e!q zDB6K87Zrwvx{4YNh$Az`#H~c2WBFtO&fp0L7%T@QXgIm=>X^=kc|2%ue@pjFHx{ND z0pMvBr`FL_y)j2xW2UdTPZSGJx=_vvk`8$*k9Te{;ODv5Kj};qEbLSj{#sY>le_x3~X!}cH^Fa|esI;5_KMm^R@MLu1^R14)D zZ1Yt{EA0~`%avMvrXWxbS0@ZfKyi=($oW2Bru3;R!Zc*$Oi**H4 zJzi<w?qMaRrCe6@ zEiLM`Yw9m4Y>X$dYEhb5U-M!1Hsg@l81sO5*OKeFSdeGQtcGenOYiGRuv?|3{NA0= z1=plgro^jsmU&~CJ0exC>~_G|1{J--pYaepYimELLLzQE92`gpu5_N2X%Y2K9+IH0 zO1i79=tDyaiD?5f0$36pfq-P)jOUC1bXdkKMPgRm*JI!ES=ka6?nS7*8Angm^pneW zsHLfnIU|x8BN3^dFn46*2i!p=ybw+`^be;X!pn)rBPFiLXeWcJ_-ORCoiARDd0r~} z-~1brakvsw#gG3~~xNs~nF>Upw8^e=)%Udt{JtjeN7}4w=+(^Is-o92)bp zh@rr;p23-tNXcS1UY(IF)slflq>@#SE46XJ1n@z|N!N6ojIXpdC!R>XxEI}K^|;#@X@_BaFP8rR zsC5?0oitD@Q%O$($`C}DkGyRYWl4&$72uUplyV}4iJ!@y`X4Qy>v2QWs`O&;q{ zTfVvU!d6?X*J)_z>Lf!dq@kKL$ma|cax;v19QPcr*YU7MFvfeD;70eh;H=D6)8sY{ zpV1J<^)|xo_^2X^?QDl~F^ZUmr;~6bk_Pa&JTYwNj@t4aBNjYxUmL7%u=iSSZalK; zdwjrhzFj)P;We_NwvHh%ZF~sqr#H8@7uu(L+ zWD(N1uj$K84HLyt9fsXNcgZ5ip;~ofaVq0D8TXOokAthk*gQQ?cpj?km}qS0f@|_i zH>9@H*VV^DD5s{`5LqLec&C(r1d+1)K{**-I43>zh0$Fkd5@o`%~wQ$z4{IKNM!=- zhe@W|P@mNSTWh3(YegJ&Q3jG(2d)CUGU1(p&jV@W^}xoxchy}{v9lDvATjP~E(efy z=eEjR8V|D1H1pl0fhDA^dP{VREBXdhSlA&2oz;i7BrLevcQGJiz6;W_-O{Rg0hP|& zcG#sGh+fB2=wApH(N%nhs%%#@yHv(1W|^d#MKLU(Fx;)qNx>uMBz)+MHwD5~!DSfTz!*Po9Fp~1L74WrhJd?uY!t%s z8^3ksx=*Njp5Jwi-qQteone*;Y5)|DaM>(D7{K>2`)SXz^s%SK4VA!pk;f>aS!031 zCs8N)q1$KDy16cu4O`VV%K6>JL`h8l0Pyg*TnK|W;~X9`rw2)#dTq!M8G!1m6riyC0BbY`CF~gzY<(PIje z1El>)?4hnBgu1Dssi^g6=WAHCEbyo^vlLW3?QG*~1BE9U9DM7~i8I*~JV_3hb|dpe zV++paz0~VZMMZU_o_H^GkWTYSvYqLj+%fFeK5TIZQv z_oln0pFNKZaFZ$2mA7{4II8aM?4b=DAw){8+bPCfatQD7+d9%@Fh9=rd4}WX)l6?| z2`_kNp}9jF!50#$`80b$op>$YW>m zZGE;w1FG+n)+7-oaxMeCi&*mBN$Bk%bNa5j@Y}SfSn4yXKA24%|ZtGk9 zEV4s=rK0L9t5VMo0x>4*XjcKJMupi%>vp5DZ0a|ZK7G~KaN`@r>IAaG!CbE zC7FRB?Dm$f#}E;aQuG_sh4{+J^;{owapvugYCalI=;%wEj7+Jc@g)#Dno@*3aboY0lOo)Cm#b% zG1Fk=Hen?>+mJtpwI79OY&F1}TGQ#b)LW&--5uKTahmKsG+q9%Sn2uUr5MKi0(bCu z(|JEuy_n9;$DYV`l1E~D9l2FA`BMj|E_RB#N@iNfq>@J>333Xs10*vNLMXxH@Z5pl z8uI@DTV&+s<1$%XU1y(wQ1#yFRd~L1bqWePT8P@~C;UJQ8C8|Z$zXW_@=4lw>@$uu z??}LjohBI^!KuL6^xXi7e0EXt*szw>{YMpb(wZCHRb-LX6;MRJ_<$droM4cBq;^rq zeNs509%sd@en|aJeG&#YhuI#~xn8>A z?0rY_Q2g-L5C-c?QPo+i>`h8YKNDmMM|jDfA}Ad0l5%!!M{|N zJX%?;qODTgpj(~3ttEPeEaTOa)n(7PU;=psvO8k{oE&IS=3&bWu!&j@z&E*8q>{Ej zT1h2UzLxntx=7whGdTnZ!{csGB$1JW_C9on(%B7u9PmW{0OPM-J(1rNnB7hkKSgb5 zv-NqXd20HylA0$u3s)i}%*P}ZZ~8#z931h^sndFUu(C|R+ohsg<3N8vnP_Vm4Q`*b3DcBz0a;Tl zwRMtCT~o$Nba6CTAPFArM!CD*{lX@#WH=@LRmCc5ra@OF zC#J;1>BqeQ&nttHdnhBvi~*ewr)9e-z9Wv;&$_>dTNO?GQ>z;kja1OW(t3o)3NNgf zz}$QeJn~OEdpJm7h(pMApM*J_8Z|p$l-FxabJf??%UV*2=W1acOt>t-WS+qN!wlr- zQv)QNmub{RC<59XJF2Kn9g>x0v((UCKw@Bmoml!YIP4UkVeE1M?}MCUUTdu9MCfFX z1)4HJph2=dcJ~Nnx3NvmkFTP*(In8VLRUpHzUCk(pJ^dkNXc=^f#l$xH9c4Ae8*vd zm5S&cU|Ay0+dGcsS*-U$eRl=+={i(U$5onC^(qeEX~bJms$QVM*DoObc2JsOy8}y3vJFjqIy+{5294ZDQQ`;$wdI1d$Z#^es#Tx*L)nd zWfy4P%TGTRgO~{o)7z9Mb*jD0@iM@+=oVm*yMgM6n*^{2ki!GoM}GRx`lqW$D~sZI zI=8P)&>Rm3lwU>C_3P>0RcL{M8+}+lgAYx~U;zHH-xxjo1C38t>wQWVG~-TLu>Sy+ z!Jv)XYCSa6v~(2pp=qPFtj#2a0o@Yh?g|tiZ*C58lbvRC->N2mPHA+z5@>DB{{U2H zMX8{0pPx_e5l~s-sArC;z{XE*M{Ru@rS&XW`fD;Wq!H{+{r4dL zN*dBTp+2hWiYkj~qqtA&+}O2CF^k&afKW*6gtsF&>~Z%-ey;{V*5+GB-F5OkN6N4Z zFkkI6!*RAtM3pgAp=jihf!tM*juZ|U1As?h3DQ4UVsyU^q-oScf8}$48@pvsX-k#C zY3S=3lvRuv>SAA2R#B1|<30|}qwVKK!0C}=5eabHek)>z;t15!VODjAMm-$#&TaxK z%CPc6;wZ;#&-f2k>|dyB9a_}=DN#H$Nm+giYTqEZ32m{Us5c6H)W@>UumSKg_)K~ z)xBlhhb2Od#IqBef1a#*mQ$TicFK%`Hva(HZCdM1d+9E+xLrk#&6Yr~>beA1TyPA4 zZXWJ@^1y1a%W@F&Vv(F)50wzq+bHI#Ls>@9K^r8{Pdb)Z2Rn}N+4TkqC(8C5XG_T= zw3iE^xjlgOMg%+l>wxzw`WoxC6!bDJUA`ED7Tqj_MC@~km>yY3#~C1FzBD7&TtrO85HT8Oy2EN^Fx2lewt%5me-6{7%u`%NuZw^2wC%^BFEHVV7iqzx& z6d=P}qZ`*x^&OtT{Yi4O!D*z5q@ZCQ!;D~;896`x%#0tNbeCN{OBxX~CoO)UG}it1 zRXRr{Eh~DpzX_RXDpsN0W12|fFX{%)8wapZ0VjaJajJBD{;263awjAaw!_t1CnLgO za0TBAT@2IPZ8J$FQo}5&0y<1l7y<3aBm}@eJ%H`4hIHl^nBSHI&1^>h0L@kXKuEyg z%^=#pp!%Csm2nv4wbflAm^6}3q?s}{?@oCC01E&%kV)VjW62ru9pG%{n$$P4JFCfI zqv0`N>Pwx`45F^nRYZ^iBc=jUgN7v-WJtgh_80@kg^bR%;%ky;J{snQU;BPi<+{G| zLG>jyq|I@xr=FsuuIBZsMkI`27Uf4`f28xC+-bQwgi*W5&kNIjPoPGNA5?C0M*`4o z4^dquq^gdFrkai{9kcKD#0rh8>Es+9`5sTlZ4RZAjSe#=mBYp(%Vv$=Z&a=!@mqb< z!=`SxIu@s@o2X%-sz8!RBZ-k#GN&VMa7J<5ciel7=uPPn=1`f!-CXZ`S}anHL=<6m z>Z%}Rg3)uHvH}69b~~e4NDNB1=5R(21n2bDV$;XHI+lux-DS93C7iToNZa{v zMmWe^a?Ox4_VL`k2^z!KJdw&^S*oB~K|>ri>P3}8`fLjBRU-rg!oEj70m#of+xuHd zvUfpqYlUA^)x9S{UpbbhrjGX*T8X74f-4rns3?BY|GoiN+Qw!tTw*-2s7c=gb z?y7q3{Zo9YrKz_iwrY}1O35HBgt!V=1Iv?)V0qV;&H9fk!Y;^RaidPyQT5>k*oE7< z+Cs7Ve@AwwS%vD7-*BfPm7*xIn8<8`asenf1d>13Tl3}QNre#b3!{5McKdJYsioh* zIPdD5^|zXcY9nfUJQOybGRt{gdBiyMt^N5)G(!+C9&bO_)XI48-eOi zvSKedxwlk5#jf?$mO6Rwb#$?r8Cq4JXRdL=pab2UpT7WSldbQmdY;TJlGwsXuKxhp zR!DKi>}3>RaOhBv4bJCmHBC^9_(!VDkn%2-lG6-E`u{$z*P!D6!hV zx}jwak#9$b>B_6+UCJb*ML`+eAYyJwybfmc5 zO{#Lt${;Z9zT5u*b<#D#fI=+yrDvmMaFJ}e7TKUqJtBtzuDyc6v`67`*pmp^g6$BDlbHfk^Bgw`yqjd9$ zG;i>7T3i9)bbtqz9?7)beRs7MplaJ~V46%jESm^JlNeGv6>|6)_|I*5_&%GFnLaaT z0oUousdk4F_LvFlF6TGOcTiG$?dH9Cd-&<%ac>AR^7L% zRcLp1(kbc=r?ynk!C6$(!(Hhn)%A*w6k$ouFf-VJ{(9c&kx1-d*+=h1k}G~u3~6KZ z&#k)BjMY-uE)_~eNMQ@IxJ+P`c-Y_qFmQPNHRJQLvTf5zNt zYBTDr^^t{=If)DCxE#MAlFPuy>@fQ8(XEzoByG_duGhb>C`EvxeyOLUiiY7;LdMxS zMf8#khRJVpfq-(~>)XDcm+2q$jTsKe4<+Ars=KLUdEFy=COPW5#ydTI{`_sLwqhWv zZ(G&D_PJBLjkw#4X_}ulVH$mxE8D&0OHEue@r^3qAs-0ZNAr4Tq{T` z9Zcx0j2t8do1B4?cLG4pc{tWj(>*Kf0rG0PaO1YiqYt}tHuX)K*g67=maXb)DyZC- zjn-I9hF(D%HmCuIY>wX=I1ZXZTe;uzMh*obD`^GN$yqCv7U>lr|J1#tievMtn4J(_+=m!QoM|jo^!8#)jpQQmLMmYwCX#9+kaFz zj(|t1TG!tm8EyWRWKB^7MxCS!v<6(URmd4($RBQX=lb79iWnImO!LYd3DP&OgWBm; z?j-J*y&{PTg0kToZ>JvNv0P3Xa>^aEE$pnx!XEz+%Wdg?l z^X#+TO!5uF9_r7bzfr8!*N&#Jn#buWY6Mh{(v9-d1C8?X4<9}8#x$Ibw}AGT+~Zn1 zk5A22<5v8t?wYt?Y4%w1Ia9Q*K<)s+IoD@(0UT!%NVPDH z_p1txhgku;EAdp;_hA)9HO^Lcrb4oqp_y58*yW6b9P&;u03%n#!jBv`Gat$tD{uV0 z)Z)Guy zNwOMfCarHrQAm^zUqec<&g(rn1TwC9!m+^)RZikNpC=mgUr=;^vds)|NHl96P(6A4 z)Wr}FI-O|~^&6!6Pp5izy1Kq~uC9QS8?f6_skrCeo>djFPj%pZweFi6#w78$8Y5&m zPl!i-DLc~uE%rLO>e`w&rg~vCY(og!{9BF)^`Evk)JMYOF^oQly5DxZ0(^xa8~DeM^HU*aN^N{$1;}en_ni>^DI5 zy&u+{D^*D)kEvSoQ&T%EaG+*tj2tdQXBhw}E5L&yRJ|B$eHF=o5*XZVa%A zIuq&FRa|OqwDZeDO7oVeimH}P&_>wWKmd}VPki?$jQO*&q@Bgimqx^I-jN0i$KGKB zl>F%5r?i%M_VrY0M|TeK(`9{TAhGwc#&8J?eivR=Tt+xqr#Jn>f>9`)lj5`Ked-92 zx_;r)*P65&h?W8ua9%ODJe-ytw&Mpm)p|}0aWY2ZfL!5JIkZ3;Pnp0VDeiUDk4bF5 z!$m!GO7X~&sDDlNV0&;Gc*((BU~$g6ED2yDRzs#n@OzI{+IQx{3$LMp(g~^MqH0ue z@0I~B@=rT=;YN2A$8NxP*6VUSDv_cw4Wt%|+mrlE?aAo0y3i3>(~J6FV=#~B}P z4zv1J46)4EStV;A9lLw_pqUy5mL1d1&(^jfRWjde>La>JS}J3O7+&hkR~tA3?ssv< zggRtU=1L31o^C*@Og>RSUkZn;x|jInL{>Sff-pEb zYJQyfSX{xeBlYiT9Fo%QN0c>esD{f~NS6vKDdQDtuTLus~p zcN5)gu7v5TYD+Z@x;wltQzq7_6E@sP7X&M)1da&KGET7msEO?nzX`f^nt2`D=xug8 z8>p8H3rlo-7EX_&X0MV@z^q=SGWwfBpum0Vxb8@79mB=})&!A36tToy4!wZ%2(8C; z>S*C4KCg8p>Z;Lnnu6W<`(*nN#9BDnLgRY79PV}lj@)M;V_vfprLm&jAYeR#IW@{! zW=nL|5>iW~9+Ro>QLW}Gi*ZR8bD5cz1S|pqfMG^(3D2|)_Txh9&2Y?XvE6Ob2FO}l zRV!%wtrOOlg{GvqS?Qrky)D0lAcHH*#^*tgeljt)lb$|x>anw546+Bh<8Yuo3FM%Y zY7f%;J=^$qt^9u7Sh6#ENa$mln6Cp027UaKxa44GI=4y6WGs87c9wpME>ZVgvURNBjMi;y%GC8; z#cJuQE(VgR3k~x^rK1_ZJ5=D{4EX!=#=O=(1}+q*DqNZYOZ5K$tu0+?bRd~1 ztI=hH@o~%t;#iUGU;4149OJq3uSFCXjBtG5ZQPWGU@Vi+o>NMC>x6YxF^V>>q(gx_ zL{a8)4kJeF&F6MO@A@A(snO(#Ab;+t-3_nh6SAo`%4*u!;{G9Rv>1ot3RXl)Hyzmy z3-SRR=g8K~C!}P=S(zhUGXZcU?_=)m#>g(pk*@)!fmB^R(NWag$5L9q2Ul;UU*;ZHcEAFu_Jhv8HJypqXUmds$32hU>uwg@mJ@gCd|fwB(>9BZ$=~>-$r?%# z8+%6G({=|b%M+4$9rfsB>n9%+J3ko6=7Cmg*#2m1D_A;~XzHqMi$hri=_HJ>yt^6D z06l{z1wjWO@(#6nPD6605IR8x(7xxI1+zf&ONeUGBxHll@kdgpGv&2IE-?X|Ya9Qv@NVG+U3 zPEH9RfC<6*&_1D_KB6OiP~6uj6F9UN4$0GMv2;B1Ngqsgbdp|uG7Sx0Fo;hN80=0# zJF~n2@O7!vSTIC$KY}m%DSQ6_a?{K(hQx`KY5sji``joW0f?S1ir7DWm{ z3R}1Uj#oZ({zq8I61NqNEH4C(_C0B5zDS17a9^oENWP68p!!!wU(|A1*7;LORV5zj z{W)Q%RRIu|?pWl99E_a(^EzI9j@1`maShURhwV=O5-YkqO=078I<}5Zk67*XvD@n} zQqrMyt&liIC_}2D{J?N>x$~W9eLK?PX=HP}J=a(7du*E`#TA(=zODT|zTKd-($pJ0 z(H)^_1uUv#kwGn)Hth#0xxx15Jm3Os+{QmO_`#Z+5hCxECAC16Yh~(*<|#_uO(n*j z7iNkXoce4}w-_G-9k}vFbbtPxbgeQ;FcJFJ&AtMHH`zG)RqcPqe~DMgaHNn-uq{L@ zB1cucV3U#UY_`##X+Ibs=f#7Ci7$;Q99E<1ph8@J^(X9goizl=X1v$K5%l4mq=6bp zk~MHy3FC0c1Adxx2kOuq}mHO8EM_$(ox_au%j30`IRct7d ziDF_@<0oliFh|EZI`ds)1FXIS7Dj2ayIZ7iKcXBb7&tV=W92rjH1(8`P+Tr`)DTnE zz$z)?jkmQpW%qM~!0AZYIPPcJVQ#$o z*6yp03L1#QOBCoNmQ$1IfJod2BrqI|@yN%O=wD1?^%*68RAI%sX{Xfly0AWCR(PuG z$t1teTYQo#%7_VoR3u?dO7XFn=w&VGWj1UMI#!1J5HBOt<^QX#; zyB*NS9>&d&Wo-kHcS(zgr-r(Uu1bohjx}WT^RcE32*aw8!RLX<>~r5+on^TBc#e(a zJ^F{u1UAO)B`NIvPjI$H3|6X&+Bs?^Rdtd-Qa6;HsvWWj134Xs1QXj=`YEiNs!Yg@ z#v(r#W3su89Q5x~^)=4ADm#?0y}bxX8V&(m0_3&{$j)={PkmMM0!L-5GCHcLVb0bWJ2SN7zHzS+^__J$v|xFQZh+fSKDSEo zMB)pFAvvJ@Iia_uX%_N@WMyEjC}-r5djfWh6WHTkkEC>S((yD;<$I{|| zxCSBI$t+H+3}l0b`0tG4fJnx)@T`M&T%T|}!;*NPX z$t6Pil0@SGY-9Z_0KoeJ3!^4%G3zyN#y0AzSu9oZH>5{6Ecp`nhl;{=s; zmt_(pFwCJh248bXgdMHG&Ivqs<5`*CP_cu;@LGKx+|lIwo>3lKeocb1>1s8nsFmq% zPKUTJXXsLZj z=|&TC0DZfFIQ#N@^R51sBy60;j4jQgNEA-{0#pa%M{j&Bb$W%c?RH@mnu_A}zPxP^ zNZ|ZCI?X#S;BmkYv~!V+pE}k0zp1l375L!Xtp=OU=DV(s9gY*Am@Px9Cf++cz}KV3gRPLZte@a1!*{I>3!@grk3JlAi<3takU zuK8XrDRhD<7_vyEaFUGSh#=vWKvRQ^5uYS!97&vQ!N%)z-E^&Mwj${S*Yw@4)p4Ys z@l>~(cuGkWMq;eaJ*>@~3=g;;9kpyep)i%RG#A$U?YdRbH=5yGTPf*(nGvCuN^(U; z^GY5ha(5WjPbzW`BP4h1ajh7obDrs=j7Ukh1N?7=(~J;CuIw3-A{mgJ%SH)3G=)-)PHVWK`}q-?qqZU~~B1m7PyjM`X2IY3eLi z`@ASZZEec#G5J^!MtM0upc?UbSot{^taP!15OF`3DzUyQ5tB|E*YZia9<8jlR$L>Y zwcGyygNljck&KTbjDxpwj&Ql)DGQvFuXWSm#gU+6F|KH?NAgJJ;!oliIl6D%4c%g_ zuj&{sm3>@oQPiE45F#r*T7Uz?9KV$a3%lTR-y=p4J6VauhLf=D2clGT*!*@$6So|I znznAMrLU@z+YRE~B~xs4WhGum7*!_+a{vdqPX`&}TYYanAG3%ha0g?y{2~mCb@N*( zaoyhYODt6qtInJjm|H7 zo4jt}RrUSFVY<{T(VA#PMk;`#aD|nys=)A0IpF7!p87A;(K9nB#zlr29{&KJ3gNA- zG?FiDt)6y<*wrmStdCCB7-rnVJGu7+_MS1{89Lp}WS$FOIMNscK<94BY}{!a_O}_g zx2-1EL3E9#vfSv^H4w)V4Xp7MdA-@hjgN52?oY=YYc4F$^%5{8{nH1VQ+Fq|QZ%u` z;jSqyQPfsJX}Yzt5{@bAB54*w=>S`hWNZ+*JOP||BUNwG`0>rl|@_6-WE>~=lT8JOvJ z(gFgp^)=V1u9Wo@*DC8=H7d+h0wE5+kdz}Jcq~sG9BUFc!KAuLEROxtk(=_gSy~gP zEwxvAcO6Y;KAcjnQb<&+ub#oOJ~QWbbD@bfhTE;9i{8Oa$pnpvhqjMo#j{ddX<`z~ zBv3&c6#iW?Us&X*48;2Y1UAq=r$lyqEZwfN=(8~)EMtR!1e;VHP)Flx?Xk-Y)iI+# zsj$f!AyC^!><)j!lg74lG20(h`zzf*us`wiRM#0kVzQrD&;fj!x|#_WbCTFlq9#x= z9Bg|I_&6)c$nBo^a9i;xaz;lSuW^iH z;EegvTQdoYb;ksdTFGSYVRZv{0X{aNvrd{kXBdD0ECCP|_8F^!nbCdSb zXK^sPF}sP_dj!+^9B+FvC3QNu18vrr=#H7{dKF3n^z_u*MN+#=c0-b+C>hR9a(4E| zW2IS*B&@ha(f&xM4S?RI2CtyS=6Ra7;F7D(AJpHpRBd6rl0i8+13ZJP&50|Y;z-@k zbQu}8$^}19RFP6dq*S~+V~Ajc_YA2Hc9p__F^m!qokO}zlLs~zmp`~WQCuC}L~9RO z*3UIl($l<*Jx7ybtVzJ&CV3t>^V?8qJT~ROKmPpnni55Uw1XcqK zrEtLVbIBv)8VCBbA#1JLrLC^*hp}lL)Rwi>&?z7Ehth7AyG2b=d!Ul$P?A+0wv)~{ z&M*L84{`U>eHpBpXG@baoZ@&pfw8ct=z~ti)83ZL($;Gq@QAHqG~OObcZGsDDk%v=f)i;cbc1#(B*CHXVuXJ!xR|`*dN)mV;l4Kc@Pf)HQax3Rr2WE9Ttu z?hXQ%JQf>>50IxT;EdR^@b-0cWj{jLDIcrBOK*^_4;-lmNNk&$XX`R*7j>{veNan?^fs=YB}HQ2SD(-Ep4lUf z6Q%U%<#=>w%+tQ!suEybu83l&w>4!f0&z_tXpk&!21av`3C=>~=NRJ}((4j4r(!;M zSQ>0yZDjXKLk3$Pu2HF;{3g#!xV%xduBC-#M2N?b0g;YNVQ@ek=ZzZ*Xx}wvIEx!r z`}PU%7?$Y;Y2KN)+XV$RrhrnzSxTy|qBh=(kPLg(&pcU4y- z*LA>M5{zptR<)&Sn`AO2Oe81@yMZh~B(DIFo==@5a&aV$)8YZHJ;Q*b^X#;6S*hgJ zgg;Z%cKdx59Suc$rB2y5FQ!;R6oN1b2PcDr*a3}s=ViwCT#mQ=etV%XlJ|LPil!e` zY~3|1iE_SDRH!+OvK2Cd4)#_Y5Ib|g-SMqHkCO%#H;0ny1>eu%UUz=*I4BigTY7r+ zS$L<*)~r~8mt!d^7a{hHbCP)-`~CImavjq2J+8dF6Sk5+G6% znJu({2|3((86b8iSs6lDXAWa;AlVJ#_S}=~hP74IDdDx-_cUu?OBjhBHW-`+8QtNZ zf&Tzk1oqdx`j@D)BOQWPhXJH~5&mqgLzE46>X~GoGd0$(uGG_Q^cEnXA{_V|iv!(> z!Ov_Bd8i_D2CZes-A<8QAfBO`*LRvKs+i|`Fc&e!5M`8ra+o+fecYUJj~Z$|p9`dJ zL$@H_n%Mg)J@g5!iUGV`C@JbDw%RIc{YX$|np_Rr&KGGXAutH%{`&L3TIIou6Lf&m z;dBt1;9O-LyCy}Z=hN06nihNQqP<=?UDCVMUrq!leT>-$lEiuO@vW|(nFeuYGu@LnD0j>hg zC}Xy=v(59ZXi~1e&vmDshW`K*fJf~~Bz7f&hfc%6EN}oh)}{_DP)1>w_tthU8`0o@ zRFsfp78f8c_UI!-)i=}l$_qu!Zz_S9iAR?zZNWzoI$2mW7@u`YEFWuLEshZsC2{@fA zY`I)&?vxTmXli!#m@9@9ppG`=63#(lai1J#OhMCIG)~BS%Rn`-w7i*u7f=K7w0+V2 z4@+gQud09ei*iJ*J7Iy491>U#>ANgPfArGXxUl29R^N#@C~aw%y!x-!*U($@K* zp%l_cBikGZ+gt!ZJAoWwyLrhPT5(wJllUA62eC&D9mS7ul6RV`eX^Fe&reP0!iXZ4 zWJO~ftbM{j*ctY&F`RAVUS0{K13>M!q7$0difH8<)BRfNXl=A~v^VRGGshA`6308E zQ-o#wz%~>y?4vxM4~=SJFuGAAHmm(o@_7ga1N}w%v($G=YMAD_3LA;ou(Z46LREdn zLR$j^fW&fp{Ak@`2T7mdHbUoV28s-&p}O2Qx6OG>diof2dJjWd#=@cVH1S zO{kMK%m6jFK{vX7vgt+mYDy6e9T<<)jdqoi2Yikc93c#u$2@rFN@ko{;qXpNqHXZd zdH(>EXkO`KE^wzO&r5VY4^Y!v+T~9}_$gdN3aXRpMi(s1`2?}S$vyM&uQk^?G;w{D z#VL>(2Rk3t02y?h=qnqS(+-L0dt9}&1Jre6INcDHiS6mSr~{MRBd|CpUZXxtP+Z5x z9O1=_ua8A+ct;KL{3Qjv!#z}1??Nh-fmG5+um1qm86-$f0PY3>;CULx!Rqpfn8ri^ z7vK6TvlcjsCDuw|P+H)QjMhZ-)W#TnCgn!h*Kq+;uppN4l5ve8nKmmtj&}U;RpWJU z*#lmFtXi#=Q`K4erkN*r1f>j+|&-z4_Ry4owW z&FTeMa%FIRxCbC80AP2=@28uF$!L>b2{+HHvd_1h#Pi!I#s018YNv_GH~_ZRA()lH z3&#X2t!wa-ozR z&9H;cN$;!4)v^3HF|oDnBfb9so1i=wedDrxT6){8@79Vso}}tZh$@;iidrg1MPs;r zVh1CEf^)ara5>kJe_6LWCP+YSjUenjlMpaH!GzFPvHeq?k)oi6<4~Qkyn1IJ84 z5xPHi4l+hZ01Rt&xv@sz=RddeP{$3}O`Xx-MqRDfl%ul4)KqtRiqYxQ&rcaH?Craq z;~j%@=LZ8yWx8*(2=U1eH@H2IZ-mrt6qrlV)J8zkHB6F3&Ih2y3gnE1CqEg%8SSO| zPHrYEMki8ry@R;B`9BJFS`u2zUE135eh5Wuh15X83@U(jsRRNEJ8}0pI>hQwzDb;A zZNz@(6dUd6q)1z^7illmw04`7L!|!zi((~5V;LdS9sM>0f-(Tl$;PxWaWOiJ3~)($ zb~|xf_d}l{(X_p-wdIp^)igRcY8a+@UXZgcJs z4PN6=JA4|w)8^Rn;j%cZrmr_y4IJ}`V?g87=R9Nr54V-)J;pxz-F`c~H_whdGSOtS zxc4bm0;(-E6z)HXM@?HLO3Xizv>=0y`6rTbkWZdFbEIPWf>R5e`ALwA7ESx^n1dfv zsBTq+{{ZiAqSaQbd^NQ0?6qcCy)c9NReNQ-oD63twl(WvIU#_yR{}sJ-{zsjS#53* zKT=Ve*7WZ2L}qCjBPkgh+d15el5z8spY_vlfZKMnE4Vtl4`0CyuwKfp(b6|dG_$;K zJyK1!JS{T9LgOPKl5joP#&SIFpm7@^| zYU5Ig5OPGB7}~&tz~`N4^_b-u7}AZH(sdth$_+GIlFP2^?e=Kping-dM>5FC1hmw~ zVH2sxw+Attp6BPV)g#Wwke1kYw`*M2kak&rsj6qMz139G^)+2g_*i5~sqkSwa0dkA z9`FW7eN!||CilPg;ZBD;%y&zkqOR)NimG~_QYfyQcQsle3+-YC5R7mXa(`T7og*($ z%44-T$5Gprc4E3sxB)PFF8h0{qmG`MzJ@8o789&ko#g{0762S%1CxyMJ7Y{k(P7Tg z?7W9ILFg}Xt>Lx4RyxPJAiG}foj}!%{8oqfyh-TB(nMsAeTrD_k34O|efj-0ji2H^ zQHdJ^z_1*7q3!OcTU)vjPNBWptkjd!SJTaPnpJ2$G%B>pq~*QC0gg@q{SHai_IFg2 z#4+5;J0HLyWkVbg_|I{xu02+|=-bTwL2a*|s;;)<46Z#nB|w5mS#WvJecOl_?V#u6 zoRg{J(E9%XHAv%NB*g7X@1YD%^8QZ9WNb|4I_PC}8xC>hV+U6qjoW@KU} zxU_?)jrab_L~V7r@~^tSf-`geA!@fxb6-d_2&#QSGl1J$IKdbh{ReLPCsDBE*YOtQJ(_RSY} zXRyCtWm0O7KFKW{7uvh~tm^d$0LwF`O~5&;>hU3L2wtS^YE$*eOG9&)fJSL6*64s6F>}U3e%WL zT>E4o3@c@`l6f8YI+^4D08ndLBdU(Xcl^}VUx<%jUW$CMHc4yfCQ~w&=l6oJ*J1KCFv-s5dL5&A)Kx>UA)sp=X!n!cT+jw>A(c$i=>qEEP1XEiBh6*ePld6kUp2Ykp&v z9G1s*9Cp@pQ$Y(38zi>+@OSp(WJ1kE4(;BOE9x&>b(NO7qRD8YN$3^Ki%_x1eNeoD zvT?D9IU^Yu$DC-GIFi8A9w8-^1>IlZx#Y;=_w9YV1#@7ern%6?U(~jnWnK10+(3bl zyXU_H8OhIn<2tqn)zftO9Rnh3wHr21ZM!EN_c@*&uGbq?KT1nbwn|r$n4$D(;U3W! z^#nprOFulE=a3I=Kb;FPoIKi#fOh?fJP(A^=X2;D09JM$tD3f+8c8j(tsB(D)pZXp z(-fSZjlPv8*lKGmRCeCO{cB8sERJ2skUEDX6>$lV<@=| z`F7`wZX=HRiMbDi=^)<87x|5lmNyA$sT6>Ya(Edy+Sxwt`+*qeS9~R;fNe981hBL{ z$nM9~m0dS$w$N3_CDO8uJ>QFP9#iGesU&dN^NbBF$r>IS$A0tt?t<)wIBdWV%2V|p zUrSwCD_2QDYA($K%0W0>v$wcl1A@RaaG+!K+UWfu`7*uo3N|n2-6fXd=o}l$x1qY- z9`$Rv(BG!1wxp>HNizX7n|b3I&NGfrZVs!A*Mjyr#DGrSxlCn-n3^kkAIUJUeQ{NC zi}5uHK@-I2;&|8FGUsmRQZVFa00ZQKpBf!k6D9b~aA%iL-uwJ2<73J(>bA1H*}kS( zZ8Z~A^(}Q7shTqhYnLHoPjN?V4}w4)j~w>W4Dn%<>*O2t_ej0_ZU)F_uWq+^DQl{% zlfz4CaI(crxR5#~eMN5k9nIg7#->u-g7bSGQR5Y-{{S!1X9?^2jv93T0FCLNNu)5( z>yKLyer?gCaexV5+x689bO0vkQf1G4ZPdo!%G4i0@3F^ST}yeavRiHHkp+>|DD-_l z%mR<)@>Pf==e|9a$?EjW6*J#>sQ_qSb5-B#lQ6Rpw}OD5iBW$+wjtoPRI_mUG*% z;GKOd>O6R{apK$s{)Ck|=6v>8_jVOsHb0Qn8^R zj6Xj0I2kAwPB2eG~TE*L@#VWl8TBIpARGT^Wp$yN}Gs z4?9V1r?5Tshk^8DeMo(hXX%*z*3dQv=X2 z)6|e!Z+3fJ&1ae&=_a1t@uIK+=4hem+vP0c?h46&P%ajVTb!0L z&{u|vX}vmX%4IB-7?1`sKOuW_oM#^zD|&{uX*lfw4dnhI?x6jpj}H+7N4mZGVLwmN z^*paC#Yh4&LQsVCWH?alsz@w1E(yTx$9-hxmdlqBG9L&H&-@?*ERwoOSOu*?cde&r zL^i=)JqkipHB{l1n2*j#9P&TzF`at+kENtzZUS8yqi6XdvrelCRdngux`N;({4DPi zINw#XbdzOyEWm+`;Aisz^RG7l0Hv{I&~P5-yf|LKZN;PcZ+6 z9ZPbC-i0EKBB)TU9Doj0bL}Jmeg;Uv@2sD*G_?J=@9EC|imxC6MN)Ln(>|c;uCp}| z&^%oyR~FC2`lvkzQNaT{ww&jW9AxJ@S(sS5yl7rVaNADa;^&$`Hqrc_0k`0G7QsO^2y9te+x338dH{gJgUnHKUb-`iG^uMwhC3pQ|cr zE`p*amZ2evk1W{=96GnPkN`$O4aw(PeFLXU_Nl(nv!o!mzsbMuqC)U3jRgwXKCE=% zztGw(oj=r2*q%k(RM2{5Uu!v49PJrV&pB4mX3ojT$t;g}n_z%;BzlCq^Bm8<+ADK( z74N3{zR)^OnzT*S?B=7=b{It5TLsQG8Z-;^Gw06Kiva%Xz}c-*@MM^n|AJ+ zdI=<+j`LYj4KR3iyDKE9=O8H{@G-&Q@-)+Ov2t>xhXS;)1F);VWLV|Y(BP>0j;4-! zVKuqopu|Q=rH*o<60N~jJclD8mmK7t`ctT5vPT9>GovlrWJkL8C<<6>D_O3P)LFWe zBKi*uVM%2tJ9GJ+l;rOt$Bkm)y^;q`W)3b1 z(xGw=e{FewgxANJJpE54>}In<>=1r9TJ0L0QM76)!_d~-J*JSuRRz8}d0nSS!|&&5 z!P+@d#sNKv=jUDnq~zu|sX5X}=CjO29zi=F#3+g=?CWSHPQTtPy)|%;sAw#++Ac6g z)dE_n8JeMq!xzE9QMVg=W5%oX0mjYFZbXt8n8Md>#g5;PbpAXbvu4ugP4&|Ceyp@r zK?RQ2N@0#|f|qfpWjG+PAgBQ3p4^NLdLF0Lar&#Gei7B-n9I8L5 z9W~TFU2R92uFC>O##xpwWX|H-nCB~>%s?Z*K1Q^0Iz#g!A=wULZpOQwcS8+trt9;s z>NidGR0g*6w4(7vNLgj5R0|YfzCm_gPEOxwAFi5b)6K{88J+3*t{Q`A3sC7gMpkF^ zFLg8u8Bu*DISv5I@!zn^9xy)oL#6a)%-K=pQMr6Yzxqz2h=hKc=K=S*Aane=w^p{%ds_Ke}D9j|03Y7tl zHh`r_#sItChd<;|ZY-BGO#@XcsF`+BJmljy4|?w1LiTcN5l z++$XHgBza148H!sPC*B_#8hGytCV(Y6?b%7$7AS#U{dH4S>F; z2fu;{Zg|c))=yRGS##vc;qVUI`l{nH@ndxXx56rx;Y(3&qm>?%)w8?;H9!LbyT)S! zf<4*5Kbu>p%XJ!&{pAlzg0#y~sN zd${NJ=UEauEF9ef)wX?+4m;#$mxHot^qs=tP5eSB?^To2iiO)V;Ui^}AaG7|xC6(9 zA39?u3dox({Es_&*TU53(d%%IPrVQGLHGXvsnSVPJt8bM&_^MHWtJpTv7-Ti)#>x>thy-j6Eno3u|aT>1m496&1G$b|-axyc4 zlZ@$j@@0cJRD1bPJFWcKnyVG z-{$Bg@r|Uq_DqJLTV1k|9uHQYX`RGzC#Qlkt+~0tz&k+Wfv;8dFH6qGYjHqfE&;;N z^-HmkvHIC{?tnV3JN+FgYP#Bsb+EB|c#15G3IG{diSF3Q`6oHjt%Ipx;x(nLJCV)u zl%63V^BVa57HYpFZzX@=B%Ris7uSH!@hm3{!c_wSm;g=>z&JYC`gUn@i~K+uTU-R2 zSX)f8ep1rO);%>ta(lIOlhjV$qDZ1S85NLi&M<$N#QE9@B>wzPbzNK2NO*IX+Wsb9Y1%FxO^xWs?l1Ut#cGkvJ@f=dgz$_#i0BZK%`Jgf& z)|Yye_WFJGcB-n)c&V$bj^|MnsZSj^r&!*Z@$O^0+z^#GIPi0;V&h{?)0=~a=UZG| z=B_`^%0A9c^6WTF$U3DFma(ZOjwluu5UJbhoP+IioTFzM9E0DEHRLnCoQ=|)i8iV} zRNNy&1T6cbk6(2vqo^&tRp`?^_4fOPB$yZ1~w zM>A%GS7_;aRIG}o=Ta_EG)$}xBYCh+&`Hi0cbw-L$0Hi2Of80HxHz~0-*A5f!1bg; z?@wX3T;!^xsi~Pr^_0wCGB*UO<2!xXU-i{+v1DP~Z@%_|Kx_2g`)spESIJLZcc`T{ z%iTS`DBQ}DAre&W8=Gj!EW;{GH-6pp^jO5ls`diL>pQHvNpjf}#+JtG1-lb`>h%j6 z-)vV=)Ro?dxzt;#oI^`DU{EMv2pGWmCxMPmH8`$&gLU@on8<#oX&frZqrOyJt#L(D zcK!ztW{~>Zr-vL8$H)vq@x}h2QRpfl}4 zVJ$q2Msl(ru^*9kI;Zo~j_1r#$ZMUh=`((n+aMLQnV#=kRV$ewnn}m0 z1f(BOO5E=_RlpeSjB6%OsNXZ$0AqBMUw73y!`W>vjTDzk%Z;_9ta#R#Ry@*3OW=UK z5Ac)7&M}7V!P5CS@C=s0l1Ogj%k9dW&@7UF8%!!&1sya}6X@4|B{0TAl;hk6bArTx zcOU8V&Yq8^V`IEJDxmsXN9?5gmkViz{{y2Hzoe(t1vm5ywj@)}j4`MT=*&dw3 zk1_LU)qdTErJa;O0FkoHeOXOUf2t`*Q(G-N?RvTDP!SsfP7!hgFc}4N$9{Cq4r{U* zrQ9pV!rj6LyL2@4RN|tJN{OHtk~xexD2#pWAsNO!$Mh%8o1YPpMCq7Gb3)K=OXMw# zf%pnczL~R40HCUomul>ENYmBy8;zwK{{YU&BRrl)JIh0iZFnAM3q*4s+Ya872D%ru z#|eg}LRWNw%$t1@dleZVXD6Tgai_XVUmf91K%X|=pDUkWZmSwdAjQhnUtX&!Bgs00 zigUmQFyE11I4APtk>45z)DCQT?0G!9*L8NYe=_yxH}HvV_Q)w_xzolPQp=Ksh{*tg z6o5d;IKa}~Hy&)3mO-|ytnK=sx$%-%ISFT5)pYgoJso8gEHqTpM3Yxjhau%g+#h!b zV8$`>duf?EjyxyAzuq@vw z>30aF26%@yIIa_IbDZsbl=bNf+RK%Wvh`g>9UC^p3r+^)UrY`TP7iW<$0JpjA-K4n zAZRXd-(Y?exsk{P8ozW|Z@${?m5e8-s7{A6&!@7ZNfgkS!>O$`bQKhf_;szBHZ?@jh|0H-k&w-WIS0TW8m?9hQQl zbfSTBMRgBP+j@?!S?VihhJu@G#~iDYk>v8DJc33*J^W}mxg)~T`=D>%{{TpG${hP{ z&7`t=`!ziC&{I?0Dw1XrL?Td<$lJ1cN_ZZ|-5P-SK3^yKlIn{%m_BS+&H?i+ZNp)^bCjS6R{U^Lsv{2MM6s4zN zRi)ydKl*q(v&lT14l~!|;7T<1YbqQtK8n z0uzJjoE9AC$a(mRTYw&rk19jUr%)^KMDHcqZ1yyQ{X=oW%(u(^cTlq|6!lR@6>~}{VnU>=0u@jz+nzako;o zqBjfW2U&I18#FChOtirfy>I!Fpdq&@#{hN*Bf{x8`eaz`E^Ceap_wf1p1mJwg7tFg zyUZ5}DDDy@Z-XXx`--}{b`6cd{{U|zN@m65Wn`_Q$mDmXt+#EUL5}tp z-wxyB-D!J#Vhar|s_1Uh($k|uB#9uAWO+ZRfN~U_*d$~CcpPbNvGoIRpz)z#(O?r> z+3c8x86=m(WnU>%)KXh5w;F3a%UOGhYB40QC{o$?cOTNtjO`tOJoB$9(KDSsQzO6t z(HE!@wTniR8bM*lZ)C&iZoBElEoPG9E!D{ofYV0-e=CIAG8RB~uP3%i^RKA=O_|f> zGGJu4OuLUD6=&4?EcQVy+;&IGsOzeyt)+Nst?ZD-;pafa$Qg+qo(FayKaSLcB{OOW)szcpH#+96UyQr8X81 z7b(spk^lzAtMdLHr8y+w=Gp+;(Nuj$db)!4i#@6+S*lr$VlxS2PQx-NBm6!805I{! zb+p{~x^s;#&u=xy=iL$~X*(oMttyL8Z-Qzi^+Oh?F>T%y<-u@v;GRJ5pPXk*z3gkO zo;gedX45vN@Z<@4gPaMv4LcT|EPAII zlP!-8t^8H(kE!FJv&%tuy~ROWBL$I5`#>J$RU{BWz#IH-2ga$1fcz3<%iWF?H@*99 z@S|I=YF8HiruvI|mX4O;cBrAVQNy+f=&IS0J;#o5?{nA_-#lYknXyBWIPpt_Y@#&! z_p!o~(n^C+uW+f7TDz^mHp?3-`eIK}SYkoE{{T?OxIeLX_w9$O>Ym@f6r)yh6ojx)WN|8ivD(A zcRcg=&^nYj(n&y(usn*i!(M3nr*)6&m($uKNewFBsvwF(_+_N@#STClcq#@43Dr!v?GJ--6GuJB z{;7rnO($i8rD&~m5<^P$aD$1IG`rYDG0xHXSCuE=ajh77r%iE>9y0nPeYV*IyUj|5 z`$tbnL0wpDDPy9DG?JKsV-WkEIm>>*ylL0v;>2|a5#HcDQ#srrA^R8dgbtL3EX zoWmqiNOMmS7(S-rICfLHMsdb)a&psp?hL>P$D7@$aCiL>6+h9Aqp4p|U)s7!C*Fcd zN~Adl{#=YW1a}AfYqD`mV>Cyb(G~11*p1Os^bNbFscRvCRW0RKII7uaS5L$i86q%q zw5~7!@Oyc4BPFwREQYbI)gF*d0C^qz{He#0o+xX$Lzn9elyueg)YW$ss7h&L-#lAb zIgPeWGp2vAC28RG{Ed~};h5rCFmK_W_iVE4<(@Axtt%GexuTK+0_;=1x z_YQk-M{%5v4x5h-DH=n`uVl-d0biuu8vRbzSl_AZZnXVCWD^&Aorc{oenOzksOMA;P@3{E~$nC9cY}kkr zIiPFZqfhhnL3A&jq(*B`J-_y|2U2}d>W5pYOZ_mmdZsm6oxqsb5wNhv8A;qqjz=0P z$HayD=W~Vcx1wR4LI&xL;N4Z%bU8`XS82@c9qsF9B&AY{W4mzY*vvsu*yLl+j&-Yr zA5O?RFdX9Xxz(`Q14$bc$+i7dp{$18Xf$=HQ4_3YS3pZ1cqb<{*RordQJ(o^3 z4c1xfd!-uR z)(VPCssm3_L`0)Dt{c))Y8W?dWI<@8&setaK;M- z@J~DfInJDrWNII306*J(m2z@oc(#N1@$*XcFCyPfR}Ce7ORVKqDC0uek~RFyy^qd+ zL*rL=IPlB+9f!&j9}QI6fb}2QNa-8ApNY6tPv~69V&tlNjPVifU`fHlV;pB$*l-xz zJ7fTo1$W&@@Eq`9CWoqb)%5MGLkmN2uA{X~1hT^<%@Vup9^Isz9FY8u+-uEC)3Tc@ zCyWOMgJA9nKHbyh&5_jcx3Zg4ZEIyk1yt9nZ^AiNa|$zU;G7U}GI->9Bx&B4*YW3* z!qKJU(X=+@D+FLPfKTF|Jq=ZBM4qZhs%pzV#`POxfH@mT?mOe-I@-vE&azqo8}aCx zi*i#=mi26z7J4gXLsuCTNMlXHRzJ`NPb3v1IKjyzYtFJwK*~9pe#8OaPYH?24sOAxw#tuos$t`Fb!WfY>MN4n`1%Q|6i6NE*b_9K z(n?@(dCBL=#~kTzrTTAC%FT49ye5)4-+yI%oQT}P3x4q9;SyE!)i+gAT&ilRrLT^q zct(vxux#LMZwtqB#yBhIsUsG_nQQWQfSlyZ!Rt=Go zRQnwMP^C+Ojz;2gr(Sjst8qytVdf`|DWdA#FexgjWowi-CXN396HKytGssn4lodXn z?%{k4@_$`nM;zE>I6J9+4;YPw5=w(h5UQb`g=O&drYNc&Fs_~dQl zKOQvS(|=3L)fnj|I$8yaG;aHXsONxa^0WPA=dQX&M7Pm(0@UR;GLR%x!6b+02)NEq z=l6EwTDaXR95*(JCO=XLS3N>z&}bFvtUupguj*q}1>W;pa@Iubj@L=<>1#kt2ZeitR|gID1a#h5Z*tTj^>mZIpBW04iq*9LiVU82s5IAP`3# zi5>KLWX0&uY;lcVF2>uctJ160CfMYZX=iVGKsPI$sbZ+N%?p{AF-~dYJ0el}8*t!r z&fK0!(I@(W{uy)(+pq{XskK~b?U7ThKx%nJVx&B2?aH=6Jnq22^Y+Ly zS>sRkn11Dt)7e8qBpM_&r*U4s{>YZf?bL8fB}76f1D1q@4(H{I9Bw>@{=@C3W6y!I zStMT@Y@$8(OYW}bCwUJj-My|q*9(=T^!3A3MVe{vH)ytV)x_lL6%u1Zkd1pZ?ZTRdh1k^bqzJf zzTp(8*D@BC7{Ochdm8D&h5cJgq? zpB?Z&O+5}&Vc%yX&DhuVR>+jLF(+b-<>^Z;R8mvaMk*&}+dMS!u}6(FAYa#Mye+32R(A8DntSexrx7FM)7rGjD^(MCpV2LNDLv5MHK;Ul5jz$l1 zbe2ABk~Cw1Wv5Wz)7SeUf2N6@sm>t!UdUqmc(rwnU-PY%@kYp1$`eJ6%8J?hyw z+DRuMoadckW_4_74v+|QeFa)OV3Cpd#;9dQkXa|CdZ9#EQ^z{TlvltnoDJDt7@S~n&akr1d6p@1 zn80G@yWjImR+C_^R;s?STY6Jgn|(S-AeyFq?(G^l_ZYz^aSif29~joQZd{qs>c{|4 zp`=#c=s+0kwDOs@s5*AK+f7+=>S^f&xn){P6%sp>f?Fg3lD}i*jdL*Lo+BLOfn#uK zD#$dOY27EU*Q@QK?q1z{yVqONR$7BuPaJDJBaPevgWm_vb>}dmIN624#DhbhbVkDF z5_UE_@A$4ao2@+cb#PHp)HAHPm+%se+xCKYXNAI$*d4R-G+goGWIjRMtA>JaWizHC zyV2b#t0}M7iYg7p8Dxea*}~yVcx+`(4hip#4i2L(BZgEvy}Vx=s~3j5Snb&GRo_$7 z-KwoO8~y(5Ym?M`!La(04a5FeZc4Dr1#Ev%>@`eBoSkIP?`wG|;kCD>Wrduz;!$7y)yPkO3JS_VJ+2(<5YMjwiz$k+Z)K>WtR|xH}Z% z>8pig(5y6xTU`iMSY@e{NeV7~yI%kWI3%4lH`9-?_E{ak<;X2?y^1l8@no`Iq>ldp zaeG}4qxgqYCVg2;48{%tE0VcC;OL7?Q?oI|d)1OY}WXCjly;KU2f;8FtyzEZM zPjRCeK4qLJt#8z~i|x9KIVFHt(6)rmt+&NT1Q7M!7Ud@>l>ZYqG$A}Drcoql!=U%UAY7dvhF~Y+Go`mc- zhM9r#4*l!3D;J3N93%dg>i(Cnm-t;J720%q*x*S@f$u88-Ip7eC+F?2Z_!LyQR@vJ zUAO}M&(Eq9`2nooe}vYp`gK)usiO64)fXCayC5-A^2Zn>pUvC!@7q}2b2CKXGB(!S zf!n&$8Bn|K-{KIh-|33V9yN-#FsNaUFrpHG@K}S`o;f+}Ines9ah^iztKzsHf&nBC zHwztgpHI}_#i!~@TT3*;Q=IBn}uygw0)cK-mxccoV3!~X!< zCjG@F6(9SQCkpl5&wAAa9DLk}KkOU%G(aU|ETvhNqWg`x^x zn60);B(^%e%fd?ox#6*rvPe1hhTW6E&a|5&Uf%{oP5o_Er&ZE_7fxNP>8t5l z8hV&gktLR5Qki3Ld1I90DspkdYt41cH*~=in8(XAv^x0%n-x@kP@&0t+AJduxvc5C z+!rUbbu!gnB~dJEO;Z?lZTm|_yZTA?=Z=2b^-^Kuw+X%pCFE_Zef`sf3rI@NlIk0S zQ`Oq8HP%|>1rPj5ibzyvBa_Y>ow(<5`)kW}uAiGYW;+7_BXSKJ9_Wp0K-AuS6BnvF zI<{zHrlz~z9{7Qgm7yX@qyQYZh5(PyND`h0 zrSG!JYl$c2F*Q+xRmRO6V8`u?-1QdL7GnOFZ$@tbRnCv}M7`PoTX+M{* zZ_P6dmhTW9!oKML0I5>cT}`&tbAmTTkC@_VGIwBPup5|RNXLQ(wetR+#OhM`(~{6O z@@=r=u|~&#l!Nz*(%mm+PN|`em`__))ey>LnFwjSk{{i`$6;mo64;kgTEO_HQ<4fjsxw>?KLM)rrV)q{i zY;bi~bdKtdm9up+U;1#}?o&*A#78W+_e+mBU zorwd-?+N@8kNxVm3vE3$9Vn@Me>LL)PSAFouLW`d82s4hUZyO$#xI7>)_7Nm{|0E@LA6mH$%pMs%vC3JOF zQBu_jrgc#Xk{~v`1wx3>92|_Vv*X?8OY~N?(3i!@JO2Qlp2>y)-I|y7-kS8k6twm7 zRT*iF5@!;+A$IpfPdHJ*!8~B|sm#pE>5lO6h-`Zu#qNGpWW*2Wm2+8jtV3=TB7!(! zPfmEIln0pbrGtP83xL=-1dJU9KB<+K408R^#?&mRe@}P-x!fSjgwoX3Qe0UoYc5pE zHkLUxE~+_XU`BDy;gWok0M?tHB{0DVsT;5-i}mrd)MhItjS>TD+n z8y$cI43atLQ>V+6?CB+dbLqjOr6lW`i zYz()2@-#Md_#=C=8sXxTzW2H3WvKWhdm6@&HdM0Db(QK?F(OXUZKaoX`62}S+i*F{ zB90D6CmGIktp5N|bv%zHfCv5~;R(2Jh*OB8=$1D}uae~=-R;XkaE3(uK&VBB@iCK? zcJR?D83cR50Cvv3RtPM`Zaz`BG@cP4$;fHZ7ata`u1PH-JXMfZC91Bj5bm*%vNr9D z5CCzw@sZqMlbq>JvC&@EK_n)c>$~KqFtQ6Jki2ebC+lt3J6}-Wi^oe&hG7)3$lk8d zybZ-iai0CMG=^(Lj%K5~MJvY%*Qg7-;aJpGUABr^n#HDQ$~%;lta7lx8&`vp7=zp# zX=p%Xy2mw1X(I8W(Bq}R-urBsR!f3UaeJ4m8^F}&6)2`dDz841WF4*txkot181Jm< zBFA|qOR_7P3qo1mO}Ai*EKhU%ZK{^ClA7M6mP)w^+bXFzC0GtSDLuIZ-II-;ou4Kw zQO4cNN6zYzNvg+m&qFPg$5BIcsr+P8FQ|erxKumQir{ga07vHS&Na0@H$};e_|dz? zcIVAo_uG)7H=FYc9;bxNi7!1>D5#36s<^OOY9m=as(&i(QhmzJoRN%f&);50sbS@H zOvbV|wWY1nEFJ+gK7G|CB)iNNHS?wVvbpAxQBP3Nt3d@j6{#i3AeIPD=2CDmlY)8H z{{Tq(W^SJ6Nj$83oHWyHk>1VrNl4%}J1nvF0>^Xdny=v1R^P={$xzjmB)%#leWLi{8lL#0~|3Vf#V<{V6T$$ zHx0P(K=>?AjTfzTCeG6{TIPoFd{?)sta#<2(Q4h_q6fNkoOdZ~(^1)LDq2)9iBx)# z$AOkEPrG0^A%Mr6oE<;WdXegr3<=%?bl;~QP2w(wM8E zfJH0{N2MT^DFTp4Omc(*tU&LLdD)-TG)Xh2W}q&seZ7#~kq?R^nS_r~?wCC%X^!!7 zq5f3Ho~lnt=7g$}yGQd|CmGL=*GtIv2$*vSEsz30*rrUN%2>nX2VvfmGM3R5w&zhu zt2Flb_oQzmi@F6|lEik~pEx|8JDn(9i7sBYD|h z+&Xg2TRlC>uCiF9f+vb9c$6yc$log*`*#w#?SYStUMwiubs87k@O~Dg($OceSa<3v zw^60mj)rOx9VJxpBtUwq%*tG_lpWY1e=dB3+dA|Z{W}niPI%S-0ODD4xueXMG_+Mk zE%dTm;iEF7Nd&@41ckGmsyQCg0pQ?*I}xYbhps>VG>sm%Z6gL$aAY;|g5X8%>F%6$ zb~>6gx7wmNM&tp@1P@anA5~UG2%!OE&;j-^X<=?h})zZBs`r zHNrSbwNXJF((NW_PZ$J#Tx1ZZjt6|}!Dh2GWemM-(>W%qabCWKF)-A|v~~&j^V3^t zT9T%cl_?Q&=*+Gv6kr7kJ*m##GoHs7&b=ISK36t5C2dz?EmhK%@-|p=>W|g?%|}#K z+A6IRwE}L6Dz=qM$;T??NMJL7GENVl8cRO{T%4V8xC#TGWsy9>C*N;m3gFbi_&F`r zR5Hk_kSnkuo095AGO7mP8~U3WIXZG?WV`onaNmPlEi(TA7#YV3b)vytU1hq^@2OfB zbWty>k748?896u|lyCva0~#Yb9Pu==Sy*qK)jD0w=eU0gVsw?IZ1M^%>jkzJR+_VN ztTCOLz-GZ2?s1Yx8S#U~k(SUSh3yO*alhbEWy^aGrt*dQwz;EGO-&NjMKen*$s&Oi zP^)KnkR0G=b1D1z(?beLq?SPgPE-ziP-4Lj~a@ z=Osv7fzAlP0PZ?FF-u(k03Jq?2Hz;Tc=)e&?04plEInmqx<*ntXL^rFzo}5*G$VEZ z+&OHF4X3cibS{sXHInYi83Zf^tlQ_5HG8tjTNQKJ98V_S#O;k4UX-x`fmQ{wO8)?+ zHJy=-<4jz~0nOj3LlOwuU%93I{_IpysGq1Fsu)^NR#x?6QfGXeh9d;_&PVCD2Rqn( z4454(v$BW&Tkw^BY;89i9cVuHTVklTq!Ri!3!$jTsUa;aU>s$L9_$6=DCZmjoq6$} zkjTVrBcDWJla}xM4g=(Fr&)S|D&UTRT6rnx>KWQ7XDtkj+-*7h;Nt~x$Hy8B*)EV% z`AlO8i=1*C2_Au2o}jqKk|i|E^)%&G)udTAxjxo)Y~>D0vB*4j?W~N6i%zK;QxeGk z01TLU2$~vxsJT|h>(hf2i|Mm(CI}01TPg|0R2&dUAZq!iJ02mB%F$zB4eo;xB!c1U zuj_hg(z2GKmfz~A^uwRT85TkA%R~@a-x}lq%0Tx9$>0tM@2q_Ok&%mr9|rn`xC=CW52Ek2SuB`Y98!U3v{lP- zp{_8rYBCigj}l860Z4Ma>7(_6qHID$ZrLYb18#oAXSTdBiK1o($O&=_fh{%9qFr9?)=rzRik^au(oH-m zH0|nbOqM%#hRy)(oaB$OI%Y13l?1f8-N)uhgv7vZw({C)6ZVHW>>MvOS4&z2xr$hARXaATrA8!G0Ps+8kIV+rHKm)= z@@2zp#EWPfci-7acKT93uKJEzF%5U%)*6O${{Rjo#O$SV(g3}Q%8w^H#p+lT=FA)o zSHB5?Be+Y7?x3>Vg(0N3!%Hz_N#u@ZcYVYKL`-2w=Wy@bje8HMKB39UjIu%tNc0|_ ztC;KVncXooHe_gPC~b8#3hMGPEX4uNTyg_o3?J@z*MaF>CGD6<$r{%3bsz965$cWY z7YO91{A4r7MprKwRbX3lk_vN?oM+w1VV(yX*XzGVzB~zXBb}P85$3*qkR0F!=+CD5 zqP{xXHIBYx94@l68H8px?eAQ7JCANKPIcz6KWDk|!{W(2W7v~>`%(jEY9YB@5UpR~ zZgYs-<|xvkRLG9mZMiw;mB$0O8qj7dt4mFu{kBxb1U%|Ompi4tzUy$FdU7dCvr0mY z%0L@cPI*&|t0v`N*n0l0Kr*)HiFQLq`lXMyD}L zC}k}hF5t*H_h9mVGq0WD^thsKxPc!h@lCg=PgEQ$;S_Y&(>sL`{v{R0HmM~YKqLA1 zz%ib9BzGq)2R)9odVVmNxWv+4H~jwqR0%e1yPwGueN%?IhK`mhN^6Z&63ZJ&9A|8D z;C?^{xPzQ=j>PNIGZI#Y>pPxD_gVQ^A?>n26vE>S7;~l zkQ9*Z#yRheEtM#1gPeDz7|ihew63}@s$-_=OMPt1QC6@;gpxUnlp#*+6Zx3(q!ZZo z(RyAv@U@Ms5SiSYDCVI>9fAspVJx-t$kg=WM0`ZVure?{)c^uW91Qsw(Xq=h*`mNL zALi+XcUgX>(O+<_Y1TMD73)VK^i-*5&JR6_!hNy{IP;}vj5&^fh|&sTC%N8G3Oc^^ z1v6f&X==Y46-+Y4v7(52Ou=$C?%Xy2%f@}&_5+uCgK^)dB1qirAP*`lY$Xp>RrIUW z(zWK|u-2gC5|s);4X{L~7yz6o1muo%47}l&7-V3#d-H$76pbzJjy|1r$4Xo7l@{f_ zdd)RD!XtSZmP8B*S$8f6H~@@lCh3|TbJ6&;6VxYYpnR)f*(|osqLc8`%lsPT>-c|L zZqhg`F^^$l1~LXlJB~3OS+2=2!N(!K-{m0;mmSaYPiwDF^tA<+#~kYg!mdhRQ`DNB zlo>I=LNY+cc+cGMbx9(73t|Lot*hBl#?7REQr@hcH~4vt6n6N?1Jfrc>yo>}Gi5$l zj^jAaId3zj7}y^hfqB_Wo84y*t$$VQ()60qTcWSs2JsSlxBzWn7a$xkIUo_cZzq$l zM~BcbUlZm#Ph1yzZ~*L7Nb~5j$3tGabE+%9gx_i`^^vm=PAR3wune8*54;i=B=g)K zI!^$2QOacH)RKL;@U;~^PfSM@IdcS&#;S=;My55HI4omR*aqy!+0LM4PZJCAoGg0p z&fI({Ei7;cx)Qln)!JpEnpkC;&|EaKG@?hPQb0v@4Tb|Jk3S<`dEZU~(j}FE6MXN# zx^1!$EN+i_Z>yoGx6@vz=5JTjP^MYQ-21lV9CE%kj!t%y&b3ufF#Y2Qb?UM0 zvY>vp7CaguJUZI(Zm#?#;MLGiLj*=OmM9<8{65_9#O(n|Bw!3CX6HC3ua>kwv_)rCQ(9Yw(s!Gt@qrTp#rlgLNddSL|X`n?6RQ~|=198|MPB2D0_tKg2$m#@++t%Le zrN?_4tT+?sH(D)kl!n%9b2VdBIy}(1QX>TJ0P&Iz0l@Y;C>y94Ab+-khMc{t^VG ztCF1rvVpcj041_Vd<@{{pT4nrd{`KewV1KIY3AK_jCH(+NO@VxZ$`T3d68)8D4t1` zikX&Qehx>dNdTfiUB3!A9l03$>qjCfUxhb_ZL(2l8ydgvsQ6=P{o1R%Ra8op^^w-j zvcWQTh`VpW0x=mJ0th2KfHjiIGFupALr=&;Cr;odC({R_y3^bvsGzN$o&#r$| z^u|^;bT5U#y_ZLFFW2!^Ej6dHMw8fTEwu60+O3LUKhBq8?{c8+R%KCwcsv1u0M>_9 zibF8S&Nnl5X&V6E_B~aY3kH_(yU$&Cf|i^;D@RVi4b5w`hTvD=@6*$Ow7>^rRU zCsjvLbAl==2DiqAQKYO3DpF&Ga8zJqxBmcWao;>@;fnj+gYQnXl>N3PA}8dh>}S$Fab=D#_^o^DyJVm zIo0}Y=*X)eY+}#fM_RAK;CS1RCcP`Dq70gH;AD#It4Nks&YG=k&rurPQ4C0 z*(_R|;jpd!Yh)b5qF}+rS|hHs+p1%duHDmSm_Mrm7K}O<&KYIo5F7)QJaMl#9#>Jv zsRWF3cHmhaZ7w*#vbB9m1We=_A@iQ||SDFe<0O00Ithd~vT~mDAkF+V&b0 zIrvqROItvT`Q1mfeN39=)9TS!x_xd4V92^{ucoq9ft*5YhZ$ig)= z`EI|L!Yp&VOwfMlyGx6wSxGJ44yEZ@2De!%5LE1yy+wHrSSj2F(!2)naxt#}?y~5f z>EjgrL~uK&BO0uP`}%v-)lpl0P?Ndis0sxTO0mJ zaY*KiZ#~fUbX2Pw(Z+V~1waHYdG`^M#~Zuyai?)x1{1?37U>OxbOm#06EO4Kq`m9# zRzmR34J>HeL&=F`W&@UIQImo3_V6^05A6V9yyCcOVI|bi3BCdK*()qBV|9{{eQ))0 znhT9(sK-w&0w5m5T8~gLzyN36pN0MP>wco@bK_-wo-^eX;op(#{S@joYO2%q3d1y0 zTBt3sRtc3-DMFOYhm}Id1Z418dt)8BIWGFot$o;@&^_1`joary{ZB_xd4}}g!!0y5 zbb|txW&|t)8>0)(3g>Vn`8d^o)_RssG_dBquDYsi4}aA|OGAJPgQ5CL^KZMFifS1q zSVXNiqQJ6}K<=IL3vrMKoOaQAZdzajeHnn=?r25;5KUqK@2u$}?UVG_-7$k+oOxSZ~#qnHl8D z1)bf5&W+2qPF27ik2vq&8uWig=1mlj5fMhOx?SWG*&J9tpx0Gi=YcFeIZ00%}jhF;mnV19dnt*i;ar`N2BFi>AC~eWFqbJMCzp!$>M* zH);jB8c8WyBv1s9Q>Z1OCxfwbhR)6hCzIz}*)n8JAA>a*h#OY>aE2aXe~lRPjfbMt zT{Y7aT_J?#8cK?fVl?rUmY6X9fPUHR3C4bLtQK_4j+p-d3GZQX-)}?iQwB&}Y%toT z>dI<9i;@~xsq1H`WfKNM!bsFMc)=^PzqgX3wg#Vc#rV+T!S`ZzuU>yueTN$=slKIL z)|wEu+NcbSvqez<0G>gUxMU6pIOO-qJb|sV)t#t3-<4Dl-S3p0>OZLT($?2aZlE>y z1ZR?>6Da}EjDx%Kj9{OR`qAn0yC6Bvpxf@K_gf2xDw5yzO4Csa&v3T-1~6WA+Svzi zUFhQglbrkmuPX+AL2)(*BZQ=xOlRH&S^ogNeHn9Vs_0~%W{Dj(&rR>%zz_)@(4)Ra z58vgO+R)-7cn$*jABM|=gMf2I+UEyULBXEHFj*8FK6mU;*0kZ;WYdmWdc+ zg{Q{se}~@F(bn|3()}r;pf>B9Uw;!MYF&LyF35J45w>%M8Q|yqfY#%pel%f{8*!s! zibVjFM`nAaqK3BLOCL|&DV`Q(o@#*d)4ODQxz5lq21Yyf9O(yQnIsZ9z*n!bTOvRP z$){1(bISx2v0G?kv$Z^Uk1{g>v@amxQASyC2_J21zpr&ByTfFSF}t0CBzgnbtmZwZ z?uuB`t@>8B>u{}FU&qvh!y~FlO)7s*bHU(|?fK_c@C%$tRmgL6lJ?t2DBGt#x_w6I zyC+Oj+h}TTbd+e+^EFLM$|>Py%Bd@y^Pc^H_!^_}p&Omb(#6WnZt^&3JPP1;O=_R1 z7Z0c**6AG$H8aMMwM0;b{7(SxeokG62N(or9O}^Io2QT(J$6};yo|>{{{Tz0n5?6v zq`XI1*2-BRil`lkzKD($ZWNCIj~U20`PC(8GrmR;Yw=oB!r55X5&|F{bzphTwvL9r zh^xQy(ZTh-+$xUb9Fu}h1~IL!g%mliaq2BD4LfPuk-p&^hIm>_#>3Pgn7XEduIX}y zN*c;rfk2QeHU&7}?V3(QGJrV)Aa2RehB{wN#lp(*ahpeeNhftoiKg)ExF5k-R9~-_ z%Y8+%r=_W6if48eQPXT`;0)OT_hG>#fG;hGh_ zl%}tvg5fJw+v18y9yVrJ5F|+^F^mrCI9!pF`ki0x?3Mmjk;U3-95*qAt~XwZz0iMC{9}`>Z>WPHd^KKs{Szi;z6UnAM^M#N$na6i zXrAI#g)}m)SPdf{cB&pb9{il^cj>vA7Bk-0w0XU_uXKo%0a{AqKSJB>)v;CFI%0cq z3K3Q6gpOOxauhrPyCaSW13mTP8#Q?ze8m3%GKlFsx`ztV>C}_-tre!+X<-(aCNMBq z!7aFw+r|jkcdj?`2p>B24}tLGGa2T~Z}mYIMXKwkTeYOgZKvt0edgU3){+{ueeC`H z#GXb-_JPN6G$}H(Bw&a*HoZaseH#MlK!dFB_i9-rptU80Q|(=@92qxZRuaRV<9BWj z7-VyxK1{}qE_fr@DuC||%y2$!!gRyYmy3T+&~*%^8+{Xv^1&;sO#`x!#g8iTV2(SH z-<>U(!I6dIz1JXhY<8jSx+VsSNj|iJs;(4Ea=pZrAC$|vOjAc1?@VNSS1paak00Y5x(qUFVz6ZGXw$5$FvA(78I z0MqV5x3K*s>xv5at`w6gr-d3ynt9U_PyzrU5GnN=C&PBeJ85^!BY1mUFWQx{;+97^ zmjPd@b^R&puc+64rlq3lZob^hAPFUI&?QwM24j)Au%|o>kVkz^H`IC4=6FYVubnry z{{V?rFi7(XPS2>1a;>7eRoiY+O-l$};dd)D@5%R%7|F*hr?3F?f2?qPNulum=xMaZ zD~_-lK;+L+P`DEdiUDC3f{x;3~OwERqRIR!AdJ0l@~Fg)@*V?DA@h{&LQVCTP+jV(THjaG^(}3hV^u>{O;AQx2P$|46aohIr%C7GG11m3i1##pOu18L;(j!qAg zteMR11=G2%G;2s)W5jy|u{F(*{p;epQgJ6#bsg%+Y?f*faj5l@7ZE1qr$VPFLl7k3 z43nPN9E~p={Z}Qv6l68EUSa)d7C8$=9u|xGXVf*8T5Ek3L^W|ztnoCGK^|5m0|d4? zazdOgMii6Jp8@rZz~nL zoMZ*gdlTfJ^w+w@hCc{B-eJXqd-@?c!15C3t|g|r+8@x}Qz)FjtapDe1M?pwAJlV? zI%nJ7xx4Tqtey6RDX)32elDim2-MK8IINVnXK?f(}OuMgR zLVRa5xQ+A=zI}k^)EAc>(r(D})+C;djM73*>UjBMQ7&>e=h{f`_SL#RB>8%q84T3z zLr~yX2feCD{V7*9*7a!WiDj)*)9fG9sEVf&?*;i?hZvBIo>v^NjU~{`&dPi_QnAIY zE&;R-+#gz1Za^Q3WKBzPzuM!Ir)iR|87DPRnt&JiOM$lwjmIF2fO~zo&+1)UBR{1z z((Zi5gLmV&T@#OpSkJl( z>~HkpIUl2WR2Kfa>%O0swvIo;^$jpbk}BGl^e3Qv5Cn`FhDJTa5D7e;8#*kEnS{9R zXk}L7_wK0avKV(Fz7)z!Cth_eJvDR-YK7&diYb^&x6=yygB4?f+d%=x13BkTeL2${ z#?zbwx3YMv*(e`729}XS*--UQ(vGU9qo${)jy0vFU&k-uYGvFQVtoS%0Z ze;_gP%hSV2CYm3E!xZChD~<=r z$89wiN_GQj(zWh&5v1J?;M#)|i-x38MMo9BNv;)?H?Et2#?gkB4aKI(AzL_doRuAc z!5rw=ugPrwE<=HBRRO)85PUX+Wi&cJs;?HgXBPOWZtXlWe=XUS(`a5K+mL(b5C?3|6#~WoYpN)p#wC2arG=hkrTjJ*%75 z9fC4qk*%9aCs5k&ZK*>x+^nJXUZTNbzDoriKjFO?0C@s3IhcmnNdBZ50D;cD)>qdV zGh>`de*1PVp;kGsK&zFhvc}*pg%Fd!O9eIOsO=SfQC61uDC1dTnVZu}UH#ma7;Ki{ z=K})+InsC^QfA{crnFp_^r(nR3&YKqSJ8{@WWBCf5jGpp$Nlm zjX9Bs#?|qh^No4jPQ5NmbxXnY73`x(G(8Q4RrQCjD=Zd1ov(+et?h0X1ftl|JQK;2 zfE~sedu{kSzE@Ai5q+R+>N+>?@U#w0k`g659#vmMb=;R*je4XnY?fJ3R;pYopvE6N zj!tocMl;FIc+_mCkT7d+Og){?f2ILoX&LxSBgx$A5(5B(7KIeYdRa^7?jVib`qR$Y5Fa?LDcdr-5f>DKAdcXWVJ(0b~h@B-C?Mjk``bjkgOh%R#SpV4P>88#Zug zpCRN|@mTzm0rWRPHFwIo>e+2kH{zVBgu4N`@IlCo@#+G9sN+PB75ln2ks09jRCxix zhm*JFjG(8M%}*TgwZ@ucC)G%s{#1;p8`V+H3BrOgkf)q!X<(8v-wsQEYVNBuTGyD* zArQlPdVsZX6{(_(`cEB2M-j?)^B-X6cVaL$=Z$tRN_*U8nsJVJrw!fiJ-Z=EBb&P& zN8tp$WS1MHkf~Y-Kz*!}Zf{TmU4Z$)1Y~oLel)IZ*)hl)ginRBuq=D>xt&`oh*V8jJjp*axt9!6c4;dKb9T>Iw6UY0rZ-U*#h-vHCzJ23YT22GB4;9P+)+vY8Yy%3UP`X8u!7)FD0-wLEcOujs34{{RC? zK^@w8t}9<8MPiAIIs#({Ad$m`!vVnl+V(jz<2C@{7Pk^N2akI8L&t|9@!ZzOdJ)+U zP}wRas*0lhH6_xfvCBrXJ7$?k&$+ToJ7b)8Bz$U<=aG)bM&T=oH(s9pgR*y|>w)fe zt2=)*J8+JIwiUGh03;R?MFlK}4&}E7XFTDu4}Tr7bh*pTf0ANEZTqABzSrF!BT(K zjaz+^vTJQM<|%GO1hY#4vh?=PjcXb0`^~acQ+gWbhcw6k?i{`DpsDycYX@vC9RWL z*q`W!WqY+{LR3@F`0*pkVm`2F-`Y#On@lzN8U6bZE^JM!ULaz7yrxsmb zbQ(~h>p{~skgPFYp0#5cZ&)_kh68ap##Cg1@$g4&OqepEJ|qyhJa^?rM5pYwKJ|h4 zD_)WMlUsABW{Rq^machBQqw{0C{HoBbGVb1FY# zra_kysH`2Vk$Nx_+!o(B3QPCZyV~=otD& zPID$dr~|+`=Z-b*ew>3to+LgiOGp51zpwO5Owb{0jqYh2TC9~ffb{ayK(fmdNghK0 zp56vWx-p(f10-M`HQ?Cu{=6}s%JVc=$RCPgIA5lQ_N278c;csm3Ta}3nio0jWH|Rq z$UA}d#z7d)w>sQ!$?8qQo5&hm)`RFbHQib~W;18TrPf@kBX*x{#nx%P7ZOYrnMMko zy~z!`KN%hL>F6**!%VIDMlIBA3RRF@>CIgtL0d2q(}~^QKye!#q{;5fHdFvlBe3A< zJbbM(miU3KrEwkiRyV7K@~Vo+(!gFABO+5HyUPScilQ>)Z)4n?6OISQvU=<=yBauP zaFU6-^ia%gM7H=q{Z(lH0QqV=jZFkIQnafmABJ6{0ET5b{++-Qao-xeeKQN}Kt$2v z*P4#^?z$*r5B>1e62WaVTI8dtsiL^kPXq6#nE>TkafXe*HaNh?eCtD@;l~_gJa22v z&e9|$yl-C?zF#kwx(Wy>ptaN**qtT+0GYqo-_puGz^V6Rx6U-cjA+nS_fe2O9l6gmBSOFUqMnw*jj*qfgPPds29Qa+y)N5p8{ZiE@-Ad9kLyQI-&Z7W=Umu9^*o*{{ZVW`wW1PK;lN> zbC5SJKTyd1;pz+?#kKvXwuW z{{Ve#^uMQ~&9{+c4?=j~)fJ4hw16cPKS~WThJQ*5CYk1cRLB^5hiMBZw*cn=FTwu) zMmc1W#AHNR??-d&ip--QYgh5u=C##PQ}q>=s*wx&pdeS3kLLh;f}A%bb|V8F^{O0k zSk{j*cO(6^>Xecv%<}&LyKCS3*#(?OQE$6cQqilpStMiXB)K6*PDxGx;F2&g#U2_d$;7#DYRA$_D3jJxA5kbxf2tTH1;kOvJ)wkScx@@8NTUyOEMkJDm?7 zr^6^E?+qU5d`V_&HM9{W{x$HAYySY*{^QfK!Ci6do{+m*rIURvWi?8p)WQ@tN@TL_ zE%`{!2W)6LZO9Xk0ftES^A0`#03>g-w4F|nYE-fGS5DdFlI2%ZMRQ11M6uG&6<}uE zsO&+>$!uqV&W-k;4p~dZxA=eIc9TgpQPojd9=58gj8sa+V?~9CPyxo(=dkvka!x-( zuE}$p!RX~k^F(giJ5Ao1>#%8Llu_O4DfJ0H%#$8#NPe4 zMrmm!j5`j>g14#XuvRqjLoLeUA@vnR;z)OJS9c=}4(yYT=eDgDJh=Kue(u-`+#_ik zNbZ?d9<{jL2$rValrc#H&rb|YOMoyvJaM%N$>5y#?X8@A`QUJ5wgbNP{{RtO$GA3% zrN8w>+610jDPp7%7k~J9j@hJR+D0FfBfbwDon~~2+xF`p`6&)AZ6#edP)T`tRp=&# z(G1~MOqEs36_1~A1CgJebFFNQh+QQY0O9$iB+H6Mh~C>b&7x}^?xK4^f{NO+y1PnZ zhE_4`1Cx+;j1U3ix$ms5p^9=Hyk}2xM#Lv#emfx%IqenwS86Y)G*&L8kt=9rnFb@O zN+%n=MjMB_7&}`T|LT&J<()Gr-oSOf0}~iQ`$2Y6NXS_O9ty17*Ie%rzG~WFcY{cBh2qS$!YNypUN4 z9D|Tp^S~GbI&LWD(g_Ft68%A;lT&!0gfG2UM{B42Nus#appZl9M;a`(tiu>$akC>Q zAY+dh(^EOH$zitFY(LMG!|~t_B(~4WGU@*Ss68P9#dD*A;Uy$=;IlfMlBjn^NXF&_ zjz@g=J+-G7SMaH?g#LV?HSdnbH0ox2Gnf5`U@H-`6kA7=Y zE&X$Ly)_Isdep0k!~;l#xmWI}axzA7oaA@YGH2-Jnw6xjT9ZNkqV)AWO+_`rs zv843mOxbnI6izUEkB`@#Or3T}gW3ECw%ZSyRq*2?q!zo}zNK{iE4@S7W~2`4iL|pl zI_&^6W4oMkqZrOW?sQyi8Q&A~B57$AM!kLh6>*4j9@zzeXm+5L>($n}xGE%qs+#L_ zk+!Wq&4!p3Cvli?xybR7JdJ5%W5LsMKG7R3#8r>{xhqU$9D(+CJ8CEL;S1TivRidU zRVu+z3~H>+9YssIVEIf&0UKX|k~@z1)*oBPlL~i36t*kV)GV%_oezRMNQ<9)o8f4F zo$Ik;ri{qV4&-FraF~-khiNhlau0mB#y$>mx@7%J96DSe;^H?TUmPMykrCQPY95H6 ztZJXAErMl+zLqpmy@2+p?8F?8X&C_VfODnh`irYE{v5ElIsX8}f0_`H^>;wuSjBDkrz8if9P*T1a-8Bpeb! z;0zxp924+0=QU`_);A44Uu9=V?Hp1vv-N!~+FHpvqUSwSG|2NoN3@kxBPG%D0Kmt$ zgZIX^Ivy(@mE+Fv2(TP`ZO7xns2c#1y=C=}rlz1V#Ys_7RTQ42W6&8nz+L(Fsm?vV zdFLGKIkP&dH&_JjK2c&}a>;*EFE^HTP1ZrFu@$9rBKO=>@R_+r2N#8@Azv0ggfsEAl}a(vn9!5hJVI z@PQ*9(X*{>E}3qcrM4Q#?$(*FrdEndN_G!9PDaMS$smKr1C3$i@!G;BSO z;D%)3&*Ui=YD#*Gb3-jEh*?RDM7bylC0Uc40fCjt9rUp1CUSg9#roIin-RJ?4gUZw zaJO_vO|21ns_TsNOHUg?GKPiYknZ-7++&Pjj9_Hx{7!`@Pi&IM``}w%QCx5T011#w zwkwnt#HP31ZB)&7t4So>iR+SN&d|jH&pp_9=Z-O^aHcspc@a844_E+Nzu6(>#9yVT zr|Hx(;*~3EX=iBtX;LGAgb)?jWO4%>430-5jzx<2@bR9#l`h9S) zbtJWs-3`i=#*YNj3=mL~V{kG+B={Y(tY50+42`A+(XqAqqF`e(DfPF|{H9Gcw($kV zmU9(FLkEsSEl3fo6Tu6#jH%}Y0B|v{J3mM2&2wCFU$?h$y0j-7So>9LKBw~i6DH|y zm95D1mr(>^DIwUr zWCqD2c0oDLGuZMz`W_}hho-*{M|e^hHB-5-g?Z12@h7IqyJvW65(=PcC6xpF9I1DA z2RoUtILT3r066e8yn*H3Bh8;xe0ZCvhmH|TT9#U-F;>w>K^SU4==j<}1%SsY!@2Lk z*FDCAz`E?XvA@H@PT#s{({&}q<+WX)s1cWrP!NEK zEY+wq9anOsrE8U%<2@|Msv1IlOftW4q+qBR_MX6j-${Kzo*jtEV2rpIRfFtO8(b6+hW+tSiQRy&jw1AQMamEe^C(gXfW%H-mtdbx_TN~O1h{jXOV#jtii@IBOs6! ze)z}f-%5RCFy+LKEH^i2w!q%DDk)~3zDOwSwHG*P>!ggxr6-19i%#eA6gkQ5w|AWL z&uZjH4g^gSO440m4mamz&wHwZ$o*5)vcf9vDG`#bhS!>OEbSmYoPJ&Buv4Dn=Tw)d zPW)HJ9F?n{59XuzKq*m2aFUj-VyKd;N;Z&8&?^GfISqs(ATaOx{@V3FPD0!$gI_2* zsGYlzps^QOYT7z#c_6gWyh|fT7&(wY-~vkl+art*jd-q*C6^J>MQ)Q(*mGCP8h+cp zsZiK#%RS!JQAHI)kQO{l(EvTp6|i>y0Mp=W&~&`1Fc|6PjJ?j=(}gaJQ%Lng*RH;{ zR>E3>@k<;Mw%yPb<=BG5AmLPo)ib*GHdGQZ_+*pse4~a90ltX~*HYSU)i(=m&Xz}~ zodkw$;HbgEfTZ^WCwcGp#*Giu(XuAOVI(vGSH*Vx5Zv1~rS$&*{{YMOgEf+x*&U@3 zu>sukB+N$CByo+Pws^y1InJ#fYK$DsmKz6u{m{IV?M8k;bgpLAa0JL&SNp%gP>(t9n ziSGnFUx33qF6b17Djfd+Q3`%Ida!&OqJ;Z<)0~p7hdM=%l2T^y}#G#}M)yVSpq$grx zG2Sqaom|=q>6+g{=}-7NO8Z-o%eO2Q0ndHC`2#1o#*5Q%+l?cPvJE6qKI&n(DWAv){{Zb|@uoVbRK)u`Vv<&!TOG=@FLRto?5*rS zP#~qZ%M;orh7~@nYVZw`Hs%Yot~oyM(ByHAc;S!J0rFM)+H(j{kdw)@+v%UIbk%i` zQOQX(5L1*-<|?y04j2*Lk8VH1r6s|1hVSsHj+5)fy_V;%3Mtclt=^>0U&B$fgBXjDD6oKN?#=+FIYiXvZsI9!z%dnx4@?ml} zDFJ!e%H;fje%kXwSh{>H`wl(5qp|e<>g;8Z7niGojDO$9=;NIc)s=4QAr!nL#dufNOPU zJ&bOuMV%FRwOHyZ&Dx$jeU?>t>8he|%yK=*6k~6@^AZn#zMbgo1~(8j9b67k+#5i+ z)uXz*{fLTty$yHat_d0RCY%RZxC!|=8^#73jysHK@?eQE8u-{^d2t5$ulZDSNAMkz z^7LzKM3lE%%_2)Aai{Y^M_CIJ#TiZxTL5HpljB$VXQ#UhB47=%tL0xU#4x zwnEDtB?WDLby$(N$`AmDIV_;#fsA7q8+93l^`Y zN|jT~4`y(~0G>6mCRDJx-fgY^A7A}0Ylz$}H#Ktg8(XRC=xVy`9$Ms+U z1M%c|)`mQcE)1QTh4X3~q5C>P9eRtu{UxEXLb1m~HGL|%-5dHR1tHm%4S~8~F)&g#`2ZOV-u3cI$<=sQOaMCy`vKq%4Y~8&9i|?>sKjG57e?Wc2=>H1C%h zF=ST9{O+P0!pNzQ0IoB1%o4iRLpA0qTyBh%1>^!u1`WVh?lahN@v2Rcmx+$g9jC&h zSH*Wt_<*smakrjVn^lTGQktFGx=5NL@y3smJ-{lJ?oYTAyPiDm(wO~Q#Qy;HH-dcz z`}!xFRchk@0QB>#5*A0Vn$>c8cQeMz>nx~e}4ejiB@uGdJC&dxvNw40jhoUo2 zl%?HG%H;GH1*A5_>I!*~Eb$hK8R7$#JZ0D%v20^*co@^sM~?F5&O1Z&S+}Q?Da7oDnfyUht(#G0wWs~TAR;%9U0N45^MSZ%ULrYyjPY99ZcpX=8 zc|A7B7z2!gxF__^Go-$vfc>4Zxa;5gBEcL_9z3Rr+jP6TS?cf9k53g1QrAtnOtUj9 zNAB=fk8wPbetT%WI!rcn@Yva+YCY_edr?xe6sU2&wk+si{L z$23bav-+LDM?M!Lw|;fI^!Sz0j{*7}wymtV`uic}^c+?^9V36A?yY)8^=+`k*6X#h zda6{a3;+jezCp-l_Vzuqz#em;N1Y?3AV>)w$|R4&{pVnv>(q9Rrnga6P+jSfC5?yR z+CKrnXHGN5LD_(Ee)_IfUSP1xKHw{|Eo~ErQ1n%Ij+0ANcRSKYXO7qSW*p=(l)gEP zgSX^%2Zi&Zbj(@Z8}g+zTrYoQ;E-ROY951XRb4-S{CsgxK1@@t+=VE=mmAdfBOiX` zYryrmPGtHglr%J&C(!b&4GRPCR_oGzK}lOAlXVmcNfCwP5W*bONs>&8rzJrHZgI%t zPr;3hWAGU8?_u7RUzX#r@~M^7)bB}dt(K|k6&cF`!Haij!5P8NKJV(|;A3c$@JIYKbtCPqjsaB@AzI(O|LNX&_2@o#$K{uWkei>{$G zGE<$L<}tc00X{#{n1}reJ}c}bh2CNEjIPFT2V=wCM)Wg)kpwsAPkN<@-lnr4wH?h za|{B(1km4e%FEIc64}y}6f~EJ`ijXy13J?z)lrG$Dak62f}m%>OVv&q%AyJophFt-RnhawOVZOv}T})9II4*QW*JGA+S{ZGguR^I^UzOv)!qx+OBFjR8F%bj2NcTxs>D;Y>Z$Lz$5jf-9HLG zp)AvZ;OG=Cfjz(|&4kK8X{D4pqTSMcKYv@lN?sk7yls{mg(ZDu_JWKK0~~^Ke&0IK z4-LeQUY8(>H-l{+ZMy@0_Ej=6%bnr5wnu+dJl$)4ueysZCstNgQWp@ryo`wrMlzu8 zz$CXDxZn;(vnMlnjFwjF0B(IyC1#;b?umMO-*M`5B@bKm9oEe}ZN1vrS`5%BTosW( zD!Y|%F~)v%=zfLF#ma1x%N>AR-OsXXBPS`bX^fu8rrY###dH@dH%ayHRaz3F6oy~N zMH!HgLEP=Ye}LV9#&fR?()zYG4pgu|9JIZKwq5F%$JXdUlXGPlM{w%DroBH=Nz^TG zt)rw<1v&tu)0lCM;Co3McW2YecyBil2bh$D0(5+g&_4)-Nlx*x3eT(Z~QP$Rh zMQXIvQN>Vy;VL8AsSpH&MpKc_><|da$;No|JwFuVW__n0$O$BXFLUluLtiUg+7joE+}spCD_>bllwDnIDNW9V1C zb=g^rCuC)6`c*x(W1fSku9sVQQcX3<7bt*}u^o8;cqhiaM?}bOQ_i8qd;b6`R3~~> z&w9G_-Nsf*`5>Lc8pk7_q`%TWoo>yI47H z)~N24om*3CgB?j`7I{>(5KdVoKF$ltobAudz3b9~AD*IJ<1D~GS(Za^buLmfS?}|uEAb;I$Dtel} zqN1@@-080u3cG{|(fYze8^)}lDB0LICoy&*ay#qMbPl8oq{FYdL;0lM%|ma+AM_X2 zZolaMrlq&vZP%%+vePJ%xHiysCkjCXoE($x1e1<&q6`ROm&S0R-1;tHXgd^n)ZJ~- zeRgK2>N?1yo)f+lc-ZZcPqZ)qhFl%$$2rOSYS!Rvioft6O2S$|=GWeuvdaV2Lq%?) zsiZp^2@!&yQGmy8+=738`PZAv&FW6hem*uqiH!|AiwABKSpNY2&!yb%if&yc{{Zs+ z)8A1^O35S7EgY`0Q%1dtDg(5ZVikGpdux4<%nb)4CtFWDauO1a4WrLxlOnNM*5N&- zqTOF5+7cjls~c6%bBP;pQ^KCao^j`1RrnbKSGri8;W*x|)!6q~G5Wv`u~RYRFG6mgI}fMrM^5s%Xv^qmW-XT66V+N2j?4geq6C1QpUNob<7 z9c1)YTguxyTJu*!YEn{?T6)^SYO71eP@K6|n7+DH}0zsfr{G_Njw zF=^`Sy&`o5RaFgzV^FXptZ^!TGib(6K_P+ti8|-UAk4#dL?y#tm~ms#7SS!b$x~>o znxcBTn)^o5?#m!mfb1jHk-$4Z9q>CI-gSPY;@+qt#WdLqi5%(l1sp$Z(I--UMYq@> z5?Lx~u5_PJ$z9MO}o)ScWUYP>KQ*uZyX$vySxzM%SUY-n2W2(#!t6S9YcPBv8P zYbDZng2i=M)-^1PQy$oP!8>q3!S@XF*!=4Y8acz+spf& z%)%u*@`P<-!y8>m9C^mxMuD4^gw_!V1;b)0x9&d#%Q}3F02!R(cSaO9b6HNX$z4_W zq(L0AMF@o*S$QfBHvk4$oO7IWr}~uf)|_?*U0rRrf6YE4EdY~hNV-W%Cxd4jB&n-kys7&D2me!dsoX zeX22@z=q=o`gt1A#F{A_*P1RIZFI7_okx)zk2^e)dvW~L?M3d>ROYIdNGfHJ{{Su| zeUrtvf$4*SaC3q{&wWmGanB#yTs66ePJ_Y!0MZwZ=^P_tMXK1VXf4$Aw>s%6t0`)k zAc|Csv8Q(>Tye`e!6csK_|mMEryIv^z>+9zJY5tY+crMQfAtp2LuHCc?essy?Wq`O(`of(I01b^ zJ2u?zBaw__UeWzu5x@3GG&I#8#QUd@i1IAjOjcd1y$K^RnBv^Oho}*RB(VgB0PgH~ z2jCoz+D8@v1SOF?k-q$U{)!w6UP6Nel3aJu(m^y*vb57WffBJK23B*B0SdcF^SJrO zw=qlS#_-w+b4Na?4cXv!D&P1Gs;y&!rhrx?v68JY+9Y=D!yJMefH^#8vDZfv@yM4k zx?vY)@17Mh+zZ+V6o&6TA=6zsb){nsRbZKBjzw65u$yi{Qg}?T%OK~0=UOr`t$&S= zF5GDw3O&&9nE{heh6%1~dh|<=;OS4f+M&WZY1#R2RA+0*K_KV%#~(UL*zJwL5#1ix zcMbf1+rTu5DI4m|R@f zFcRKRDd}P8Dtfv~dddjtZBj`esKig{C?sW?Q_cqRpx|fS&bOP=rDGqNll!9?s~%$O z<;UF$DJm%=XAUV7)Hc?!bzSDob-KdVrV`B~g!J89s30~@K+j-KJ0G^Sx(;cFs0KC0 zW)>6XvCsBYFK`q>Hu^`?whBrMmBRgTNh%#=o{&?%#c|uwC%G5}xg#e$_8QTjr{P4` zx=M50(glt0w(Eo$XLzi)k2P0kz zAE$b_A=5qW0*zQB`KKeC2w2G>1YfT~rF9iOB`AY4LX(rWFb_CB;K1Mv z9C$byV>ic!BIzV0zwJAM6&^wJ5lG(AXQ6u>6VuU9ibESPjuKzfj~ETl4B02=l1E{s zbNY5nni~{aoM?^ve5$f$-pWNBbP-a{1w=AbM0Uci#pH=FM#8uXLCD%XWaq}8#`=Ka zz%jC$;mc;f9ybW>=G&$I`a>} z>JFdnaCPl@sjE7%=eLVQRTI()X_`X-+GAkDg2eKy4$+T-zDBk% zx+K`HA>MDA?3HeDl!iCWe}c-_>x8#!^{yY{t!PmbFR3JLB(AZXoH$}R?}ONnzP4h* z%aw)jFuJ+ia+S-Kjq=0heQ=7cW{#~Dh0^zPoH`JQcDzjEl1Ui=gOYoZ{!F(IsE;Us zKD*cl{0Q%4%-&Yf^i}uzI*Y3M%OyoMWQF7P75zC?6k`eq!gj_-J^pkf(>jCW4TdXt z`@+V3gLl#d3{3~{W5WHJy-V0Uf+hE-g zCM?=~TzLz0^Ig!$>ifJb%SRm?kq21z$~OrkJAI*vEb0LxBk$Wmmm%#o2`%(g@PTfg z=WSZ{<83_9t2WRq{g{0}V(oSE=;0Sfg`B;?ngiYhbR8XzlYjuoI+S44odGn z)A56k9&xS5v;P2&*2Hb@0M*hriMGt$5p?zT{{TyMY2l`p5DO}>eE(*t3IDgPgz3r6ew$m%#hB%^4t-&Ktko0lD^@_KI2bx zTAo^=bWf^U26%7Rbc61z9UWX0m2oAeXrq{k5)!D>aun_#l#Q*=>85e|pvD6bn&0`M z%Z$x>S{Kq zYaPH^Xv~cB6aiz}6e^cbk%BqTbAhTvYhbi;S|ERf(YfCL07TB|I-awwr?>i&RP3gQ zml3GMdCLh1_Z2+x#~AaiC+B28W)fZUsNm3c3hAwM&pdSsW|E1SM5!Vc*o+7$_~UMQ z40-46todM?Iaw}4Ye~Ls42Ie$xzB5}QBfqY%(XGcBuy69Ws&!drzD&yk0*>|DF>Zx zO4dy9;kX~7=L!~3tleMJ?=>V=n%aA1Lo0y+jLYkfaXG-wF_D4cfXMHy7QQHdcwA0R?yQW6C0rZTrQ2dq#*uq#DGasa=GAVS*?kNEmANG zzcpFk`yxH99#Ft5x?t5y9hR1^mP(2RrzEQ^H0n|!azbw4oPyW~D~yqxYCh2xG=2nc z@c`J9wfErzC#A-cWU1fDW1de3NcDz&w6noFreF`8L(nOk-Oih%IS#njdOEHZ5n4kK+xEgn@38R2Y=NR8%rJ(lpwVyK! zhN6_!whN8fy*f&pa#qC{RK$i-iI!(B-NE3m?0z)97<{g4OZ5oN@88*7(vIrFmVz4% zih|)YQq@o;MrWm2R2X*qLm=!_j^vO(&sy^J2ROP{GmZUGnHY1L2Pa^NXg;jhQl(85 zOjAp>*{LO@3GYxtCj5}Ck;VZ%j+UAJArCD8{#znp$k~J3)8%QReGm0g?VVvoeigD& zG*iN0Ei3vU$L_!>u#k5G%ro2X+e_m{*Sz7~4d1A9w{MSis~N`w&0jQ_oi%Nsqibzs zFw%ZKK#d#?(y_;9Vc!Zfw;2n8r221FbfZIJ1KlzqE&IV&b-c@8b&sd4v@_DGL^eq* zj2dYP%YbrnpkVQiay)B4t48Uk48(5DzYvYK+OO48R{{4`cT!o+Q~I=%D#ILplyaxI zN4N}~o>(_?<6g%jt}-s4sqi*Xzy%U%AWxc)2sfy@GM z`QYU1KQfXjmQDJ@^Z^9AxqL(R!y=#K@ao zON3Mh0{H&`Em5ps6a^J&ce2F|JJQwA)U;DZ##)a>%d&3_i~|5i{{Uy?d~46UJ}I2o zdOk^!=VCbLj#Z0VMG8UDUx>(0`552duO)2FI(%9 zV?hjAP=esmxjXI89;)z0T^x^a+#R<;JzHt40^IaevRl|NaRG;Liz|g!W0QgI=dsVu zp1_Hb!dd>}9r>i(OSf`E-RY_=mF6)!dhp37uB?rj1~7B?901>9PB1ila}k%q2h0Zh zc1C1Giwl}o)zMtzt*fk}T6kauR1(n0A1WW5W>Q!#55@;NEZsjh#npn%Z52lA^jE=Q zYsql1j4A76t1VGI9?EY~Lm40*rp$X-kTMl^pJRMzjOsa2xbnk(U=JtVFzi`@GHLSq z+G%vJNA%>~IQJVxs)o?9EUi!J7Gm25<->nc5>)5L;w}lWd8-KTcKkl>rP{j_e-T8OigG+-FWf)0>XX5M;UF8`4D!^`xVnRL~F?MRZkl zB)4knsw!&OqKu+Ijxy#?F1a9u$8P#sEX>!~U@l{4Al%#ep3KQO#?o!+8`mRl7h z6tYxIOd?N67LcA>4h}~c@4zH+jXq-;6Sc4(-PYuK`jOnMDLR0He+|O!>eej1)ZHnr zS6$FkPf(I7xp}`hBl=r7&Q1yKpN)D9IAF)hIFD#>(|f02$!pDnUkjDGO55d{=!pz1 zFp1)gBv#qETad*5W6n9i#_agi(%!3xODs zfOQ9PXuuc)p8o)#`|Aox*w)jHehH~SE;Nt6o?C}XU+QYF)zuWWF;ciONVzg00Pj$I zc^vXV<4ts~u-tYy8rY?k+GlVBXCb4ymseG zH>gD@HUkGXER^tZE(6&c*^H9IS2VS?moHIKxL`vdPje!#Io*Mt;Aey1S{TfOOT-y2 zb&Z8`PjAItlQ>Gy2G%U8^psb*{Rr%xJ96p(?HQJ6;{jlkKHRbXlh2R0#+Sp8Q}LuV zEPD~j1n{S*VoGva=0|NL@51#xr>Jd|tzBPXiK(WL#1G+_o;e%4I;1cL@^L3@`nEajN2lbj^M^=qJcz`M+=kcDg54^1{)Y0jOX^wvpS9ovQOG? zNE9ocJ=Cz)#W<<1G`DJ5KZRP=5I$o`)SpxiS(OjbnZ;wF`$&lE{KpT!#_YJx&J z0Z5VQ;;*l+tfPX8nxy{#E=hK`rn`A04%pB6_UA@sRLl>{)^xP-+kfn%Sab|;eIou; zIx5R;nABHQTh*$72$2!*Fbw6$Czar1?~j~oA0HX>G=Mx=cOv->^uI-BI9AD}(==Tj z)Neh{_+XF-f z7kMr9`}ahsGV?j?%(){hFhlnM42=boMDwCEZn^`D^hN+uoo9~sb6V3wk`L^2vs z-sA2EvwKbs4wdk*=)6s>{{ZZ*iS#GkLvMuiv05UAfolt)B1zufl%$4HouCxudv0D% z4hB1ET+W+3xKj*vRFAD7R@;I=Dr9Dtx6u$$b#23>;37+9MNwo%mZ8Cjt1k*b-~uo* zPtT2F{{X7w{XG|lD?fPpcIS{qcD2t8H>uh}1YWnc^%BWjI@cv~O)3~8s~eRaaDSF@ zwJ?QS`;PkbIPHzh@E;g#zk0p96-POP?wv}W`F*3jM@IruRmo7XtV<+yqYekVct$wb zFvFfn?eVFULn!eb7?tq(%Shw#MR={kQD1FBrYgo!dWRr9JBqq+x!_=oj=&$vdDd=c z)CN{-7_DgD=>AA;ApDh4()~MUt4f;Qp@j_fnT>Wa-5e|6fB_B201i%0JnPltV(Mch zV{X9wHnj!G%WAcB{RI@2lr;H@P>{Mjr`3){B#f>ADsTZlaCqlf_}H=HxX8oiHY>2T z+D%;`s{^#ugfR|GLr$gLq*07)Z7Y;EHt3<7cSI|g04N1^jC|^F zVVGpast_6vD?34L30z1$k95hTg4z|UE_ByZFpSAB;U>sgd>yQC265XU^V=H8@ftd| zxrU~K?sxS~K*1UP&d3X{I*ARovO8tkqStgHSf!3?D?#KY(j05939$upZSmUra zXl`UDXuz+8;`)7Trb>%-zM-xZ6t3BdqNfZGeD2zL+(#$20Qt_F>G(M2Hp?tccsq+1 z@|A>XacKS|-KqLz))y05M=W&rH;St;dRYr1?)L0<#!1g4asIl->sXl3!6eLuqPW_T z+a8ofY3Ry}4Sh^EI~6S?3%XJeZxS(HLlem?K>+YQynO2eslOz>zh-%KdPlGa)9_3( zk0CtzHLfwoX0_CWV@%5O$nmVx$GDQDla0YZ10R%U$%obYl%Au4|Qi*Sn93ypM<&7 zo4m0Hf|@BquFi09NIYkEI6bsUCfxXr+lo~U+_<5xl;~S;Sv^Fz%8Q)!G6XWs9(_Q` zxd%VcyyKn@GpC^F%$dMQZNyj=*ds#iF_xZD1?%a3cS+eGdwSMSM;eNnhM4+NG@FQY zW65AiIBq}BUIDP@vQXKcc@?<)6&TU{OCR1#Q;B7$mt>MudT`R@8k4x58{LH+v*WjI zdhfBzh93Nf7nh3jcKndel4*e*rA;+`x|)i)>E@(Zgouq8JT3y20|oY#Ip+)k_{Oy{ z`aqTjJH#LmJ1UnjntGvjnmr~Q)Eq; zwldO94XsQ1d6)j{sCL`3>r+`BH7iFH(QIje5-+_ZY-eZ7amhWfeDSAxzDu$3n(y&daI{H(s;UbcM$#A|ifobzRXIjbGrR6bIsxj&cr?nu z(s(z)Rx-1eI@J1`eQm;}Bcp~3rNL>HF&I0NAaG>e+d1Ipxg#ERl>~A;7~}K3yCxeG z>h$uB`Xb?X=Ge9AhLkr@)XHl>R*WyYTBw0J4Y(bt?%YBA*yGNV>NwI2eQ$Yrus21N7kNS^ z>29iP-TLo?ym*NV!Bu8l!0JVHj4 z)0s0H?F6*k4oEvd&we=9kNQeDXM_xCi|cx>H|KPHPI2=a1CL}b*UD?PazCXyQceX- ztgZp|R|lSQGT9*qZ+C5aHf0DVI-9DyAMXxRHW0;CnubcWf>S$wJ7TzKvJ-B3_$LRD+@3z# z20Xa<@)zPeo+2LlklfP;m&)}u6gpasCB-l2pCEG)DnzCAOi1gvY#;^iNCnOQD@%nbb(EhiT z^WkGU7kOmW`E0EVp%OqX6f5BhM8+5Z&IKj zjvN@wTTTQZkIGk)Nyi5O={EIE4F@Fg>$+o3)!766Lg|?W%6dMVvYLttV+@lB-HNL* zBN*p0Wb%Fi_|~iD>65XtM<4|7Lr(ty+Lsqr!2n-=l8TE3WyVSAsrrg^of?%~03}5& z8OCyDARc%dPCN6h-=pQ!=^OsSJ*`Gl%ZReE)%xp4X`mu?lB;8^Z2-v-jtR-Z-GTtX zJ@7T>Sl2RVo;=6-rEbaxZHB7#zTUNK6@swSth2`ZC0MqC%!K<}u@7LiI#d}-(wpmPGnXuDF=6V`>o0Tu&Og@V0N$R?S)m)ZFS}g4I13P_X0kvt!=F2PBdPNgRA&4HfxQ7j8&#;3nfOV@n&e zPd_NCm+EEG-3>iNu=*C%Ec2`royEuiJbQ9CA8rA_Bzzra;^Mm>Aj=ELb#^F!V9P#X zAD7nBQ>pGxdcW6KnVzOuRb*fxWs*>y3W3Y=PJdhtE0YGi(Cm@oIm8NgZ+rMba3Jid z?&6jiLR3X_f!Qz;SWZ7KH=`U8jl}lONY}5*=rF`QGa-b#fCr*hP}9iWA$rcmPf+&Q zDXtf|YRC?zTuzb@avDxR4n{^k0oHzI9KAW71TG}YzOD}6%?36TARU#z(%zaEtCghI z&dPmUvpVENg9Kz?WO0n1GQ*!bz@6C=xI*`mHb0US1+p->G&g_bzep{8F;Q-zilSRw zlu^$JNuZJwBBn+pPbWCWae>I>9Wm5;gbZ(okOQCo)9yWe)15R1%I#cf{Z@0j^v_q~ zc#4$&emM&hIMF;4`78$#%vO9icXmBExYZ5kg5JeO&H%8#MCLI%0Y zomF1910@o@O$x5*VBAQ~KcodJTzq4;HB5~BceseBE(eiR2eOyO?m=3fXX*Zqw=1Q- zlD;S-Stl_l^{Q1y2P6RN{W%|(0P~Zg^na>4eiTlCkSyJ9Kaf>!vMI*Z$J5&CNiS1e zscc<6ainQ>LrYs35fP3MvVh9RBxG=S_}87vZWmB@`jcnUUAB z@|Vx|Hr-!c(~4=`+(}&^QjEdQaz8mGumcV=rTSasZZmQ71^K(1ncE-j>%j&irhI8AWok zbpcOS|?;{@X*csgD=x`EA)3JDzNmg)eW!(wlZtA%|5 zTVFoBTv9qCOKOsu2w5`$l=`x<0G>_;&j*hLj{4sE*P$6OT3GVOwbpCCk;VI~i3?I+ zs(z~VM^M#40FIIfC6-4J!uv_XD{L-&5$wiE!SUNo^bV^eoh~Bnyh{Y%1J~U{V%BVx zM^Dvu)~=_tTxW}>?=>(wh^lLc;i`SRiWtTN0HCoy)BZ)7FfnnMHPcJC)CTYOKItiF zOvr}%iiJ_sHBiX{$8xvTTaY`YL~~?O#t$m3!N53E=aGU(15*pCLfk1NiTd2rTZ6|5 zz{f^-wJWCYeMeJTZM{c*p_P9Q#hYPs3l=3-Q`~!hDI0)bGu)kcoNuRmH#$bVkyrjw z7G``xEiE+UeR8pWD%WkOw#fxtQ^e%FPFggLTmv5P0WL{Dayx;YWM{Mg0E-;4dE_MD z{v)vmzrwTTY|RX+)8ExBDlDrjymCQW^DLWYjiO+~+;Fig0gq@00fqn?Nhoqz$AkOlyT8;C#4PBV=6*0)4!WY5tv zw$gdrZNh2T<~D}4yB|fP?p;ID^e|IXU7an|?*v&ZD=V;zv0~uld$|M-;Euyvy+b&& zAUaM-ALXk%6G*zE^$of;r?;v$$YWAu2nI2@cVaR)?gmNLw^5(%FOvb|LtbxC zJBs6EmNq5%sVh2;-$(pz(NkAD&s*yOBoRlHjn#q3aDA!?$2sym>d|HCw*B%p-}%?F zdMM>2xH7<6Kk4^ULe^V{@oGC&L@{S(9hcarb{LV3zDOC#*O9}*$B$gC$S-|*dT(FR zFN2l#mpB#%*ScWcud4CeYQ@rlt!S-V0E%Fuc^P|_2P^`W8-K(Gw6cDkZ&T^fdk-B|~!^=KM$3{EkIjIVh%lli#G8m!rl$AOrh zDSp+zvQ^_#$)p`o({)QhSZ!vZ1hLG24K#83!Z4&dgPn?S+sfw$zIB}60rCLT)?LO>a9!XUvkTH;Tm75=l6j@>VC3C|_ z8|*#*0HUd8_M}|y7W-pDY3uCpEKW>e>BjdN7+y~p&I@y#p7_>>)y)s-}oI~FR1qK-MYZUGQ}eYh$K`8~6Z0}2NotsC9kxc-F{ zXmICmRiFN&PbDMVsORcbxxqX>nT=`4nkbllTpSR%&JJ^v&b71h#H}NzGdjZFLY-%W8XtO-n~kvQ;5L94estk%j!Y;D81(@vWj}#u0d+7QF0v zKZ;yOY}BbPbhBFGn&s%Eb%svc3w|K}R7Zd60dl|tljmN`r(;PRMn;c)_CSgeBOV?~ zOWmwylCa$73j}4q5tyXhvHP%8V+XOo&m&%YCNT`O+9dD!tw2Gm>?seYTuB{M)EZja zi0SZ(l1QwjLtv{CJ;M$8yYZgG8h6Ame~5YE9I)34qWRX)(scaS8LgCS8Z==SaZ|Lp zP0T^bAeHQL2RhQj(n)D(OZZOk-My4GL)DYlu{2ckUZtd|k>ri=tWgsgV&2wHcVpyd z1D$#vfsPEOW`S&?Jr;f&T-F~L+4tVQ6_q<)ZVwOeX=Ul~#iDnNjJ8JNg`2)tAPivV zIT+S+7}gnMm+v{m{{ZO`&2zr#t4*Ghwsi!w_d8{CS4p`GK?_Qw@eWABzi_Mhw=f%k z90RX8)-#!lo2+ilAW<7%;Fn}lyAMl6yt|)d*6A?~J=!;`N`FJ}q8Uw59*eYVjq0O0 z8-~)1axwrq&X=v{$lYEW1Aogwn-9Rr5V^(1$2;+xRYAL7MkFrWgH@LSwG{EiG++qY z4{3KQ0bg!OU;*dg=Oau-S+aeG$zePG8>L^Bk4t8mdkr#kM95nr?OGG4SVUPk3WLEx71p0QPEclYgB3>a;}+Z z)v=A40FZJCB!iQKtUsu*@-ZKxPyySz(QZUf1jlr)Z5<-g)zVZ;B&w0jl}pi!wafc4Hl8y#ms6+HoAh04^1M(`Vlef~LNa1;SRR{4*6y>{iPG z5|#H2z$wN%XPj%%b$+J@Qi0InIO=z?9q;)pe0YR0>2CMxlKoB6w+g$Qw?|r`8(1r(B#ux*^zG7G$Re+no`RYG0G5bczivYX z&md!VNf`Z3jn+no2}8+YT8i(Ye!V@myL%KMl*$T z;GT6}TnakE_6L221 z$s->FCtZ{EK2BS;%Eq@q(lprEaCzl9{)rO+d}NS)O1SCUS5S4eRc$pAJe9QKDdTL=A`zMtuut3+=--T>7RfTnjK zv?F0vQVS65zys~^!OkloH2yeg6nFmw^dtjH#!PPt)}pg9Hck_o{dCxNdQh4sfl$#})fd&3>cYYmS3DmlOl;Zt3@OQ&HBg0kmX0fEBE z)9hI#VW0VGeXKzKt}uLQ+`fe#Hr&O!6q(t%N0G=s@rO# zs+zrH^u0QneMkW#PZ(1lAOwu}%t&(^J8MwzD@9GBt4mIt%l(RXKc%TOlPj!~r19IVGM*G?Xev?aM zwIW>6DnOB@npnh%@BE-~oys}M;OC7mCsUUVf;@JD9swl!kB-S%FdAN6WE=jdlFx0d znt)T)Rn{cMzx`wbu(s?mkwD=_8-pIM&7F-6=Eo`ip^)d~jrO1ry?0Et z!dH%|uarBZXLWMO9FA1ruF^;!n>ZYI#<21JqR3z}Mgb<(q!jIqp-S(j6Vp!(O%-QT zS6n5-!5EFC2*lvBvy!X^0LDP$j15wJw@v%$0MzZ(15Fn8D3Z(c{{X1XRTVuaPe~)l zP9~l-13Q2tcVNdXd*_e7bfC_2TdF342da#AG|C$<-S$;izo$JzY`Xe)+tI{wx~vmS zThdVMF}Jb#@A`JoPgj0Jt?jPJj>d(DRAiCvq+5^hVJbs%tGFv2f>NjR2FPL?gWo*! zfvI*R@_*Vi2K?9ZL}!$j1ks@sHP*U%%E%y;Dw56tRT&+bM71k)3JfeOc5c1Y@*lqrTzS z`S6C=-p6b=_v7c~RnH|%G&cz8sj5gWBLTP!K_eOR2;Gm8agBWE2c>mchlvz~fz7=3 z=en@BZs4n|R*L)GT}5RBNg#?ZN?71^0f_e(0QYZp-P|5dd~4S9=mu`M=9H{&bKz8b2CaOn3ieBwBd*wnT|>QxZ@bt=**3|A<;PApO2I$uvU_@f7A|? z5nS#yU&NsFBreJ6N?$0%5t6_HbKn9#{&bFPjG9w3M#uVXX*`@(i33L9QQayo78SX? z(^kBdaU>@NaJ~UAxBMXImB(Y?>#(ukIFv?rj00!<-8(ayO@T;;qN}GZ7RnI)Ql?Uf zjYs$+8lsdK^qdSF=h_A^GvtjI3mQNiXC$7hc zs0;yJz=tESD~w>CbK@A+ge_-@a_q|Feu?Rut==zw7pqmi_$t=nNpbb*)Ow;wORF$a zNj#9sxc-t(2hKF>FejcNk%_x5teQ99@Ji6F_f0z9ps1xLvW=N)r9~?mGYI1aj>M7h zPq-6|^Y_xjr{qHISrkXjrSlqNZoWuLxm+x^2x=|!r6odKMNu%eOl4J-!=!I8&)N z?3A4z)bz8p($8uwAue7(_+WGI*fW#ijt+1JyzXXKi5-gksaU$X?wD1w#@2|I$8~5e zx!%$)CFi;V4nX9vApNu3R_fSJ`w@~Yo#pReG=31BzbGhlecJk>vZD82SwjN|rIsME zO#>c%#~kCkU|?;>jVnHUF_8r1>c5?pNClLJZ#@Y^Zmp=f!ChA%NlX4RLXo;DIZ#jK z0F#Ul&5%wtsrI;9_ObR0Phro>nAk?dB(GQ9Z82UXgZRy^df3VW%y=j5u5X#7ju%U9PfuMaZWkD-sYSMkEOAFl`^(I%q=DEB9AJM|Njwci z*~1yFk2hyl)GZi~4ktJbiTzJ>UnC1jBo(%pt+vKk<1+eCsn|&ZR4Stc0nSewbJ(3F z)bhGy(Cf#0!|V<#{%i41G8b$H>UGzs`iR?%cH6}aGDuZK#Wo0|Rt1zd^)dedF9%t! zw|?`B>=0sWNVYhU?xE7ff4R{*R#l1IdcK_T%Hs{eQX4rWC^=k==gyhN8Ep;_G-*B0 zb<@ni0VvMj)Rb|MD=DdJrj2&g#lLxtw}ZPQkf(QJ^NfSs&YY9x753tY2DbCSOA4Mjp> z>D|D1Sd48(Zs4fKF_GUoQ%o60?LeA40J|QDYx~-{spPhz+`qbZ*eW;QLoE{8Kdy#@ub+3<@fnY%iTQ%wtLK#bXSU)somN& zjX*%HfC=C;V~n?P9(_)XoYkwZv}Ttz>c0${E?J3a(tcjoh9I!3W#={+cE>L=VX^ zN!)sE`mT~09BqRC07YZeMywc;q3SB7FsA%QB52fh{McUmMmzFGwOfl09O3bR2_vxn ziEP8RBrDU9!CqX#%SNhB2PpvG3&P zjA+BmZM+}NFC!iZb4MPj=TFi7PX#Ty<7&L!YN}n&t4{=B5}Yu`_}?J0?cctL%qK@L zg&yy{?}|K8Nlz9;^EP)H1CS2i6qH>_TYjRt)Jan%-f1IsZ(3y$$&?rv0A*wtJRD~` zyYr#D5X$GZqs88nyb9sd9YLs?m5xd)Hj zFaUB`fyWxv^G7bZd96OE88hXVdDy_;WyYFbtgux{B^?!Yx~fDf^3;-{fZ&rJHt~Ug zax=!FTCR3LbG10IayMEo%hcCei)++Xz_lmQXjRgoL(?(axaCGNzSh~!-27-R>JqVm zjf@uho>v#f518z&C~fIHEQgXrx$i8uhT&Q1?+Zzr~Omn8DG%ye&??eK}I zgcnA3wRE&r%Pn;^Mak`nMvTcUbyE9APS7#jgP+gYMsbb~l;}B)eo=FWmXGGIBfPrT z8x78<_}tezN+_tQnq0ufKuI6tQVv;i7%p%?0G#>Ql2*4XTN^>d4ZABbQ zQ({Y#RnW;zHC(_vs`2}9r;ML*4csvHgWQd2I+*tlUj6?76W{Sn$CWh0L%;#NQZK3N z*u6PxDk|#arb2=zUwatH_F+KX`GYR*-H#)kDJDS!P!<&GvqcNhR9kNaI_yEvUfh5oCwg<}U&+vTTOX(`myxKQ4Y3mS(3 zoJW8|b__o$=eg%f$JMhZds`D5ppkuzD(*Kzy;*#go;s#c_{p33w6#={V9E)>-U$w+ z4oF;NjC|`-Z2VkV=E9JjPx7~J{^znBjV|GLw_2}}(!jSmntG~+jTIJ^_kaQI?9VJ2 zhd*vIGp6I{Q_YI@V{a{h0W{xT{{RFA;>^*g@HSm9zfJCUDdWGu)ijW^qygSVc%oV;~__x7;ih)6>IOaEe5CrZ6Q)h|7iy%t$zF zVX$%AllS-EP<4E%a(p1r)_@4B=ku-A+C5R#1r@@gy2o^eZOc^Zz&Z7Q2%)j+5&>W^ z54dFHc?VfNR&TO4EV+d`ORqpeI}O+iQMH1Oi5{v0G~!p?2vQE$BOA_mC%>LSKcfF-9*oGE`TlB1k(ahz%ZZ1S*4Kptp+Ut~cfZWh(4 zdTRGeLqgQ$YG*LT>A>))3{G$vcVSfiY=TG!TTO(+FNVWy9D0%XAxR+u{S}>IH|pv- z8L42r2ICCja50b>j|_zOCpiZi@qJtB&;DqLv96$ZaI?2yj_AlccL*=)^-o2{=}T&& zN*d5jJo9BEdYHoDh)(-5co+bM86#T#Q!Tl$(ee;^1AV(+qFL>=pD1pzM_9GxqNkDR z$~(&;jQY7|_p0|?VYu(`bE84iv2pR;lJ`5zH}s*|S%Yo(B{A)tOJ!?K$!;<=vW^)4 z0F|_o8IL%Ol1Ntn0RFXJmyzS)&y2tbc96r|{GQ5OrZ7Sd8LD zou$Gzw3J+Pw{N>DJN~?DeZ3T7GTcdF?sqnSqJfn5{m>T=;gz)%)6&qian&NM#np*5 z8;K>Gjx(P1?nh8eI!91CuG`?%H-|b zdy+uT2*ErK2B7^_46SqV$#8Gp2LAv>XE5DbBdN4**azZd z-qP=RhF59jBxUOxb=vDm9c0(aia6xo1dyVKLP*@P6T$nQ`R$`RM>XZ(-Eh0abgde| z^4fmcEH%}Y6f(n0aj1Z8o_OG9c;v%_w*YS-@D2}r{A(-hQcmOG?(coKKk$smSnxxM z9}0`BI!=P3{{T@PO*19Q+wsPZX&2KAo!Rvp+PLQ#&pG2y{iZ@{YS?yCaauNJHUVP_tsET zRMbsFcT_Pa;iOlBIMGRG5y%J%LBjj0WWBJk$jg|jK@I-{p3 z>8*h`HHRHT~T?@ydoe!pF3B>#a{!UT$}~YI}SXUWiqL-YOM_ zcyAm9Qdh9g9AxSamprf;n?w(PU+SVpk`~AMf7kXNoUnkFrM!}xy+ zjN@52P>_Vb%Qp8~GJ)1ZO zzi%1QCdZ2?G-N%)SKOn??|#T~Vo1Q(VbLe=YCp*?@A6UDL)oG?GHJwuPsYLpK#bvb zt_BK{Pd&irN6NzLF@cT%3~s>Q_3nm6ig4Xlbi5ro{8dfH>u`b^wm=#c^^3%C4l)UU zZ~*@RP|tjGph8S9*CF#!_eFG$a1FhJZ{E5}j*+1L8X%=e3$$@c8!N!U^Ra*-e3B12 zBUI}>eqiA?J42f48~XP&hD%1y;UMc-takxb1ua>ro!RPTOp-&lyrE(@00wixIQZ7X zI<8G1p2BC3%orngTe4D4NDsYk?x~$~keh ztq9c%LhBPs_ewXs)=M17+n3d;+^o3Dl2q_fMV^MMZFyBy80+FlHXyppgbQ5H}1jAnQA(K^yW9j!ufN$K(2;!63+s#K${v zx{GDHbq`7{N>n|3^@?PQnWSC*sK^_(=MBaI$m9Fz+@GW(bDsQ1ld-Ek>-(USq*M7y zKBoHV*B5x_ut9aPy|yV`DkG5s%_t~xg9HY3{M_;|d-1Hko%HO@)%K0Zs^0!kSs;6W z?uj}|D9p4~a?(wC9-3x2ssReb0#xMXnD^(N`dh7JxE8UehS%zw$HIgn)r5 z669%QqeyDK5GF4gC*d-ue#IOX7zZbwb*=RFKy(^hFGq9E_xYvr1wDWhM(uZ}f=b4? zP~NXM&q_r}U@D3N#heYK@(Cv+z{Y!MxKlzhb!2_hf5{QmyHySC>gj138+84`svF@ih&fMf4+zclEt&A)*_w-eb<&4pE7cP$KKDV{pE8@A`U^aPGNTfR- z)P(_DmQ3ws+sl4z;|G(jldH9wM3x2d$^+$VO~fzdKkVIaPgha+Dw9PkD#p`pWdamj z;DulpdGnkcW69Fpc384sj~i?-lYYZ)6&)dY1>(LF9DPHsqn?zs@U zJ=<}g`n>QoyC%%dj!ZZj-BFz^>_{EYy(Z$t2X-CN?QM0psX9t|+Jb>D^3urAdXOn# z2>_~r!+w~_@^$JmF|hFQKl!6e?0wJ)+(T!ToOUWf0AAt9ha`G-jMc(Mq#{ zzz4nv_LboA&l&TZ zIqXK5EphWWll+m)q>9oLqon%MhC2i+a+;#&S2+-rG5Tu?<8P>NGWg_Y#~(T2&ZD?+g z6qfpt^u}AQ5UH)|TIw0huS@E~R8lDNq?G}1RhJm!z{Yvbn09-hnUOnY$$R(N3LKaw z4gj4^=h0*SzUXQ`jj4v?{`FPe=+Y8@3}C|zpn|LkE!!NPa&xDjAQ0as{{WidksMhJ z6CH>sFQjFpp1j8eB`vNBkWyI;>Y?yM06eCvL#gqQ;VG`LL;S~L~VdBK|%rCN#t?cxYi7=hyxp3VkGP5SGS@A@to-nnk8FBd+G_}T1q%%g|MqnF`I0nCE<54AqVAboR0YC z7!fusnJ*8;J2z)u+xshcax-U*(5-#fs%)OLNppr5p9=K8n#u~FacEURRtg6vz$LJG z8eSRVid|COSI+29W}{loe5uyotdv%JWn~T3ZxUm4vp!%In)R!phA*Zi3?5w`6RdlT=$OGD{ zMpJLcIT`0q^vpNKkkZ#VXLN6W;TX#D1A5wIDiF_yJQ+2oxFXTT(Sh|D{C9J(js?pZB%d%C^ap}5>XAzmA zShDTvhTE_wECXb)Ba9Bc_jjVIJ8=CTOvOKE#ebf}ScFL$OwAJ>A zT@W<2Ai5F(Whnmul?eoeT#V-#)$+Pjd2bKP8eLW8pg;Dnp)&eT^J}w1TN6`JLr@>v zQ|kd73d4tU;X`mco^UmrlLkf`{=YKk0 zxHA)hgWud#XE?`l065Yx;W{wcp(WE-miXVBT*m{mlYWz_E9t4z_(go;i6NCK=2=6! z6Ce%-azS9cf-nH%SlRe4&vZagn^M78vE3@ z+e}w$d}~f4iYG~ZCk%IG2sk6Y3C}ssHREtPhE^*bk(6~8)e9s7Jv}~DK?}A8Yfhi) zn#;{EO$xcBABI@u37oWOP>e4B0C8-Lv0=_WHSAf_+thJ`=OYlH+(O%LoNn`_edeSPc zptSWjNX1C@>t%?mJe0Fc;S{k2M)Ck4mK?Vk$oF~w^HW#DBE>y0k9151L5&DL-HuKQf(bnL*Q<%K&yTEmgFxhZtYeF-EsuGk z4%E2Q^j}umY9s#u@nvj z4d#uGcJxf*bxBK$3~l_D#QHl?MMH9?qNfzq7P`51v{I_4;$2P)fHF5XKY!b{e_VL! z!aR<3njr8fcBG|H1G>~*Z_(W$bfULaTx`oz2d6zNNw}9HPYMACD~>afpE%d1>6x7~ zC7{gB{{Z|T(fn;ooweG>J#+Ni=^f>(Xs%Z)mrK$iR7ds=81I>8LV zov<*(?H=F&^OKI;XgxzX#nfK*0ORG@@HX^F#|iuQ<@rK!-ukN3d79&Mx0<^%z|^e; zY>Wbs?w!ri>^3%msH_S}i zOJw?rJ%^pZ1sU>3I`gwm_!tWSF84=sTV7F1+gRthTqrIP{x+fluTr#Z=_Db*!Q2#W z1n}8X2_v@z=@_tb<{mr;_lNHdRMF@F>=NyclVZm;bd*&qLuR-&1X3(gy2I{%Jm#dbsUmc zBOQmGH`bpi$q&eA4%1XPq2J|0qaY-=QI4RJl_0mFfcxp?lSYKMawwN?j83Q&9zN&V*4?oxx=T5-fm@8$IxMV|Rm`aiN^d z%&c*c<~PCskbj5{=Dk!FypSEWe!nPHuD-lA3sFHr*I74)sfE~0w-^F7;WrcP818&% z9XqZ%NrVuz9bL~qho`zDz$9uPk{hJx+jZs;^$=W*HCrqz6!2oAIS0A75 Date: Fri, 21 Nov 2014 05:51:23 -0800 Subject: [PATCH 229/835] Adding type hints. --- lib/OpenCloud/Common/Service/Endpoint.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index c42c43285..17690db97 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -72,7 +72,7 @@ public static function factory($object, $supportedServiceVersion, Client $client * @param $publicUrl * @return $this */ - public function setPublicUrl($publicUrl) + public function setPublicUrl(Url $publicUrl) { $this->publicUrl = $publicUrl; @@ -91,7 +91,7 @@ public function getPublicUrl() * @param $privateUrl * @return $this */ - public function setPrivateUrl($privateUrl) + public function setPrivateUrl(Url $privateUrl) { $this->privateUrl = $privateUrl; From 72ba0f1be9fe5e1717e28b70507dab7566bbf4f7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 21 Nov 2014 05:53:25 -0800 Subject: [PATCH 230/835] Remove unnecessary variable. --- lib/OpenCloud/Common/Service/Endpoint.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index 17690db97..bffbd0ead 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -135,12 +135,10 @@ public function getRegion() */ protected function getVersionedUrl($url, $supportedServiceVersion, Client $client) { - $urlObj = Url::factory($url); - $versionRegex = '/\/[vV][0-9][0-9\.]*/'; if (1 === preg_match($versionRegex, $url)) { // URL has version in it; use it as-is - return $urlObj; + return Url::factory($url); } // Make GET request to URL From eb3ccc411b8cc043e6f79b104f9070bac5c1a395 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 21 Nov 2014 05:53:51 -0800 Subject: [PATCH 231/835] Return Guzzle\Http\Url object. --- lib/OpenCloud/Common/Service/Endpoint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index bffbd0ead..e1ddfef68 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -154,7 +154,7 @@ protected function getVersionedUrl($url, $supportedServiceVersion, Client $clien && $version->id == $supportedServiceVersion) { foreach ($version->links as $link) { if ($link->rel == 'self') { - return $link->href; + return Url::factory($link->href); } } } From be060e7245e8ee324695945256019918c99f4079 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 21 Nov 2014 05:54:25 -0800 Subject: [PATCH 232/835] Use null instead of 'unknown'. --- lib/OpenCloud/Common/Service/CatalogService.php | 2 +- lib/OpenCloud/Common/Service/Endpoint.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index 42e436f49..addcd170b 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -27,7 +27,7 @@ abstract class CatalogService extends AbstractService { const DEFAULT_URL_TYPE = 'publicURL'; - const SUPPORTED_VERSION = 'unknown'; + const SUPPORTED_VERSION = null; /** * @var string The type of this service, as set in Catalog. diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index e1ddfef68..a7bac2784 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -141,6 +141,10 @@ protected function getVersionedUrl($url, $supportedServiceVersion, Client $clien return Url::factory($url); } + if (is_null($supportedServiceVersion)) { + throw new UnsupportedVersionError('Service version supported by SDK not specified.'); + } + // Make GET request to URL $response = Formatter::decode($client->get($url)->send()); From 3dc992d9bf75ee9d678762a9760b7c0ac89191da Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 21 Nov 2014 05:55:04 -0800 Subject: [PATCH 233/835] Make method private instead of protected. --- lib/OpenCloud/Common/Service/Endpoint.php | 2 +- .../Tests/Common/Service/EndpointTest.php | 60 ++++++++++++++----- tests/OpenCloud/Tests/OpenCloudTestCase.php | 18 ++++++ 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index a7bac2784..3e3e3cb8c 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -133,7 +133,7 @@ public function getRegion() * @param OpenCloud\Common\Http\Client $client HTTP client * @return Guzzle/Http/Url Endpoint URL with version in it */ - protected function getVersionedUrl($url, $supportedServiceVersion, Client $client) + private function getVersionedUrl($url, $supportedServiceVersion, Client $client) { $versionRegex = '/\/[vV][0-9][0-9\.]*/'; if (1 === preg_match($versionRegex, $url)) { diff --git a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php index c14401e7d..1be4e09d9 100644 --- a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php +++ b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php @@ -21,24 +21,23 @@ use OpenCloud\Tests\OpenCloudTestCase; use OpenCloud\Common\Http\Client; -class PublicEndpoint extends Endpoint -{ - public function getVersionedUrl($url, $supportedServiceVersion, Client $client) - { - return parent::getVersionedUrl($url, $supportedServiceVersion, $client); - } -} - class EndpointTest extends OpenCloudTestCase { public function testGetVersionedUrlWithVersionLessEndpointSupportedVersionFound() { $this->addMockSubscriber($this->makeResponse('{"versions":[{"status":"CURRENT","id":"v2.0","links":[{"href":"http://hostport/v2.0","rel":"self" }]}]}', 200)); - $e = new PublicEndpoint(); + $endpoint = new Endpoint(); $expectedUrl = "http://hostport/v2.0"; - $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport", 'v2.0', $this->client)); + $actualUrl = $this->invokeMethod( + $endpoint, + 'getVersionedUrl', + array('http://hostport', 'v2.0', $this->client) + ); + + $this->assertInstanceOf('Guzzle\Http\Url', $actualUrl); + $this->assertEquals($expectedUrl, $actualUrl); } /** @@ -48,9 +47,13 @@ public function testGetVersionedUrlWithVersionLessEndpointSupportedVersionNotFou { $this->addMockSubscriber($this->makeResponse('{"versions":[{"status":"CURRENT","id":"v2.0","links":[{"href":"http://hostport/v2.0","rel":"self" }]}]}', 200)); - $e = new PublicEndpoint(); + $endpoint = new Endpoint(); - $e->getVersionedUrl("http://hostport", 'v2.1', $this->client); + $this->invokeMethod( + $endpoint, + 'getVersionedUrl', + array('http://hostport', 'v2.1', $this->client) + ); } /** @@ -60,18 +63,43 @@ public function testGetVersionedUrlWithVersionLessEndpointInvalidResponse() { $this->addMockSubscriber($this->makeResponse('{}')); - $e = new PublicEndpoint(); + $endpoint = new Endpoint(); + + $this->invokeMethod( + $endpoint, + 'getVersionedUrl', + array('http://hostport', 'v2.1', $this->client) + ); + } - $e->getVersionedUrl("http://hostport", 'v2.1', $this->client); + /** + * @expectedException OpenCloud\Common\Exceptions\UnsupportedVersionError + */ + public function testGetVersionedUrlWithVersionLessEndpointSupportedVersionNotSpecified() + { + $endpoint = new Endpoint(); + + $this->invokeMethod( + $endpoint, + 'getVersionedUrl', + array('http://hostport', null, $this->client) + ); } public function testGetVersionedUrlWithVersionedEndpointUrl() { $this->addMockSubscriber($this->makeResponse('{}', 200)); - $e = new PublicEndpoint(); + $endpoint = new Endpoint(); $expectedUrl = "http://hostport/v1"; - $this->assertEquals($expectedUrl, $e->getVersionedUrl("http://hostport/v1", 'unknown', $this->client)); + $actualUrl = $this->invokeMethod( + $endpoint, + 'getVersionedUrl', + array("http://hostport/v1", null, $this->client) + ); + + $this->assertInstanceOf('Guzzle\Http\Url', $actualUrl); + $this->assertEquals($expectedUrl, $actualUrl); } } diff --git a/tests/OpenCloud/Tests/OpenCloudTestCase.php b/tests/OpenCloud/Tests/OpenCloudTestCase.php index 04d60a716..628881fbb 100644 --- a/tests/OpenCloud/Tests/OpenCloudTestCase.php +++ b/tests/OpenCloud/Tests/OpenCloudTestCase.php @@ -124,4 +124,22 @@ public function isCollection($object) { $this->assertInstanceOf(self::COLLECTION_CLASS, $object); } + + /** + * Call protected/private method of a class. + * + * @param object &$object Instantiated object that we will run method on. + * @param string $methodName Method name to call + * @param array $parameters Array of parameters to pass into method. + * + * @return mixed Method return. + */ + public function invokeMethod(&$object, $methodName, array $parameters = array()) + { + $reflection = new \ReflectionClass(get_class($object)); + $method = $reflection->getMethod($methodName); + $method->setAccessible(true); + + return $method->invokeArgs($object, $parameters); + } } From 6148835571e3d5832f7f40401287f1867c6c6e06 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 21 Nov 2014 05:55:18 -0800 Subject: [PATCH 234/835] More thorough check/ Using isset instead of property_exists. --- lib/OpenCloud/Common/Service/Endpoint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index 3e3e3cb8c..1b5dbc302 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -149,7 +149,7 @@ private function getVersionedUrl($url, $supportedServiceVersion, Client $client) $response = Formatter::decode($client->get($url)->send()); // Attempt to parse response and determine URL for given $version - if (!property_exists($response, 'versions')) { + if (!isset($response->versions) || !is_array($response->versions)) { throw new UnsupportedVersionError('Could not negotiate version with service.'); } From b6d927eb26a15a240e42ea5631cf3b25f67fcf39 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 16:00:06 +0100 Subject: [PATCH 235/835] Adding changes based on code review --- samples/ObjectStore/auto-extract-archive-files.php | 3 +++ samples/ObjectStore/copy-object.php | 6 +++--- samples/ObjectStore/get-object-temporary-url.php | 3 ++- samples/ObjectStore/list-objects-with-prefix.php | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/samples/ObjectStore/auto-extract-archive-files.php b/samples/ObjectStore/auto-extract-archive-files.php index 9b61a0386..53717b3c6 100644 --- a/samples/ObjectStore/auto-extract-archive-files.php +++ b/samples/ObjectStore/auto-extract-archive-files.php @@ -36,4 +36,7 @@ // 4. Upload archive for auto-extraction. Note that while we call fopen to open // the file resource, we do not call fclose at the end. The file resource is // automatically closed inside the bulkExtract call. +// +// For example, if you have a photo.jpg file inside a container named Photos, +// then the remote path will be Photos/photo.jpg. $objectStoreService->bulkExtract('{remotePath}', $fileData, UrlType::TAR_GZ); diff --git a/samples/ObjectStore/copy-object.php b/samples/ObjectStore/copy-object.php index f0996696b..882df7f27 100644 --- a/samples/ObjectStore/copy-object.php +++ b/samples/ObjectStore/copy-object.php @@ -30,13 +30,13 @@ $objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('{oldContainerName}'); +$container = $objectStoreService->getContainer('{sourceContainerName}'); // 4. Get object. $object = $container->getObject('{objectName}'); // 5. Create another container for object's copy. -$objectStoreService->createContainer('{newContainerName}'); +$objectStoreService->createContainer('{destinationContainerName}'); // 6. Copy object to another container. -$object->copy('{newContainerName}/{objectName}'); +$object->copy('{destinationContainerName}/{objectName}'); diff --git a/samples/ObjectStore/get-object-temporary-url.php b/samples/ObjectStore/get-object-temporary-url.php index 5d438972c..605c16d20 100644 --- a/samples/ObjectStore/get-object-temporary-url.php +++ b/samples/ObjectStore/get-object-temporary-url.php @@ -38,7 +38,8 @@ // 5. Set expiration (in seconds) $expirationTime = 3600; // one hour from now -// 6. Set the allowed HTTP method +// 6. Set the HTTP method allowed on the object in the container. If you want +// to allow read-only access, use GET; for write-access, use POST. $httpMethod = 'GET'; // 7. Get temp URL diff --git a/samples/ObjectStore/list-objects-with-prefix.php b/samples/ObjectStore/list-objects-with-prefix.php index 9e489e0d7..084f4328a 100644 --- a/samples/ObjectStore/list-objects-with-prefix.php +++ b/samples/ObjectStore/list-objects-with-prefix.php @@ -32,7 +32,7 @@ // 3. Get container. $container = $objectStoreService->getContainer('{containerName}'); -// 4. Get list of objects whose names start with a prefix +// 4. Get list of objects whose names start with {prefix} $objects = $container->objectList(array( 'prefix' => '{prefix}' )); From ba59d56b5710d7e0e28e4d5bc5eef2c573c91371 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 16:09:10 +0100 Subject: [PATCH 236/835] Removing output functions --- samples/ObjectStore/get-account-bytes-used.php | 3 ++- samples/ObjectStore/get-account-container-count.php | 3 ++- samples/ObjectStore/get-account-object-count.php | 3 ++- samples/ObjectStore/get-account-temp-url-secret.php | 4 ++-- samples/ObjectStore/get-cdn-object-http-url.php | 4 ++-- samples/ObjectStore/get-cdn-object-https-url.php | 4 ++-- samples/ObjectStore/get-cdn-object-ios-streaming-url.php | 4 ++-- samples/ObjectStore/get-cdn-object-streaming-url.php | 4 ++-- samples/ObjectStore/get-container-bytes-quota.php | 5 +++-- samples/ObjectStore/get-container-bytes-used.php | 5 +++-- samples/ObjectStore/get-container-count-quota.php | 5 +++-- samples/ObjectStore/get-container-metadata.php | 7 +++---- samples/ObjectStore/get-container-object-count.php | 2 +- samples/ObjectStore/get-container.php | 3 +-- samples/ObjectStore/get-object-metadata.php | 9 ++++++--- samples/ObjectStore/get-object-temporary-url.php | 4 ++-- samples/ObjectStore/get-object.php | 5 ----- samples/ObjectStore/list-containers.php | 3 +-- .../ObjectStore/list-objects-with-limit-and-prefix.php | 3 +-- samples/ObjectStore/list-objects-with-limit.php | 3 +-- samples/ObjectStore/list-objects-with-prefix.php | 3 +-- samples/ObjectStore/list-objects.php | 3 +-- 22 files changed, 43 insertions(+), 46 deletions(-) diff --git a/samples/ObjectStore/get-account-bytes-used.php b/samples/ObjectStore/get-account-bytes-used.php index 2ad0239f7..655e48581 100644 --- a/samples/ObjectStore/get-account-bytes-used.php +++ b/samples/ObjectStore/get-account-bytes-used.php @@ -33,5 +33,6 @@ $account = $objectStoreService->getAccount(); // 4. Get the space (in bytes) used by the account. + +/** @var $accountSizeInBytes int */ $accountSizeInBytes = $account->getBytesUsed(); -printf("Space (in bytes) used by this account: %d\n", $accountSizeInBytes); diff --git a/samples/ObjectStore/get-account-container-count.php b/samples/ObjectStore/get-account-container-count.php index 140c5cf40..66c4ace18 100644 --- a/samples/ObjectStore/get-account-container-count.php +++ b/samples/ObjectStore/get-account-container-count.php @@ -33,5 +33,6 @@ $account = $objectStoreService->getAccount(); // 4. Get the number of containers in the account. + +/** @var $accountContainerCount int */ $accountContainerCount = $account->getContainerCount(); -printf("# of containers in account: %d\n", $accountContainerCount); diff --git a/samples/ObjectStore/get-account-object-count.php b/samples/ObjectStore/get-account-object-count.php index 3d1392c40..05f0e90a8 100644 --- a/samples/ObjectStore/get-account-object-count.php +++ b/samples/ObjectStore/get-account-object-count.php @@ -33,5 +33,6 @@ $account = $objectStoreService->getAccount(); // 4. Get the number of objects in the account. + +/** @var $accountObjectCount int */ $accountObjectCount = $account->getObjectCount(); -printf("# of objects in account: %d\n", $accountObjectCount); diff --git a/samples/ObjectStore/get-account-temp-url-secret.php b/samples/ObjectStore/get-account-temp-url-secret.php index 101ed6675..d0d4275ad 100644 --- a/samples/ObjectStore/get-account-temp-url-secret.php +++ b/samples/ObjectStore/get-account-temp-url-secret.php @@ -33,6 +33,6 @@ $account = $objectStoreService->getAccount(); // 4. Get the temporary URL secret. -$tempUrlSecret = $account->getTempUrlSecret(); -printf("Account temporary URL secret: %s\n", $tempUrlSecret); +/** @var $tempUrlSecret string */ +$tempUrlSecret = $account->getTempUrlSecret(); diff --git a/samples/ObjectStore/get-cdn-object-http-url.php b/samples/ObjectStore/get-cdn-object-http-url.php index c5e6fe42c..8427bce81 100644 --- a/samples/ObjectStore/get-cdn-object-http-url.php +++ b/samples/ObjectStore/get-cdn-object-http-url.php @@ -36,6 +36,6 @@ $object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible HTTP URL. -$httpUrl = $object->getPublicUrl(); -printf("Object's publicly accessible HTTP URL: %s\n", $httpUrl); +/** @var $httpUrl string */ +$httpUrl = $object->getPublicUrl(); diff --git a/samples/ObjectStore/get-cdn-object-https-url.php b/samples/ObjectStore/get-cdn-object-https-url.php index 954598255..cac63bcef 100644 --- a/samples/ObjectStore/get-cdn-object-https-url.php +++ b/samples/ObjectStore/get-cdn-object-https-url.php @@ -37,6 +37,6 @@ $object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible HTTPS URL. -$httpsUrl = $object->getPublicUrl(UrlType::SSL); -printf("Object's publicly accessible HTTPS URL: %s\n", $httpsUrl); +/** @var $httpsUrl string */ +$httpsUrl = $object->getPublicUrl(UrlType::SSL); diff --git a/samples/ObjectStore/get-cdn-object-ios-streaming-url.php b/samples/ObjectStore/get-cdn-object-ios-streaming-url.php index e38b8db11..684cfe0e9 100644 --- a/samples/ObjectStore/get-cdn-object-ios-streaming-url.php +++ b/samples/ObjectStore/get-cdn-object-ios-streaming-url.php @@ -37,6 +37,6 @@ $object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible iOS streaming URL. -$iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); -printf("Object's publicly accessible iOS streaming URL: %s\n", $iosStreamingUrl); +/** @var $iosStreamingUrl string */ +$iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); diff --git a/samples/ObjectStore/get-cdn-object-streaming-url.php b/samples/ObjectStore/get-cdn-object-streaming-url.php index ca8a6233e..d66040664 100644 --- a/samples/ObjectStore/get-cdn-object-streaming-url.php +++ b/samples/ObjectStore/get-cdn-object-streaming-url.php @@ -37,6 +37,6 @@ $object = $container->getObject('{objectName}'); // 5. Get object's publicly-accessible streaming URL. -$streamingUrl = $object->getPublicUrl(UrlType::STREAMING); -printf("Object's publicly accessible streaming URL: %s\n", $streamingUrl); +/** @var $streamingUrl string */ +$streamingUrl = $object->getPublicUrl(UrlType::STREAMING); diff --git a/samples/ObjectStore/get-container-bytes-quota.php b/samples/ObjectStore/get-container-bytes-quota.php index ff6216839..11603e215 100644 --- a/samples/ObjectStore/get-container-bytes-quota.php +++ b/samples/ObjectStore/get-container-bytes-quota.php @@ -33,5 +33,6 @@ $container = $objectStoreService->getContainer('{containerName}'); // 4. Get the quota for total size of objects in container. -$maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); -printf("Total size of objects allowed in container: %d\n", $maximumTotalSizeOfObjectsAllowedInContainer); + +/** @var $bytesQuota int */ +$bytesQuota = $container->getBytesQuota(); diff --git a/samples/ObjectStore/get-container-bytes-used.php b/samples/ObjectStore/get-container-bytes-used.php index 875068440..31cca2f00 100644 --- a/samples/ObjectStore/get-container-bytes-used.php +++ b/samples/ObjectStore/get-container-bytes-used.php @@ -33,5 +33,6 @@ $container = $objectStoreService->getContainer('{containerName}'); // 4. Get the space (in bytes) used by this container. -$containerSizeInBytes = $container->getBytesUsed(); -printf("Space (in bytes) used by this container: %d\n", $containerSizeInBytes); + +/** @var $bytesUsed int */ +$bytesUsed = $container->getBytesUsed(); diff --git a/samples/ObjectStore/get-container-count-quota.php b/samples/ObjectStore/get-container-count-quota.php index 76e5015d3..58e1e983d 100644 --- a/samples/ObjectStore/get-container-count-quota.php +++ b/samples/ObjectStore/get-container-count-quota.php @@ -33,5 +33,6 @@ $container = $objectStoreService->getContainer('{containerName}'); // 4. Get the quota for number of objects in container. -$maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); -printf("Number of objects allowed in container: %d\n", $maximumNumberOfObjectsAllowedInContainer); + +/** @var $countQuota int */ +$countQuota = $container->getCountQuota(); diff --git a/samples/ObjectStore/get-container-metadata.php b/samples/ObjectStore/get-container-metadata.php index 48a7ea759..a5cbe6dac 100644 --- a/samples/ObjectStore/get-container-metadata.php +++ b/samples/ObjectStore/get-container-metadata.php @@ -32,7 +32,6 @@ // 3. Get container. $container = $objectStoreService->getContainer('{containerName}'); -// 4. Set container metadata. -$containerMetadata = $container->getMetadata(); -/** @var $container $containerMetadata OpenCloud\ObjectStore\Resource\ContainerMetadata **/ -printf("Container author: %s\n", $containerMetadata->getProperty('author')); +// 4. Get container metadata. +/** @var $metadata OpenCloud\ObjectStore\Resource\ContainerMetadata **/ +$metadata = $container->getMetadata(); diff --git a/samples/ObjectStore/get-container-object-count.php b/samples/ObjectStore/get-container-object-count.php index 34fdbe8d2..94607d8c3 100644 --- a/samples/ObjectStore/get-container-object-count.php +++ b/samples/ObjectStore/get-container-object-count.php @@ -33,5 +33,5 @@ $container = $objectStoreService->getContainer('{containerName}'); // 4. Get the number of objects in this container. +/** @param $containerObjectCount int */ $containerObjectCount = $container->getObjectCount(); -printf("# of objects in container: %d\n", $containerObjectCount); diff --git a/samples/ObjectStore/get-container.php b/samples/ObjectStore/get-container.php index 40cdc73d0..7d3a20a23 100644 --- a/samples/ObjectStore/get-container.php +++ b/samples/ObjectStore/get-container.php @@ -30,6 +30,5 @@ $objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Get container. -$container = $objectStoreService->getContainer('{containerName}'); /** @var $container OpenCloud\ObjectStore\Resource\Container **/ -printf("Container name: %s\n", $container->getName()); +$container = $objectStoreService->getContainer('{containerName}'); diff --git a/samples/ObjectStore/get-object-metadata.php b/samples/ObjectStore/get-object-metadata.php index 8f44a4190..e2ca0d300 100644 --- a/samples/ObjectStore/get-object-metadata.php +++ b/samples/ObjectStore/get-object-metadata.php @@ -32,9 +32,12 @@ // 3. Get container. $container = $objectStoreService->getContainer('{containerName}'); -// 4. Get object metadata. +// 4. Retrieve information about the object. $object = $container->getPartialObject('{objectName}'); -$objectMetadata = $object->getMetadata(); +// 5. Retrieve metadata. /** @var $objectMetadata \OpenCloud\Common\Metadata **/ -printf("Object author: %s\n", $objectMetadata->getProperty('author')); +$objectMetadata = $object->getMetadata(); + +/** @var $metadataItemValue mixed **/ +$metadataItemValue = $objectMetadata->getProperty('{key}'); diff --git a/samples/ObjectStore/get-object-temporary-url.php b/samples/ObjectStore/get-object-temporary-url.php index 605c16d20..f8a2b0211 100644 --- a/samples/ObjectStore/get-object-temporary-url.php +++ b/samples/ObjectStore/get-object-temporary-url.php @@ -43,6 +43,6 @@ $httpMethod = 'GET'; // 7. Get temp URL -$tempUrl = $object->getTemporaryUrl($expirationTime, $httpMethod); -printf("Object temporary URL: %s\n", $tempUrl); +/** @param $tempUrl string */ +$tempUrl = $object->getTemporaryUrl($expirationTime, $httpMethod); diff --git a/samples/ObjectStore/get-object.php b/samples/ObjectStore/get-object.php index 89e231a83..a6b5e94fb 100644 --- a/samples/ObjectStore/get-object.php +++ b/samples/ObjectStore/get-object.php @@ -37,16 +37,11 @@ /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ $object = $container->getObject('{objectName}'); -printf("Object name: %s\n", $object->getName()); - /** @var $objectContent Guzzle\Http\EntityBody **/ $objectContent = $object->getContent(); - // 5. Write object content to file on local filesystem. $objectContent->rewind(); $stream = $objectContent->getStream(); $localFilename = tempnam("/tmp", 'php-opencloud-'); file_put_contents($localFilename, $stream); - -printf("Your object has been written to %s\n", $localFilename); diff --git a/samples/ObjectStore/list-containers.php b/samples/ObjectStore/list-containers.php index 71de3f1fe..5011d41fb 100644 --- a/samples/ObjectStore/list-containers.php +++ b/samples/ObjectStore/list-containers.php @@ -32,6 +32,5 @@ // 3. Get container list. $containers = $objectStoreService->listContainers(); foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - printf("Container name: %s\n", $container->getName()); + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ } diff --git a/samples/ObjectStore/list-objects-with-limit-and-prefix.php b/samples/ObjectStore/list-objects-with-limit-and-prefix.php index e34548ca9..0462aaf01 100644 --- a/samples/ObjectStore/list-objects-with-limit-and-prefix.php +++ b/samples/ObjectStore/list-objects-with-limit-and-prefix.php @@ -39,6 +39,5 @@ )); foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - printf("Object name: %s\n", $object->getName()); + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ } diff --git a/samples/ObjectStore/list-objects-with-limit.php b/samples/ObjectStore/list-objects-with-limit.php index 8bf6f4306..3720ae9cb 100644 --- a/samples/ObjectStore/list-objects-with-limit.php +++ b/samples/ObjectStore/list-objects-with-limit.php @@ -38,6 +38,5 @@ )); foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - printf("Object name: %s\n", $object->getName()); + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ } diff --git a/samples/ObjectStore/list-objects-with-prefix.php b/samples/ObjectStore/list-objects-with-prefix.php index 084f4328a..c2814c9f8 100644 --- a/samples/ObjectStore/list-objects-with-prefix.php +++ b/samples/ObjectStore/list-objects-with-prefix.php @@ -38,6 +38,5 @@ )); foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - printf("Object name: %s\n", $object->getName()); + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ } diff --git a/samples/ObjectStore/list-objects.php b/samples/ObjectStore/list-objects.php index 7f40f327b..0621c0f08 100644 --- a/samples/ObjectStore/list-objects.php +++ b/samples/ObjectStore/list-objects.php @@ -36,6 +36,5 @@ $objects = $container->objectList(); foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - printf("Object name: %s\n", $object->getName()); + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ } From 146ea65b6bc51827cbaf4a92348717a50d43a4a1 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 16:36:55 +0100 Subject: [PATCH 237/835] Remove comment --- samples/Compute/create_server_with_network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Compute/create_server_with_network.php b/samples/Compute/create_server_with_network.php index 6baaf51ba..123e689e9 100644 --- a/samples/Compute/create_server_with_network.php +++ b/samples/Compute/create_server_with_network.php @@ -47,8 +47,8 @@ 'flavorId' => '{flavorId}', 'networks' => array( $customNetwork, - $computeService->network(Network::RAX_PRIVATE), // This is the internal Rackspace network - strongly recommended - $computeService->network(Network::RAX_PUBLIC), // This is the public internet - strong recommended + $computeService->network(Network::RAX_PRIVATE), + $computeService->network(Network::RAX_PUBLIC), ), )); } catch (BadResponseException $e) { From e9f7a4e5f5e91c28ab6f0f3cadcf7b63cceeb044 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 21 Nov 2014 17:14:11 +0100 Subject: [PATCH 238/835] Appeasing the PSR gods --- samples/Compute/create_server_with_network.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Compute/create_server_with_network.php b/samples/Compute/create_server_with_network.php index 123e689e9..cf66486c8 100644 --- a/samples/Compute/create_server_with_network.php +++ b/samples/Compute/create_server_with_network.php @@ -47,7 +47,7 @@ 'flavorId' => '{flavorId}', 'networks' => array( $customNetwork, - $computeService->network(Network::RAX_PRIVATE), + $computeService->network(Network::RAX_PRIVATE), $computeService->network(Network::RAX_PUBLIC), ), )); From fb0070f571ed3c49b05293fb9acddbcc9cfb31ae Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Mon, 24 Nov 2014 10:12:13 +0100 Subject: [PATCH 239/835] Rename Servers doc file --- docs/userguide/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 9c9a97752..934951e24 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -17,7 +17,7 @@ - [Zones](/docs/userguide/CloudMonitoring/Zones.md) ## Compute -- [Servers](/docs/userguide/Compute/Servers.md) +- [Servers](/docs/userguide/Compute/Server.md) - [Images](/docs/userguide/Compute/Images.md) - [Flavors](/docs/userguide/flavors.md) - [Keypairs](/docs/userguide/Compute/Keypairs.md) From c40b9c557d3513afbdc818db84eac78614ce2de3 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 24 Nov 2014 10:51:10 +0100 Subject: [PATCH 240/835] Shifting to placeholders --- samples/Orchestration/abandon-stack.php | 27 ++++++----------- samples/Orchestration/adopt-stack.php | 28 +++++++---------- .../create-stack-from-template-file.php | 30 +++++++------------ .../create-stack-from-template-url.php | 28 +++++++---------- samples/Orchestration/delete-stack.php | 22 ++++---------- samples/Orchestration/get-build-info.php | 21 ++++--------- .../get-resource-type-template.php | 25 +++++----------- samples/Orchestration/get-resource-type.php | 23 +++++--------- .../get-stack-resource-event.php | 28 +++++------------ .../get-stack-resource-metadata.php | 27 +++++------------ samples/Orchestration/get-stack-resource.php | 25 +++++----------- samples/Orchestration/get-stack-template.php | 24 +++++---------- samples/Orchestration/get-stack.php | 22 ++++---------- samples/Orchestration/list-resource-types.php | 20 ++++--------- samples/Orchestration/list-stack-events.php | 23 +++++--------- .../list-stack-resource-events.php | 28 ++++++----------- .../Orchestration/list-stack-resources.php | 23 +++++--------- samples/Orchestration/list-stacks.php | 20 ++++--------- .../preview-stack-from-template-file.php | 29 +++++++----------- .../preview-stack-from-template-url.php | 27 ++++++----------- samples/Orchestration/quickstart.php | 26 ++++++---------- .../update-stack-from-template-file.php | 28 ++++++----------- .../update-stack-from-template-url.php | 28 ++++++----------- .../validate-template-from-template-file.php | 21 ++++--------- .../validate-template-from-template-url.php | 19 ++++-------- 25 files changed, 195 insertions(+), 427 deletions(-) diff --git a/samples/Orchestration/abandon-stack.php b/samples/Orchestration/abandon-stack.php index 475cd5684..842978524 100644 --- a/samples/Orchestration/abandon-stack.php +++ b/samples/Orchestration/abandon-stack.php @@ -15,34 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Abandon stack. -$abandonStackData = $stack->abandon(); + /** @var $abandonStackData string **/ +$abandonStackData = $stack->abandon(); file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); diff --git a/samples/Orchestration/adopt-stack.php b/samples/Orchestration/adopt-stack.php index 337e16178..4c470c8bb 100644 --- a/samples/Orchestration/adopt-stack.php +++ b/samples/Orchestration/adopt-stack.php @@ -15,33 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Adopt a stack. + +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), - 'adoptStackData' => file_get_contents(__DIR__ . '/sample_adopt_stack_data.json'), + 'name' => '{stackName}', + 'template' => file_get_contents('{yamlTemplateFilePath}'), + 'adoptStackData' => file_get_contents('{stackDataFilePath}'), 'timeoutMins' => 5 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-file.php b/samples/Orchestration/create-stack-from-template-file.php index 0934e64de..b7d8d892f 100644 --- a/samples/Orchestration/create-stack-from-template-file.php +++ b/samples/Orchestration/create-stack-from-template-file.php @@ -15,36 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Create a stack. + +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), + 'name' => '{stackName}', + 'template' => file_get_contents('{yamlTemplateFilePath}'), 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{hostName}', + 'image' => '{image}' ), 'timeoutMins' => 5 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index 879c131e8..b1ac369fd 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -15,36 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Create a stack. + +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', + 'name' => '{name}', 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{serverHost}', + 'image' => '{image}' ), 'timeoutMins' => 5 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/delete-stack.php b/samples/Orchestration/delete-stack.php index aa0919fec..05c0acc80 100644 --- a/samples/Orchestration/delete-stack.php +++ b/samples/Orchestration/delete-stack.php @@ -15,31 +15,21 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Delete stack. $stack->delete(); diff --git a/samples/Orchestration/get-build-info.php b/samples/Orchestration/get-build-info.php index 7303435af..ae9557a5f 100644 --- a/samples/Orchestration/get-build-info.php +++ b/samples/Orchestration/get-build-info.php @@ -15,28 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get build info. -$buildInfo = $orchestrationService->getBuildInfo(); /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ +$buildInfo = $orchestrationService->getBuildInfo(); diff --git a/samples/Orchestration/get-resource-type-template.php b/samples/Orchestration/get-resource-type-template.php index 1f9f1e509..963bbf8df 100644 --- a/samples/Orchestration/get-resource-type-template.php +++ b/samples/Orchestration/get-resource-type-template.php @@ -15,32 +15,23 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * RESOURCE_TYPE_NAME: Name of resource type -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get resource type. -$resourceType = $orchestrationService->getResourceType(getenv('RESOURCE_TYPE_NAME')); +$resourceType = $orchestrationService->getResourceType('{resourceTypeName}'); // 4. Get template for resource type. -$resourceTypeTemplate = $resourceType->getTemplate(); + /** @var $resourceTypeTemplate string **/ +$resourceTypeTemplate = $resourceType->getTemplate(); diff --git a/samples/Orchestration/get-resource-type.php b/samples/Orchestration/get-resource-type.php index 8f4022b83..6e9956039 100644 --- a/samples/Orchestration/get-resource-type.php +++ b/samples/Orchestration/get-resource-type.php @@ -15,29 +15,20 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * RESOURCE_TYPE_NAME: Name of resource type -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get resource type. -$resourceType = $orchestrationService->getResourceType(getenv('RESOURCE_TYPE_NAME')); + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ +$resourceType = $orchestrationService->getResourceType('{resourceTypeName}'); diff --git a/samples/Orchestration/get-stack-resource-event.php b/samples/Orchestration/get-stack-resource-event.php index f5f679c7e..e8b2ccbf5 100644 --- a/samples/Orchestration/get-stack-resource-event.php +++ b/samples/Orchestration/get-stack-resource-event.php @@ -15,37 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, -// * STACK_NAME: Name of stack, -// * STACK_RESOURCE_NAME: Name of resource in stack, and -// * STACK_RESOURCE_EVENT_ID: ID of stack resource event -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get resource in stack. -$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +$resource = $stack->getResource('{stackResourceName}'); // 5. Get stack resource event. -$resourceEvent = $resource->getEvent(getenv('STACK_RESOURCE_EVENT_ID')); /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ +$resourceEvent = $resource->getEvent('{stackResourceEventId}'); diff --git a/samples/Orchestration/get-stack-resource-metadata.php b/samples/Orchestration/get-stack-resource-metadata.php index dfb04f707..0b9b4c97a 100644 --- a/samples/Orchestration/get-stack-resource-metadata.php +++ b/samples/Orchestration/get-stack-resource-metadata.php @@ -15,36 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, -// * STACK_NAME: Name of stack, -// * STACK_RESOURCE_NAME: Name of resource in stack, and -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get resource in stack. -$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +$resource = $stack->getResource('{stackResourceName}'); // 5. Get stack resource metadata. -$resourceMetadata = $resource->getMetadata(); /** @var $resourceMetadata \stdClass **/ +$resourceMetadata = $resource->getMetadata(); diff --git a/samples/Orchestration/get-stack-resource.php b/samples/Orchestration/get-stack-resource.php index b347c8462..574691732 100644 --- a/samples/Orchestration/get-stack-resource.php +++ b/samples/Orchestration/get-stack-resource.php @@ -15,33 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// * STACK_RESOURCE_NAME: Name of resource in stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get resource in stack. -$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ +$resource = $stack->getResource('{stackResourceName}'); diff --git a/samples/Orchestration/get-stack-template.php b/samples/Orchestration/get-stack-template.php index 2d251ace6..828697a0e 100644 --- a/samples/Orchestration/get-stack-template.php +++ b/samples/Orchestration/get-stack-template.php @@ -15,32 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get stack template. -$stackTemplate = $stack->getStackTemplate(); /** @var $stackTemplate string **/ +$stackTemplate = $stack->getStackTemplate(); diff --git a/samples/Orchestration/get-stack.php b/samples/Orchestration/get-stack.php index 0f924e7de..5ad0b731a 100644 --- a/samples/Orchestration/get-stack.php +++ b/samples/Orchestration/get-stack.php @@ -15,29 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack using the stack name. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ +$stack = $orchestrationService->getStack('{stackName}'); diff --git a/samples/Orchestration/list-resource-types.php b/samples/Orchestration/list-resource-types.php index 5c60b1215..3d70eca47 100644 --- a/samples/Orchestration/list-resource-types.php +++ b/samples/Orchestration/list-resource-types.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get resource types. $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ } diff --git a/samples/Orchestration/list-stack-events.php b/samples/Orchestration/list-stack-events.php index 3d74384d4..3ed4533db 100644 --- a/samples/Orchestration/list-stack-events.php +++ b/samples/Orchestration/list-stack-events.php @@ -15,34 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get list of events for the stack. $stackEvents = $stack->listEvents(); + foreach ($stackEvents as $stackEvent) { /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ } diff --git a/samples/Orchestration/list-stack-resource-events.php b/samples/Orchestration/list-stack-resource-events.php index 99e38559b..29e9e81ec 100644 --- a/samples/Orchestration/list-stack-resource-events.php +++ b/samples/Orchestration/list-stack-resource-events.php @@ -15,38 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, -// * STACK_NAME: Name of stack, and -// * STACK_RESOURCE_NAME: Name of resource in stack -// - -require __DIR__ . '/../../vendor/autoload.php'; +require dirname(__DIR__) . '/../vendor/autoload.php'; + use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get resource in stack. -$resource = $stack->getResource(getenv('STACK_RESOURCE_NAME')); +$resource = $stack->getResource('{stackResourceName}'); // 5. Get list of events for the stack resource. $resourceEvents = $resource->listEvents(); + foreach ($resourceEvents as $resourceEvent) { /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ } diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index e49284078..cc38526c7 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -15,34 +15,25 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Get list of resources in the stack. $resources = $stack->listResources(); +å foreach ($resources as $resource) { /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } diff --git a/samples/Orchestration/list-stacks.php b/samples/Orchestration/list-stacks.php index 8092a367d..6edc47db4 100644 --- a/samples/Orchestration/list-stacks.php +++ b/samples/Orchestration/list-stacks.php @@ -15,30 +15,22 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stacks. $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ } diff --git a/samples/Orchestration/preview-stack-from-template-file.php b/samples/Orchestration/preview-stack-from-template-file.php index e0e793421..480798ce4 100644 --- a/samples/Orchestration/preview-stack-from-template-file.php +++ b/samples/Orchestration/preview-stack-from-template-file.php @@ -15,35 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Preview a stack. +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yaml'), + 'name' => '{name}', + 'template' => file_get_contents('{yamlTemplateFilePath}'), 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{serverHost}', + 'image' => '{image}' ) )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/preview-stack-from-template-url.php b/samples/Orchestration/preview-stack-from-template-url.php index 66167f7b3..27bdd3729 100644 --- a/samples/Orchestration/preview-stack-from-template-url.php +++ b/samples/Orchestration/preview-stack-from-template-url.php @@ -15,35 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Preview a stack. +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', + 'name' => '{name}', 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{serverHost}', + 'image' => '{image}' ) )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index fd20d682e..efe11e8fc 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -15,34 +15,26 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_USERNAME: Your Rackspace Cloud Account Username, -// * OS_PASSWORD: Your Rackspace Cloud Account Password, and -// * OS_REGION_NAME: The Rackspace Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\Rackspace; -// 1. Instantiate a Rackspace client. -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +// 1. Instantiate an OpenStack client. +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Create a stack. $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', + 'name' => '{name}', 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{serverHost}', + 'image' => '{image}' ), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/update-stack-from-template-file.php b/samples/Orchestration/update-stack-from-template-file.php index c28c18113..52219e4b2 100644 --- a/samples/Orchestration/update-stack-from-template-file.php +++ b/samples/Orchestration/update-stack-from-template-file.php @@ -15,38 +15,28 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Update stack. $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yaml'), + 'template' => '{yamlTemplateFilePath}', 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{serverHost}', + 'image' => '{image}' ), 'timeoutMins' => 5 )); diff --git a/samples/Orchestration/update-stack-from-template-url.php b/samples/Orchestration/update-stack-from-template-url.php index 683a70dfd..622d94904 100644 --- a/samples/Orchestration/update-stack-from-template-url.php +++ b/samples/Orchestration/update-stack-from-template-url.php @@ -15,39 +15,29 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, -// * OS_REGION_NAME: The OpenStack Cloud region you want to use, and -// * STACK_NAME: Name of stack -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Get stack. -$stack = $orchestrationService->getStack(getenv('STACK_NAME')); +$stack = $orchestrationService->getStack('{stackName}'); // 4. Update stack. +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack->update(array( 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + 'server_hostname' => '{serverHost}', + 'image' => '{image}' ), 'timeoutMins' => 5 )); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ diff --git a/samples/Orchestration/validate-template-from-template-file.php b/samples/Orchestration/validate-template-from-template-file.php index 5b6c4cbcc..c8b179d55 100644 --- a/samples/Orchestration/validate-template-from-template-file.php +++ b/samples/Orchestration/validate-template-from-template-file.php @@ -15,33 +15,24 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; use OpenCloud\Common\Exceptions\InvalidTemplateError; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Validate template from file. try { $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + 'template' => '{yamlTemplateFilePath}', )); } catch (InvalidTemplateError $e) { // Use $e->getMessage() for explanation of why template is invalid diff --git a/samples/Orchestration/validate-template-from-template-url.php b/samples/Orchestration/validate-template-from-template-url.php index e391fd9c1..4d498e9d6 100644 --- a/samples/Orchestration/validate-template-from-template-url.php +++ b/samples/Orchestration/validate-template-from-template-url.php @@ -15,28 +15,19 @@ * limitations under the License. */ -// -// Pre-requisites: -// * Prior to running this script, you must setup the following environment variables: -// * OS_AUTH_URL: Your OpenStack Cloud Authentication URL, -// * OS_USERNAME: Your OpenStack Cloud Account Username, -// * OS_PASSWORD: Your OpenStack Cloud Account Password, and -// * OS_REGION_NAME: The OpenStack Cloud region you want to use -// +require dirname(__DIR__) . '/../vendor/autoload.php'; -require __DIR__ . '/../../vendor/autoload.php'; use OpenCloud\OpenStack; use OpenCloud\Common\Exceptions\InvalidTemplateError; // 1. Instantiate an OpenStack client. -$client = new OpenStack(getenv('OS_AUTH_URL'), array( - 'username' => getenv('OS_USERNAME'), - 'password' => getenv('OS_PASSWORD') +$client = new OpenStack('{authUrl}', array( + 'username' => '{username}', + 'password' => '{password}', )); // 2. Obtain an Orchestration service object from the client. -$region = getenv('OS_REGION_NAME'); -$orchestrationService = $client->orchestrationService(null, $region); +$orchestrationService = $client->orchestrationService(null, '{region}'); // 3. Validate template from URL try { From cf6fe8ed4201aec13c86efe134ecb05cdac60e7d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 24 Nov 2014 14:56:20 +0100 Subject: [PATCH 241/835] Use correct import --- samples/Orchestration/quickstart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Orchestration/quickstart.php b/samples/Orchestration/quickstart.php index efe11e8fc..a81540bf0 100644 --- a/samples/Orchestration/quickstart.php +++ b/samples/Orchestration/quickstart.php @@ -17,7 +17,7 @@ require dirname(__DIR__) . '/../vendor/autoload.php'; -use OpenCloud\Rackspace; +use OpenCloud\OpenStack; // 1. Instantiate an OpenStack client. $client = new OpenStack('{authUrl}', array( @@ -31,7 +31,7 @@ // 3. Create a stack. $stack = $orchestrationService->createStack(array( 'name' => '{name}', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'templateUrl' => '{templateUrl}', 'parameters' => array( 'server_hostname' => '{serverHost}', 'image' => '{image}' From 2d5e482792e8380df1187a641d6874c1e7a107ba Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 24 Nov 2014 14:56:28 +0100 Subject: [PATCH 242/835] Use {templateUrl} --- samples/Orchestration/create-stack-from-template-url.php | 2 +- samples/Orchestration/preview-stack-from-template-url.php | 2 +- samples/Orchestration/update-stack-from-template-url.php | 2 +- samples/Orchestration/validate-template-from-template-url.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/Orchestration/create-stack-from-template-url.php b/samples/Orchestration/create-stack-from-template-url.php index b1ac369fd..c0efbd702 100644 --- a/samples/Orchestration/create-stack-from-template-url.php +++ b/samples/Orchestration/create-stack-from-template-url.php @@ -33,7 +33,7 @@ /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->createStack(array( 'name' => '{name}', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'templateUrl' => '{templateUrl}', 'parameters' => array( 'server_hostname' => '{serverHost}', 'image' => '{image}' diff --git a/samples/Orchestration/preview-stack-from-template-url.php b/samples/Orchestration/preview-stack-from-template-url.php index 27bdd3729..bced6086e 100644 --- a/samples/Orchestration/preview-stack-from-template-url.php +++ b/samples/Orchestration/preview-stack-from-template-url.php @@ -32,7 +32,7 @@ /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->previewStack(array( 'name' => '{name}', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'templateUrl' => '{templateUrl}', 'parameters' => array( 'server_hostname' => '{serverHost}', 'image' => '{image}' diff --git a/samples/Orchestration/update-stack-from-template-url.php b/samples/Orchestration/update-stack-from-template-url.php index 622d94904..23cae7be0 100644 --- a/samples/Orchestration/update-stack-from-template-url.php +++ b/samples/Orchestration/update-stack-from-template-url.php @@ -34,7 +34,7 @@ // 4. Update stack. /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'templateUrl' => '{templateUrl}', 'parameters' => array( 'server_hostname' => '{serverHost}', 'image' => '{image}' diff --git a/samples/Orchestration/validate-template-from-template-url.php b/samples/Orchestration/validate-template-from-template-url.php index 4d498e9d6..6cce6b4b4 100644 --- a/samples/Orchestration/validate-template-from-template-url.php +++ b/samples/Orchestration/validate-template-from-template-url.php @@ -32,7 +32,7 @@ // 3. Validate template from URL try { $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + 'templateUrl' => '{templateUrl}' )); } catch (InvalidTemplateError $e) { // Use $e->getMessage() for explanation of why template is invalid From a9f61749d1440f5a053a8189f9f80cad2f726e07 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 24 Nov 2014 14:57:17 +0100 Subject: [PATCH 243/835] Remove the red herring :fish: --- samples/Orchestration/list-stack-resources.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Orchestration/list-stack-resources.php b/samples/Orchestration/list-stack-resources.php index cc38526c7..eb6e47e2a 100644 --- a/samples/Orchestration/list-stack-resources.php +++ b/samples/Orchestration/list-stack-resources.php @@ -33,7 +33,7 @@ // 4. Get list of resources in the stack. $resources = $stack->listResources(); -å + foreach ($resources as $resource) { /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } From 398109a383aa4acba4ee1b3312ce7046fa2ba9e0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 24 Nov 2014 06:33:14 -0800 Subject: [PATCH 244/835] Changes after editor review; better line wrapping. --- docs/userguide/Networking/USERGUIDE.md | 147 +++++++++++++++---------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index bea5ea173..1bcc99223 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -1,10 +1,13 @@ # Complete User Guide for the Networking Service -Networking is a service that you can use to create virtual networks and attach cloud devices such as servers to these networks. +Networking is a service that you can use to create virtual networks and attach +cloud devices such as servers to these networks. -This user guide will introduce you the entities in the Networking service — networks, subnets, and ports — as well as show you how to create and manage these entities. +This user guide introduces you the entities in the Networking service — +networks, subnets, and ports — and shows you how to create and manage +these entities. -## Table of Contents +## Table of contents * [Concepts](#concepts) * [Prerequisites](#prerequisites) * [Client](#client) @@ -33,20 +36,33 @@ This user guide will introduce you the entities in the Networking service &mdash ## Concepts -To use the Networking service effectively, you should understand the following key concepts: +To use the Networking service effectively, you should understand the following +key concepts: -* **Network**: A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. +* **Network**: An isolated virtual layer-2 broadcast domain that is typically +reserved for the tenant who created it unless it is configured to be shared. The +network is the main entity in the Networking service. Ports and subnets are +always associated with a network. -* **Subnet**: A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. +* **Subnet**: An IP address block that can be used to assign IP addresses to +virtual instances (such as servers created using the Compute service). Each +subnet must have a CIDR and must be associated with a network. -* **Port**: A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. +* **Port**: A virtual switch port on a logical network switch. Virtual instances +(such as servers created using the Compute service) attach their interfaces into +ports. The port also defines the MAC address and the IP address or addresses to +be assigned to the interfaces plugged into them. When IP addresses are +associated with a port, this also implies the port is associated with a subnet +because the IP address is taken from the allocation pool for a specific subnet. ## Prerequisites ### Client -To use the Networking service, you must first instantiate a `OpenStack` or `Rackspace` client object. +To use the Networking service, you must first instantiate a `OpenStack` or +`Rackspace` client object. -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: +* If you are working with an OpenStack cloud, instantiate an +`OpenCloud\OpenStack` client as follows: ```php use OpenCloud\OpenStack; @@ -57,7 +73,8 @@ To use the Networking service, you must first instantiate a `OpenStack` or `Rack )); ``` -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: +* If you are working with the Rackspace cloud, instantiate an +`OpenCloud\Rackspace` client as follows: ```php use OpenCloud\Rackspace; @@ -72,7 +89,7 @@ To use the Networking service, you must first instantiate a `OpenStack` or `Rack All Networking operations are done via a _networking service object_. To instantiate this object, call the `networkingService` method on the `$client` -object. This method takes two arguments: +object. This method takes the following arguments: | Position | Description | Data type | Required? | Default value | Example value | | -------- | ----------- | ----------| --------- | ------------- | ------------- | @@ -85,12 +102,15 @@ $region = ''; $networkingService = $client->networkingService(null, $region); ``` -Any networks, subnets, and ports created with this `$networkingService` instance will -be stored in the cloud region specified by `$region`. +Any networks, subnets, and ports created with this `$networkingService` instance +are stored in the cloud region specified by `$region`. ## Networks -A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. +A network is an isolated virtual layer-2 broadcast domain that is typically +reserved for the tenant who created it unless it is configured to be shared. The +network is the main entity in the Networking service. Ports and subnets are +always associated with a network. ### Create a network @@ -98,9 +118,9 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `My private backend network` | +| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My private backend network` | | `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant or not. | Boolean | No | `false` | `false` | +| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | | `tenantId` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | `123456` | You can create a network as shown in the following example: @@ -117,7 +137,7 @@ $network = $networkingService->createNetwork(array( ### Create multiple networks This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the table above](#create-a-network). +be an associative array with the keys shown in [the preceding table](#create-a-network). You can create multiple networks as shown in the following example: @@ -151,7 +171,7 @@ foreach ($networks as $network) { [ [Get the executable PHP script for this example](/samples/Networking/list-networks.php) ] -### Get network +### Get a network You can retrieve a specific network by using that network's ID, as shown in the following example: @@ -162,15 +182,15 @@ $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c' [ [Get the executable PHP script for this example](/samples/Networking/get-network.php) ] -### Update network +### Update a network This operation takes one parameter, an associative array, with the following keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the network. Might not be unique. | String | No | `null` | `My updated private backend network` | +| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My updated private backend network` | | `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant or not. | Boolean | No | `false` | `false` | +| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | You can update a network as shown in the following example: @@ -182,7 +202,7 @@ $network->update(array( [ [Get the executable PHP script for this example](/samples/Networking/update-network.php) ] -### Delete network +### Delete a network You can delete a network as shown in the following example: @@ -194,7 +214,9 @@ $network->delete(); ## Subnets -A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. +A subnet represents an IP address block that can be used to assign IP addresses +to virtual instances (such as servers created using the Compute service). Each +subnet must have a CIDR and must be associated with a network. ### Create a subnet @@ -204,14 +226,14 @@ This operation takes one parameter, an associative array, with the following key | ---- | ----------- | --------- | --------- | ------------- | ------------- | | `networkId` | Network this subnet is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | | `ipVersion` | IP version | Integer (`4` or `6`) | Yes | - | `4` | -| `cidr` | CIDR representing IP range for this subnet | String (CIDR) | Yes | - | `192.168.199.0/25` | -| `name` | Human-readable name for the subnet. Might not be unique. | String | No | `null` | `My subnet` | +| `cidr` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | `192.168.199.0/25` | +| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My subnet` | | `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | | `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `allocationPools` | Sub-ranges of CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | `array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))` | -| `hostRoutes` | Routes that should be used by devices with IPs from this subnet (not including local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet or not | Boolean | No | `true` | `false` | -| `tenantId` | Owner of subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | `123456` | +| `allocationPools` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | `array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))` | +| `hostRoutes` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | +| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | +| `tenantId` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | `123456` | You can create a subnet as shown in the following example: @@ -230,7 +252,7 @@ $subnet = $networkingService->createSubnet(array( ### Create multiple subnets This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the table above](#create-a-subnet). +be an associative array with the keys shown in [the preceding table](#create-a-subnet). You can create multiple subnets as shown in the following example: @@ -253,7 +275,8 @@ foreach ($subnets as $subnet) { ### List subnets -You can list all the subnets to which you have access as shown in the following example: +You can list all the subnets to which you have access as shown in the following +example: ```php $subnets = $networkingService->listSubnets(); @@ -264,9 +287,10 @@ foreach ($subnets as $subnet) { [ [Get the executable PHP script for this example](/samples/Networking/list-subnets.php) ] -### Get subnet +### Get a subnet -You can retrieve a specific subnet by using that subnet's ID, as shown in the following example: +You can retrieve a specific subnet by using that subnet's ID, as shown in the +following example: ```php $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); @@ -275,17 +299,18 @@ $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); [ [Get the executable PHP script for this example](/samples/Networking/get-subnet.php) ] -### Update subnet +### Update a subnet -This operation takes one parameter, an associative array, with the following keys: +This operation takes one parameter, an associative array, with the following +keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the subnet. Might not be unique. | String | No | `null` | `My updated subnet` | +| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My updated subnet` | | `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.62.155` | | `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `hostRoutes` | Routes that should be used by devices with IPs from this subnet (not including local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet or not | Boolean | No | `true` | `false` | +| `hostRoutes` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))` | +| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | You can update a subnet as shown in the following example: @@ -304,7 +329,7 @@ $subnet->update(array( [ [Get the executable PHP script for this example](/samples/Networking/update-subnet.php) ] -### Delete subnet +### Delete a subnet You can delete a subnet as shown in the following example: @@ -316,7 +341,13 @@ $subnet->delete(); ## Ports -A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. +A port represents a virtual switch port on a logical network switch. Virtual +instances (such as servers created using the Compute service) attach their +interfaces into ports. The port also defines the MAC address and the IP address +or addresses to be assigned to the interfaces plugged into them. When IP +addresses are associated with a port, this also implies the port is associated +with a subnet because the IP address is taken from the allocation pool for a +specific subnet. ### Create a port @@ -325,14 +356,14 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | | `networkId` | Network this port is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `name` | Human-readable name for the port. Might not be unique. | String | No | `null` | `My port` | +| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | | `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | | `macAddress` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | `0F:5A:6F:70:E9:5C` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))` | -| `deviceId` | Identifies the device (e.g. virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (e.g. DHCP agent) using this port | String | No | `null` | `network:router_interface` | +| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))` | +| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | +| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | | `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | -| `tenantId` | Owner of port. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the port | `123456` | +| `tenantId` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | `123456` | You can create a port as shown in the following example: @@ -348,8 +379,9 @@ $port = $networkingService->createPort(array( ### Create multiple ports -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the table above](#create-a-port). +This operation takes one parameter, an indexed array. Each element of this +array must be an associative array with the keys shown in +[the preceding table](#create-a-port). You can create multiple ports as shown in the following example: @@ -374,7 +406,8 @@ foreach ($ports as $port) { ### List ports -You can list all the ports to which you have access as shown in the following example: +You can list all the ports to which you have access as shown in the following +example: ```php $ports = $networkingService->listPorts(); @@ -385,9 +418,10 @@ foreach ($ports as $port) { [ [Get the executable PHP script for this example](/samples/Networking/list-ports.php) ] -### Get port +### Get a port -You can retrieve a specific port by using that port's ID, as shown in the following example: +You can retrieve a specific port by using that port's ID, as shown in the +following example: ```php $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); @@ -398,15 +432,16 @@ $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); ### Update port -This operation takes one parameter, an associative array, with the following keys: +This operation takes one parameter, an associative array, with the following +keys: | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Human-readable name for the port. Might not be unique. | String | No | `null` | `My port` | +| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | | `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))` | -| `deviceId` | Identifies the device (e.g. virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (e.g. DHCP agent) using this port | String | No | `null` | `network:router_interface` | +| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))` | +| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | +| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | | `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | You can update a port as shown in the following example: @@ -424,7 +459,7 @@ $port->update(array( [ [Get the executable PHP script for this example](/samples/Networking/update-port.php) ] -### Delete port +### Delete a port You can delete a port as shown in the following example: From f95df612f59c34c39430db692274837efbd3a2ee Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 24 Nov 2014 06:36:50 -0800 Subject: [PATCH 245/835] Updating TOC links and labels to match headings. --- docs/userguide/Networking/USERGUIDE.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 1bcc99223..76bc83ed8 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -16,23 +16,23 @@ these entities. * [Create a network](#create-a-network) * [Create multiple networks](#create-multiple-networks) * [List networks](#list-networks) - * [Get network](#get-network) - * [Update network](#update-network) - * [Delete network](#delete-network) + * [Get a network](#get-a-network) + * [Update a network](#update-a-network) + * [Delete a network](#delete-a-network) * [Subnets](#subnets) * [Create a subnet](#create-a-subnet) * [Create multiple subnets](#create-multiple-subnets) * [List subnets](#list-subnets) - * [Get subnet](#get-subnet) - * [Update subnet](#update-subnet) - * [Delete subnet](#delete-subnet) + * [Get a subnet](#get-a-subnet) + * [Update a subnet](#update-a-subnet) + * [Delete a subnet](#delete-a-subnet) * [Ports](#ports) * [Create a port](#create-a-port) * [Create multiple ports](#create-multiple-ports) * [List ports](#list-ports) - * [Get port](#get-port) - * [Update port](#update-port) - * [Delete port](#delete-port) + * [Get a port](#get-a-port) + * [Update a port](#update-a-port) + * [Delete a port](#delete-a-port) ## Concepts From d9df96477e4540b800bd5504f71df08730e631b8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 24 Nov 2014 10:46:51 -0800 Subject: [PATCH 246/835] Fixing heading to be consistent with others of its ilk. --- docs/userguide/Networking/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 76bc83ed8..74e713b8a 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -430,7 +430,7 @@ $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); [ [Get the executable PHP script for this example](/samples/Networking/get-port.php) ] -### Update port +### Update a port This operation takes one parameter, an associative array, with the following keys: From 627ea06e2980eebfbdd24cce097c6911c848754f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 1 Dec 2014 10:46:50 +0000 Subject: [PATCH 247/835] Making batch object deletion more robust --- .../ObjectStore/Resource/Container.php | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 3f47c2227..c0b93de73 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -184,15 +184,39 @@ public function delete($deleteObjects = false) */ public function deleteAllObjects() { - $requests = array(); + $paths = array(); - $list = $this->objectList(); + $objects = $this->objectList(); - foreach ($list as $object) { - $requests[] = $this->getClient()->delete($object->getUrl()); + foreach ($objects as $object) { + $i++; + $paths[] = sprintf('/%s/%s', $this->getName(), $object->getName()); } - return $this->getClient()->send($requests); + // Batch delete can only handle 10000 paths per request + $chunks = array_chunk($paths, 10000); + foreach ($chunks as $chunk) { + $this->getService()->bulkDelete($chunk); + } + + // Poll the container for state change + $timeout = 60; + $currentTime = 0; + + while (true) { + ++$currentTime; + + if ($currentTime >= $timeout) { + return false; + } + + $metadata = $this->retrieveMetadata(); + if ($metadata->getProperty('Object-Count') === 0) { + return false; + } + } + + return true; } /** From 26d980e59ed08d581918210704a4d66dd7ac593d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 1 Dec 2014 10:53:58 +0000 Subject: [PATCH 248/835] No need for counter :thought_balloon: --- lib/OpenCloud/ObjectStore/Resource/Container.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index c0b93de73..9b970f3f5 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -189,7 +189,6 @@ public function deleteAllObjects() $objects = $this->objectList(); foreach ($objects as $object) { - $i++; $paths[] = sprintf('/%s/%s', $this->getName(), $object->getName()); } From 87797b083015a34314c105f5aa3f9aa3e6284131 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 1 Dec 2014 14:56:54 +0000 Subject: [PATCH 249/835] Use proper timeout and class constant --- .../ObjectStore/Resource/Container.php | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 9b970f3f5..0f3bb9b13 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -43,6 +43,7 @@ class Container extends AbstractContainer { const METADATA_LABEL = 'Container'; + const BATCH_DELETE_MAX = 10000; /** * This is the object that holds all the CDN functionality. This Container therefore acts as a simple wrapper and is @@ -193,26 +194,19 @@ public function deleteAllObjects() } // Batch delete can only handle 10000 paths per request - $chunks = array_chunk($paths, 10000); + $chunks = array_chunk($paths, self::BATCH_DELETE_MAX); foreach ($chunks as $chunk) { $this->getService()->bulkDelete($chunk); } // Poll the container for state change - $timeout = 60; - $currentTime = 0; - - while (true) { - ++$currentTime; - - if ($currentTime >= $timeout) { + $timeout = time() + 60; + while (time() < $timeout) { + if ($this->retrieveMetadata()->getProperty('Object-Count') === 0) { return false; } - $metadata = $this->retrieveMetadata(); - if ($metadata->getProperty('Object-Count') === 0) { - return false; - } + sleep(1); } return true; From cf46cbf5a36d30f09b5aea091ec0f4c90f35e180 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 1 Dec 2014 16:17:55 +0000 Subject: [PATCH 250/835] Moving chunking functionality to service client; deprecate bulkDelete; add container waiter --- .../ObjectStore/Resource/Container.php | 34 ++++++++----- lib/OpenCloud/ObjectStore/Service.php | 50 +++++++++++++++++-- .../ObjectStore/Resource/ContainerTest.php | 18 ++++++- .../Tests/ObjectStore/ServiceTest.php | 14 ++++++ 4 files changed, 98 insertions(+), 18 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 0f3bb9b13..6a4673e59 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -43,7 +43,6 @@ class Container extends AbstractContainer { const METADATA_LABEL = 'Container'; - const BATCH_DELETE_MAX = 10000; /** * This is the object that holds all the CDN functionality. This Container therefore acts as a simple wrapper and is @@ -193,23 +192,32 @@ public function deleteAllObjects() $paths[] = sprintf('/%s/%s', $this->getName(), $object->getName()); } - // Batch delete can only handle 10000 paths per request - $chunks = array_chunk($paths, self::BATCH_DELETE_MAX); - foreach ($chunks as $chunk) { - $this->getService()->bulkDelete($chunk); - } + $this->getService()->batchDelete($paths); - // Poll the container for state change - $timeout = time() + 60; - while (time() < $timeout) { - if ($this->retrieveMetadata()->getProperty('Object-Count') === 0) { - return false; + return $this->waitUntilEmpty(); + } + + /** + * This is a method that makes batch deletions more convenient. It continually + * polls the resource, waiting for its state to change. If the loop exceeds the + * provided timeout, it breaks and returns FALSE. + * + * @param int $secondsToWait The number of seconds to run the loop + * @return bool + */ + public function waitUntilEmpty($secondsToWait = 60, $interval = 1) + { + $endTime = time() + $secondsToWait; + + while (time() < $endTime) { + if ((int) $this->retrieveMetadata()->getProperty('Object-Count') === 0) { + return true; } - sleep(1); + sleep($interval); } - return true; + return false; } /** diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index b35cb3c31..a2d01a479 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -36,6 +36,7 @@ class Service extends AbstractService { const DEFAULT_NAME = 'cloudFiles'; const DEFAULT_TYPE = 'object-store'; + const BATCH_DELETE_MAX = 10000; /** * This holds the associated CDN service (for Rackspace public cloud) @@ -169,14 +170,55 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ } /** - * This method will delete multiple objects or containers from their account with a single request. + * @deprecated Please use {@see batchDelete()} instead. + */ + public function bulkDelete(array $paths) + { + $this->getLogger()->deprecated(__METHOD__, '::batchDelete()'); + + return $this->executeBatchDeleteRequest($paths); + } + + /** + * Batch delete will delete an array of object paths. By default, + * the API will only accept a maximum of 10,000 object deletions + * per request - so for arrays that exceed this size, it is chunked + * and sent as individual requests. + * + * @param array $paths The objects you want to delete. Each path needs + * be formatted as /{containerName}/{objectName}. If + * you are deleting object_1 and object_2 from the + * photos_container, the array will be: + * + * array( + * '/photos_container/object_1', + * '/photos_container/object_2' + * ) * - * @param array $paths A two-dimensional array of paths: - * array('container_a/file_1', 'container_b/file_78', 'container_c/file_40582') + * @return array The array of responses from the API + * @throws Exception\BulkOperationException + */ + public function batchDelete(array $paths) + { + $chunks = array_chunk($paths, self::BATCH_DELETE_MAX); + + $responses = array(); + + foreach ($chunks as $chunk) { + $responses[] = $this->executeBatchDeleteRequest($chunk); + } + + return $responses; + } + + /** + * Internal method for dispatching single batch delete requests. + * + * @param array $paths * @return \Guzzle\Http\Message\Response * @throws Exception\BulkOperationException */ - public function bulkDelete(array $paths) + private function executeBatchDeleteRequest(array $paths) { $entity = EntityBody::factory(implode(PHP_EOL, $paths)); diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 8dfd1da5f..719550e49 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -335,4 +335,20 @@ public function test_Object_Exists_False() { $this->assertFalse($this->container->objectExists('test.foo')); } -} + + public function test_Waiter_Returns_True_For_Empty_Container() + { + $response = new Response(204, array('X-Container-Object-Count' => 0)); + $this->addMockSubscriber($response); + + $this->assertTrue($this->container->waitUntilEmpty(2, 0)); + } + + public function test_Waiter_Returns_False_On_Timeout() + { + $response = new Response(204, array('X-Container-Object-Count' => 10)); + $this->addMockSubscriber($response); + + $this->assertFalse($this->container->waitUntilEmpty(0.1, 1)); + } +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php index e5a898712..dfb62e63c 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php @@ -25,6 +25,7 @@ */ namespace OpenCloud\Tests\ObjectStore; +use Guzzle\Http\Message\Response; use OpenCloud\ObjectStore\Constants\UrlType; use OpenCloud\ObjectStore\Service; @@ -137,6 +138,19 @@ public function test_Bad_Bulk_Delete() $this->service->bulkDelete(array('nonEmptyContainer')); } + public function test_Batch_Delete_Returns_Array_Of_Responses() + { + $responses = array_fill(0, 2, new Response(200)); + + foreach ($responses as $response) { + $this->addMockSubscriber($response); + } + + $paths = array_fill(0, 15000, '/foo/bar'); + + $this->assertEquals($responses, $this->service->batchDelete($paths)); + } + public function test_Accounts() { $account = $this->service->getAccount(); From 4f095fc8f66cb2ca1f8677d3173ecd522ce5c0af Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 1 Dec 2014 17:20:05 +0000 Subject: [PATCH 251/835] PSR-2 fixes --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 719550e49..db9e04f12 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -351,4 +351,4 @@ public function test_Waiter_Returns_False_On_Timeout() $this->assertFalse($this->container->waitUntilEmpty(0.1, 1)); } -} \ No newline at end of file +} From 099d68da7efdbd98be24ac180168f3e312b0b0a2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 1 Dec 2014 11:14:42 -0800 Subject: [PATCH 252/835] Check if all objects are deleted before attempting to delete container. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 6a4673e59..36c678208 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -159,7 +159,9 @@ public function getBytesQuota() public function delete($deleteObjects = false) { if ($deleteObjects === true) { - $this->deleteAllObjects(); + if (!$this->deleteAllObjects()) { + throw new ContainerException('Could not delete all objects within container. Cannot delete container.'); + } } try { From 07af90d6bcd58d97885403a8d2385719fc7db2f3 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 1 Dec 2014 16:55:31 -0600 Subject: [PATCH 253/835] Update Service.php Is this a a typo? Intended to be used with \OpenCloud\ObjectStore\Service and other service constructors? Also ambiguity of 'urlType' as used with archiveType is a little confusing. Use of constants and better naming would make the ability to use service net (internal) endpoints more obvious. --- lib/OpenCloud/Common/Constants/Service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Common/Constants/Service.php b/lib/OpenCloud/Common/Constants/Service.php index c61992fc7..636e535b5 100644 --- a/lib/OpenCloud/Common/Constants/Service.php +++ b/lib/OpenCloud/Common/Constants/Service.php @@ -19,6 +19,6 @@ class Service { - const INTERNAL_URL = 'publicUrl'; - const PUBLIC_URL = 'internalUrl'; + const INTERNAL_URL = 'internalUrl'; + const PUBLIC_URL = 'publicUrl'; } From fdea86c70b1c207433881e8b1ef2d57d41c348c0 Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 00:23:12 -0600 Subject: [PATCH 254/835] Adding the functionality for certificate mappings to the PHP SDK --- .../Resource/CertificateMappings.php | 98 +++++++++++++++++++ .../LoadBalancer/Resource/LoadBalancer.php | 89 ++++++++++++++++- 2 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php new file mode 100644 index 000000000..374085c74 --- /dev/null +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -0,0 +1,98 @@ + (object) $updated_params); + } + + } diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 69cea1699..22b408bea 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -150,6 +150,7 @@ class LoadBalancer extends PersistentResource implements HasPtrRecordsInterface protected static $url_resource = 'loadbalancers'; protected $associatedResources = array( + 'certificateMapping' => 'CertificateMappings', 'node' => 'Node', 'virtualIp' => 'VirtualIp', 'connectionLogging' => 'ConnectionLogging', @@ -158,9 +159,10 @@ class LoadBalancer extends PersistentResource implements HasPtrRecordsInterface ); protected $associatedCollections = array( - 'nodes' => 'Node', - 'virtualIps' => 'VirtualIp', - 'accessList' => 'Access' + 'certificateMappings' => 'CertificateMappings', + 'nodes' => 'Node', + 'virtualIps' => 'VirtualIp', + 'accessList' => 'Access' ); protected $createKeys = array( @@ -392,6 +394,87 @@ public function virtualIpList() return $this->getService()->resourceList('VirtualIp', null, $this); } + /** + * Returns a Certificate Mapping + * + * @return \OpenCloud\LoadBalancer\Resource\CertificateMappings + */ + public function certificateMapping($id = null) + { + return $this->getService()->resource('CertificateMappings', $id, $this); + } + + /** + * Returns a Collection of Certificate Mappings + * + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function certificateMappingList() + { + return $this->getService()->resourceList('CertificateMappings', null, $this); + } + + /** + * Creates a certificate mapping + * + * @return array of {@see \Guzzle\Http\Message\Response} objects + * @throws \OpenCloud\Common\Exceptions\MissingValueError + */ + public function addCertificateMapping( + $hostName, + $privateKey, + $certificate, + $intermediateCertificate = null + ) { + $certificateMapping = $this->certificateMapping( + array( + 'hostName' => $hostName, + 'privateKey' => $privateKey, + 'certificate' => $certificate, + 'intermediateCertificate' => $intermediateCertificate + ) + ); + $json = json_encode($certificateMapping->createJson()); + $request = $this->getClient()->post($certificateMapping->getUrl(), self::getJsonHeader(), $json); + + return $this->getClient()->send($request); + } + + /** + * Updates a certificate mapping + * + * @return array of {@see \Guzzle\Http\Message\Response} objects + * @throws \OpenCloud\Common\Exceptions\MissingValueError + */ + public function updateCertificateMapping( + $id, + $hostName = null, + $privateKey = null, + $certificate = null, + $intermediateCertificate = null + ) { + $certificateMapping = $this->certificateMapping($id); + return $certificateMapping->update( + array( + 'hostName' => $hostName, + 'privateKey' => $privateKey, + 'certificate' => $certificate, + 'intermediateCertificate' => $intermediateCertificate + ) + ); + } + + /** + * Remove a certificate mapping + * + * @param int $id id of the certificate mapping + * @return \Guzzle\Http\Message\Response + */ + public function removeCertificateMapping($certificateMappingId) + { + return $this->certificateMapping($certificateMappingId)->delete(); + } + /** * Return the session persistence resource * From 0ca9f7c29a319918633a4b030c39101281cfd25a Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 10:54:58 -0500 Subject: [PATCH 255/835] Formatting changes --- .../Resource/CertificateMappings.php | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 374085c74..27bd310e7 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -21,49 +21,44 @@ /** * Certificate Mapping uses SSL Termination to map a particular certificate - * to a corresponding hostname, allowing multiple SSL certificates to + * to a corresponding hostname, allowing multiple SSL certificates to * exist and be accurately utilized from a Load Balancer. */ class CertificateMappings extends PersistentResource { /** - * Id for the Load Balancer. - * - *@var string - */ - + * Id for the Load Balancer. + * + * @var string + */ public $id; /** - * Hostname to be mapped to certificate. - * - *@var string - */ - + * Hostname to be mapped to certificate. + * + * @var string + */ public $hostName; - - /** - * Certificate to be mapped to hostname. - * - *@var string - */ + /** + * Certificate to be mapped to hostname. + * + * @var string + */ public $certificate; /** - * Private Key to the certificate. - * - *@var string - */ - + * Private Key to the certificate. + * + * @var string + */ public $privateKey; /** - * Intermediate certificate for the chain. - * - *@var string - */ - + * Intermediate certificate for the chain. + * + * @var string + */ public $intermediateCertificate; protected static $json_name = 'certificateMapping'; @@ -74,7 +69,7 @@ class CertificateMappings extends PersistentResource 'hostName', 'certificate', 'privateKey', - 'intermediateCertificate' + 'intermediateCertificate', ); protected function updateJson($params = array()) @@ -95,4 +90,4 @@ protected function updateJson($params = array()) return (object) array('certificateMapping' => (object) $updated_params); } - } +} From 94c6a74c77ebf80c4830b36210ff2be0f315ebd1 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 10:55:29 -0500 Subject: [PATCH 256/835] Make $createKeys protected Follows the example of other child classes --- lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 27bd310e7..0d3092473 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -65,7 +65,7 @@ class CertificateMappings extends PersistentResource protected static $json_collection_name = 'certificateMappings'; protected static $url_resource = 'ssltermination/certificatemappings'; - public $createKeys = array( + protected $createKeys = array( 'hostName', 'certificate', 'privateKey', From 11d7958e741ab3c6946f81cfa77193e612ccc8c5 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 10:56:29 -0500 Subject: [PATCH 257/835] Make updateJson method more efficient Also throws an exception when trying to update an invalid field. --- .../Resource/CertificateMappings.php | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 0d3092473..6cba3d48a 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -17,6 +17,7 @@ namespace OpenCloud\LoadBalancer\Resource; +use OpenCloud\Common\Exceptions\InvalidArgumentError; use OpenCloud\Common\Resource\PersistentResource; /** @@ -74,20 +75,27 @@ class CertificateMappings extends PersistentResource protected function updateJson($params = array()) { - if ($params['hostName']) { - $updated_params['hostName'] = $params['hostName']; - } - if ($params['privateKey']) { - $updated_params['privateKey'] = $params['privateKey']; - } - if ($params['certificate']) { - $updated_params['certificate'] = $params['certificate']; + $update_fields = array( + 'hostName', + 'certificate', + 'privateKey', + 'intermediateCertificate', + ); + + $fields = array_keys($params); + foreach ($fields as $field) { + if (!in_array($field, $update_fields)) { + throw new InvalidArgumentError("You cannot update ${field}."); + } } - if ($params['intermediateCertificate']) { - $updated_params['intermediateCertificate'] = $params['intermediateCertificate']; + + $object = new \stdClass(); + $object->certificateMapping = new \stdClass(); + foreach ($params as $name => $value) { + $object->certificateMapping->$name = $this->$name; } - return (object) array('certificateMapping' => (object) $updated_params); + return $object; } } From e692101b1c1702ba5caf00ab5b4f9f7703863398 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 10:58:04 -0500 Subject: [PATCH 258/835] More formatting changes --- .../LoadBalancer/Resource/LoadBalancer.php | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 22b408bea..5922138ce 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -395,7 +395,7 @@ public function virtualIpList() } /** - * Returns a Certificate Mapping + * Returns a Certificate Mapping. * * @return \OpenCloud\LoadBalancer\Resource\CertificateMappings */ @@ -405,7 +405,7 @@ public function certificateMapping($id = null) } /** - * Returns a Collection of Certificate Mappings + * Returns a Collection of Certificate Mappings. * * @return \OpenCloud\Common\Collection\PaginatedIterator */ @@ -415,17 +415,22 @@ public function certificateMappingList() } /** - * Creates a certificate mapping + * Creates a certificate mapping. * - * @return array of {@see \Guzzle\Http\Message\Response} objects * @throws \OpenCloud\Common\Exceptions\MissingValueError + * + * @param string $hostName The domain name for the certificate. + * @param string $privateKey The private key for the certificate + * @param string $certificate The certificate itself. + * @param string $intermediateCertificate The intermediate certificate chain. + * @return array An array of \Guzzle\Http\Message\Response objects. */ public function addCertificateMapping( $hostName, $privateKey, $certificate, $intermediateCertificate = null - ) { + ){ $certificateMapping = $this->certificateMapping( array( 'hostName' => $hostName, @@ -441,10 +446,14 @@ public function addCertificateMapping( } /** - * Updates a certificate mapping + * Updates a certificate mapping. * - * @return array of {@see \Guzzle\Http\Message\Response} objects - * @throws \OpenCloud\Common\Exceptions\MissingValueError + * @param int $id ID of the certificate mapping. + * @param string $hostName (Optional) The domain name of the certificate. + * @param string $privateKey (Optional) The private key for the certificate. + * @param string $certificate The certificate itself. + * @param string $intermediateCertificate The intermediate certificate chain. + * @return array An array of \Guzzle\Http\Message\Response objects. */ public function updateCertificateMapping( $id, @@ -452,7 +461,7 @@ public function updateCertificateMapping( $privateKey = null, $certificate = null, $intermediateCertificate = null - ) { + ){ $certificateMapping = $this->certificateMapping($id); return $certificateMapping->update( array( @@ -465,9 +474,9 @@ public function updateCertificateMapping( } /** - * Remove a certificate mapping + * Remove a certificate mapping. * - * @param int $id id of the certificate mapping + * @param int $id ID of the certificate mapping. * @return \Guzzle\Http\Message\Response */ public function removeCertificateMapping($certificateMappingId) From dd9d258ceee7c495e297d8fa2ae22b52bce2c88c Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 10:58:13 -0500 Subject: [PATCH 259/835] Match variable name to docblock --- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 5922138ce..35db59f92 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -479,9 +479,9 @@ public function updateCertificateMapping( * @param int $id ID of the certificate mapping. * @return \Guzzle\Http\Message\Response */ - public function removeCertificateMapping($certificateMappingId) + public function removeCertificateMapping($id) { - return $this->certificateMapping($certificateMappingId)->delete(); + return $this->certificateMapping($id)->delete(); } /** From 63684d71db541060aa40c0c34b46bbb7569d6dd0 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 12:06:42 -0500 Subject: [PATCH 260/835] Var type is int --- lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 6cba3d48a..2247dfeed 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -30,7 +30,7 @@ class CertificateMappings extends PersistentResource /** * Id for the Load Balancer. * - * @var string + * @var int */ public $id; From 8b59a027775dd8b58a0911bdfc6dc2dfbf0dfe64 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 12:07:45 -0500 Subject: [PATCH 261/835] ID for certificate map, not Load Balancer --- lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 2247dfeed..41e291a98 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -28,7 +28,7 @@ class CertificateMappings extends PersistentResource { /** - * Id for the Load Balancer. + * Id for the Certificate Map. * * @var int */ From 762a2bea661f2bad64cbb4383e8807385e2d9435 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Tue, 2 Dec 2014 13:08:11 -0500 Subject: [PATCH 262/835] Update documentation for certificateMapping() --- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 35db59f92..cede0df4e 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -397,6 +397,9 @@ public function virtualIpList() /** * Returns a Certificate Mapping. * + * @param int|array $id (Optional) Either a particular Certificate mapping ID, or an array of data about the + * mapping. An array can include these keys: hostName, privateKey, certificate, + * intermediateCertificate. * @return \OpenCloud\LoadBalancer\Resource\CertificateMappings */ public function certificateMapping($id = null) From 750c222318d814a1d3c034e6076fd2b908fc0647 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 11:41:15 -0800 Subject: [PATCH 263/835] Reliably remove a container and its objects --- .../ObjectStore/Resource/Container.php | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 36c678208..9f170fb6e 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -159,23 +159,49 @@ public function getBytesQuota() public function delete($deleteObjects = false) { if ($deleteObjects === true) { - if (!$this->deleteAllObjects()) { - throw new ContainerException('Could not delete all objects within container. Cannot delete container.'); + $this->deleteWithObjects(); + } else { + try { + return $this->getClient()->delete($this->getUrl())->send(); + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 409) { + throw new ContainerException(sprintf( + 'The API returned this error: %s. You might have to delete all existing objects before continuing.', + (string) $e->getResponse()->getBody() + )); + } else { + throw $e; + } } } + } - try { - return $this->getClient()->delete($this->getUrl())->send(); - } catch (ClientErrorResponseException $e) { - if ($e->getResponse()->getStatusCode() == 409) { - throw new ContainerException(sprintf( - 'The API returned this error: %s. You might have to delete all existing objects before continuing.', - (string) $e->getResponse()->getBody() - )); - } else { - throw $e; + public function deleteWithObjects($secondsToWait = 60) + { + $endTime = time() + $secondsToWait; + $containerDeleted = false; + while ((time() < $endTime) && !$containerDeleted) { + error_log("Time remaining: " . ($endTime - time()) . " seconds"); + $this->deleteAllObjects(); + try { + $this->delete(); + $containerDeleted = true; + } catch (ContainerException $e) { + error_log("Container delete exception caught"); + // Ignore exception and try again + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + error_log("Container 404 exception caught"); + // Container has been deleted + $containerDeleted = true; + } else { + throw $e; + } } } + if (!$containerDeleted) { + throw new ContainerException('Container and all its objects cound not be deleted'); + } } /** @@ -186,17 +212,13 @@ public function delete($deleteObjects = false) */ public function deleteAllObjects() { + error_log("Deleting all objects..."); $paths = array(); - $objects = $this->objectList(); - foreach ($objects as $object) { $paths[] = sprintf('/%s/%s', $this->getName(), $object->getName()); } - - $this->getService()->batchDelete($paths); - - return $this->waitUntilEmpty(); + return $this->getService()->batchDelete($paths); } /** From 07bdd698c624bc96b3a28e0d41c7c8273e9c2319 Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 14:43:49 -0600 Subject: [PATCH 264/835] Adding unit test to verify we hit the right URL --- .../Tests/LoadBalancer/Resource/LoadBalancerTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php b/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php index 97d317d1c..8901ec265 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php @@ -243,6 +243,18 @@ public function test_SSL_Termination() ); } + public function test_Certificate_Mapping() + { + $this->assertEquals( + 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456/loadbalancers/2000/ssltermination/certificatemappings', + (string)$this->loadBalancer->CertificateMappings()->Url() + ); + $this->assertEquals( + 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456/loadbalancers/2000/ssltermination/certificatemappings/1', + (string)$this->loadBalancer->CertificateMappings(1)->Url() + ); + } + public function test_Metadata() { $this->assertEquals( From 9fe8acea6a2e7d0d0dad34914c657bc2b5f28f55 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 12:54:59 -0800 Subject: [PATCH 265/835] Return from if block so else block is not necessary --- .../ObjectStore/Resource/Container.php | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 9f170fb6e..43f4b4659 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -159,19 +159,20 @@ public function getBytesQuota() public function delete($deleteObjects = false) { if ($deleteObjects === true) { - $this->deleteWithObjects(); - } else { - try { - return $this->getClient()->delete($this->getUrl())->send(); - } catch (ClientErrorResponseException $e) { - if ($e->getResponse()->getStatusCode() == 409) { - throw new ContainerException(sprintf( - 'The API returned this error: %s. You might have to delete all existing objects before continuing.', - (string) $e->getResponse()->getBody() - )); - } else { - throw $e; - } + // Delegate to auxiliary method + return $this->deleteWithObjects(); + } + + try { + return $this->getClient()->delete($this->getUrl())->send(); + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 409) { + throw new ContainerException(sprintf( + 'The API returned this error: %s. You might have to delete all existing objects before continuing.', + (string) $e->getResponse()->getBody() + )); + } else { + throw $e; } } } From b3052f97d8083f354bd3aee0c5b6d1268f1f6506 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 12:55:37 -0800 Subject: [PATCH 266/835] Allow for timeout to be estimated --- lib/OpenCloud/ObjectStore/Resource/Container.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 43f4b4659..f08ac57db 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -177,8 +177,17 @@ public function delete($deleteObjects = false) } } - public function deleteWithObjects($secondsToWait = 60) + public function deleteWithObjects($secondsToWait = null) { + + // If timeout (seconds to wait) is not specified by caller, try to + // estimate it based on number of objects in container + if (is_null($secondsToWait)) { + $numObjects = $this->getObjectCount(); + $secondsToWait = $numObjects / 2; + } + + // Attempt to delete all objects and container $endTime = time() + $secondsToWait; $containerDeleted = false; while ((time() < $endTime) && !$containerDeleted) { From 7f753f47c3daa3be719691efa77c9048779bc245 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 12:56:12 -0800 Subject: [PATCH 267/835] Remove debugging statements. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index f08ac57db..c8b18d295 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -191,17 +191,14 @@ public function deleteWithObjects($secondsToWait = null) $endTime = time() + $secondsToWait; $containerDeleted = false; while ((time() < $endTime) && !$containerDeleted) { - error_log("Time remaining: " . ($endTime - time()) . " seconds"); $this->deleteAllObjects(); try { $this->delete(); $containerDeleted = true; } catch (ContainerException $e) { - error_log("Container delete exception caught"); // Ignore exception and try again } catch (ClientErrorResponseException $e) { if ($e->getResponse()->getStatusCode() == 404) { - error_log("Container 404 exception caught"); // Container has been deleted $containerDeleted = true; } else { @@ -222,7 +219,6 @@ public function deleteWithObjects($secondsToWait = null) */ public function deleteAllObjects() { - error_log("Deleting all objects..."); $paths = array(); $objects = $this->objectList(); foreach ($objects as $object) { From 015ff09073e12733dddd1c8798890f259e89168c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 12:56:28 -0800 Subject: [PATCH 268/835] Return response of container deletion API call. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index c8b18d295..5765e8fa4 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -193,7 +193,7 @@ public function deleteWithObjects($secondsToWait = null) while ((time() < $endTime) && !$containerDeleted) { $this->deleteAllObjects(); try { - $this->delete(); + $response = $this->delete(); $containerDeleted = true; } catch (ContainerException $e) { // Ignore exception and try again @@ -209,6 +209,7 @@ public function deleteWithObjects($secondsToWait = null) if (!$containerDeleted) { throw new ContainerException('Container and all its objects cound not be deleted'); } + return $response; } /** From dd8c6407bb99d6a9c532a4b7b7df5ad38e04a26d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 12:56:49 -0800 Subject: [PATCH 269/835] Remove waiter method (no longer used) and associated tests. --- .../ObjectStore/Resource/Container.php | 23 ------------------- .../ObjectStore/Resource/ContainerTest.php | 16 ------------- 2 files changed, 39 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 5765e8fa4..41776b072 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -228,29 +228,6 @@ public function deleteAllObjects() return $this->getService()->batchDelete($paths); } - /** - * This is a method that makes batch deletions more convenient. It continually - * polls the resource, waiting for its state to change. If the loop exceeds the - * provided timeout, it breaks and returns FALSE. - * - * @param int $secondsToWait The number of seconds to run the loop - * @return bool - */ - public function waitUntilEmpty($secondsToWait = 60, $interval = 1) - { - $endTime = time() + $secondsToWait; - - while (time() < $endTime) { - if ((int) $this->retrieveMetadata()->getProperty('Object-Count') === 0) { - return true; - } - - sleep($interval); - } - - return false; - } - /** * Creates a Collection of objects in the container * diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index db9e04f12..8dfd1da5f 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -335,20 +335,4 @@ public function test_Object_Exists_False() { $this->assertFalse($this->container->objectExists('test.foo')); } - - public function test_Waiter_Returns_True_For_Empty_Container() - { - $response = new Response(204, array('X-Container-Object-Count' => 0)); - $this->addMockSubscriber($response); - - $this->assertTrue($this->container->waitUntilEmpty(2, 0)); - } - - public function test_Waiter_Returns_False_On_Timeout() - { - $response = new Response(204, array('X-Container-Object-Count' => 10)); - $this->addMockSubscriber($response); - - $this->assertFalse($this->container->waitUntilEmpty(0.1, 1)); - } } From 9b944a6f0c3d2c995371a1912636ffd56723f3ae Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 15:31:28 -0600 Subject: [PATCH 270/835] Fixing spacing issue --- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index cede0df4e..9077cf628 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -433,7 +433,7 @@ public function addCertificateMapping( $privateKey, $certificate, $intermediateCertificate = null - ){ + ) { $certificateMapping = $this->certificateMapping( array( 'hostName' => $hostName, From 48e05e140c0ee110b9babd8daa92ab1ec0c37bd1 Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 15:32:09 -0600 Subject: [PATCH 271/835] Fixing spacing issue --- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 9077cf628..19b667cd7 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -464,7 +464,7 @@ public function updateCertificateMapping( $privateKey = null, $certificate = null, $intermediateCertificate = null - ){ + ) { $certificateMapping = $this->certificateMapping($id); return $certificateMapping->update( array( From 1f5ab053b521c8d50af30e5279809e9a1a3171ef Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 15:33:00 -0600 Subject: [PATCH 272/835] Fixing line spacing issue --- lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 41e291a98..c4fed5dd3 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -97,5 +97,4 @@ protected function updateJson($params = array()) return $object; } - } From e906b984363275ec12e1e5e50dfa5e4e83b2d690 Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 16:15:45 -0600 Subject: [PATCH 273/835] Whoops, should invoke this correctly --- .../Tests/LoadBalancer/Resource/LoadBalancerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php b/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php index 8901ec265..88e590aa2 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php @@ -247,11 +247,11 @@ public function test_Certificate_Mapping() { $this->assertEquals( 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456/loadbalancers/2000/ssltermination/certificatemappings', - (string)$this->loadBalancer->CertificateMappings()->Url() + (string)$this->loadBalancer->certificateMappings()->Url() ); $this->assertEquals( 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456/loadbalancers/2000/ssltermination/certificatemappings/1', - (string)$this->loadBalancer->CertificateMappings(1)->Url() + (string)$this->loadBalancer->certificateMappings(1)->Url() ); } From afa8211c3a83d426da5da0d34cfcd3d623f84ae8 Mon Sep 17 00:00:00 2001 From: tylerturk Date: Tue, 2 Dec 2014 16:34:41 -0600 Subject: [PATCH 274/835] Whoops, should invoke this correctly --- .../Tests/LoadBalancer/Resource/LoadBalancerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php b/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php index 88e590aa2..b4a270a8c 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/Resource/LoadBalancerTest.php @@ -247,11 +247,11 @@ public function test_Certificate_Mapping() { $this->assertEquals( 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456/loadbalancers/2000/ssltermination/certificatemappings', - (string)$this->loadBalancer->certificateMappings()->Url() + (string)$this->loadBalancer->certificateMapping()->Url() ); $this->assertEquals( 'https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123456/loadbalancers/2000/ssltermination/certificatemappings/1', - (string)$this->loadBalancer->certificateMappings(1)->Url() + (string)$this->loadBalancer->certificateMapping(1)->Url() ); } From 5e2c0df3c32d54bd827e0e9afc40b959baa2a011 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 2 Dec 2014 16:30:28 -0800 Subject: [PATCH 275/835] Appeasing the PSR-2 linter. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 41776b072..e4f619af0 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -179,7 +179,6 @@ public function delete($deleteObjects = false) public function deleteWithObjects($secondsToWait = null) { - // If timeout (seconds to wait) is not specified by caller, try to // estimate it based on number of objects in container if (is_null($secondsToWait)) { From 82344206ccdd7c4a7d6dae8bba930b4cc196ae7c Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 3 Dec 2014 10:48:46 -0500 Subject: [PATCH 276/835] Remove trailing s from class name --- .../LoadBalancer/Resource/CertificateMappings.php | 2 +- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index c4fed5dd3..28e75aa9a 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -25,7 +25,7 @@ * to a corresponding hostname, allowing multiple SSL certificates to * exist and be accurately utilized from a Load Balancer. */ -class CertificateMappings extends PersistentResource +class CertificateMapping extends PersistentResource { /** * Id for the Certificate Map. diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 19b667cd7..bd0742bcb 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -150,7 +150,7 @@ class LoadBalancer extends PersistentResource implements HasPtrRecordsInterface protected static $url_resource = 'loadbalancers'; protected $associatedResources = array( - 'certificateMapping' => 'CertificateMappings', + 'certificateMapping' => 'CertificateMapping', 'node' => 'Node', 'virtualIp' => 'VirtualIp', 'connectionLogging' => 'ConnectionLogging', @@ -159,7 +159,7 @@ class LoadBalancer extends PersistentResource implements HasPtrRecordsInterface ); protected $associatedCollections = array( - 'certificateMappings' => 'CertificateMappings', + 'certificateMappings' => 'CertificateMapping', 'nodes' => 'Node', 'virtualIps' => 'VirtualIp', 'accessList' => 'Access' @@ -400,11 +400,11 @@ public function virtualIpList() * @param int|array $id (Optional) Either a particular Certificate mapping ID, or an array of data about the * mapping. An array can include these keys: hostName, privateKey, certificate, * intermediateCertificate. - * @return \OpenCloud\LoadBalancer\Resource\CertificateMappings + * @return \OpenCloud\LoadBalancer\Resource\CertificateMapping */ public function certificateMapping($id = null) { - return $this->getService()->resource('CertificateMappings', $id, $this); + return $this->getService()->resource('CertificateMapping', $id, $this); } /** @@ -414,7 +414,7 @@ public function certificateMapping($id = null) */ public function certificateMappingList() { - return $this->getService()->resourceList('CertificateMappings', null, $this); + return $this->getService()->resourceList('CertificateMapping', null, $this); } /** From 4014af2c8ab4e6750cdc3dfe82b1d307c92ceebc Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 3 Dec 2014 10:49:08 -0500 Subject: [PATCH 277/835] Adjust variable name --- lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 28e75aa9a..4dc4053d1 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -75,7 +75,7 @@ class CertificateMapping extends PersistentResource protected function updateJson($params = array()) { - $update_fields = array( + $updateFields = array( 'hostName', 'certificate', 'privateKey', From 0b8b9adb082a0c72fb4239bb2752a3c8e0b81ee9 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 3 Dec 2014 10:49:18 -0500 Subject: [PATCH 278/835] Combine two loops into one --- .../LoadBalancer/Resource/CertificateMappings.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php index 4dc4053d1..3b770a212 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php @@ -82,16 +82,12 @@ protected function updateJson($params = array()) 'intermediateCertificate', ); - $fields = array_keys($params); - foreach ($fields as $field) { - if (!in_array($field, $update_fields)) { - throw new InvalidArgumentError("You cannot update ${field}."); - } - } - $object = new \stdClass(); $object->certificateMapping = new \stdClass(); foreach ($params as $name => $value) { + if (!in_array($name, $updateFields)) { + throw new InvalidArgumentError("You cannot update ${name}."); + } $object->certificateMapping->$name = $this->$name; } From a9372ace3302ea888ba1cfaf436d284f11959314 Mon Sep 17 00:00:00 2001 From: tylerturk Date: Wed, 3 Dec 2014 10:47:11 -0600 Subject: [PATCH 279/835] Renaming PHP script to fix the inclusion --- .../Resource/{CertificateMappings.php => CertificateMapping.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/OpenCloud/LoadBalancer/Resource/{CertificateMappings.php => CertificateMapping.php} (100%) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php similarity index 100% rename from lib/OpenCloud/LoadBalancer/Resource/CertificateMappings.php rename to lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php From cab84bf4db307a5257e553ba89862aec7e5fd5cf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 3 Dec 2014 09:14:37 -0800 Subject: [PATCH 280/835] Changing null check for consistency and slight perf gain. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index e4f619af0..e23db01a9 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -181,7 +181,7 @@ public function deleteWithObjects($secondsToWait = null) { // If timeout (seconds to wait) is not specified by caller, try to // estimate it based on number of objects in container - if (is_null($secondsToWait)) { + if (null === $secondsToWait) { $numObjects = $this->getObjectCount(); $secondsToWait = $numObjects / 2; } From 381d64e2a44cb8f0f6a0ab2afbdf0ae29d38fc7b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 3 Dec 2014 09:15:46 -0800 Subject: [PATCH 281/835] Casting $secondsToWait to integer so it is not a fraction. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index e23db01a9..ae020ca6e 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -183,7 +183,7 @@ public function deleteWithObjects($secondsToWait = null) // estimate it based on number of objects in container if (null === $secondsToWait) { $numObjects = $this->getObjectCount(); - $secondsToWait = $numObjects / 2; + $secondsToWait = (int) $numObjects / 2; } // Attempt to delete all objects and container From 12d74cab3b52a688e2e8e661b3aa335b51eb0c7c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 3 Dec 2014 09:17:16 -0800 Subject: [PATCH 282/835] Get a fresh object count from the service. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index ae020ca6e..b81521370 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -182,7 +182,7 @@ public function deleteWithObjects($secondsToWait = null) // If timeout (seconds to wait) is not specified by caller, try to // estimate it based on number of objects in container if (null === $secondsToWait) { - $numObjects = $this->getObjectCount(); + $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count'); $secondsToWait = (int) $numObjects / 2; } From 4456fd64e0a7e2d1612281fdcf2cc4e815000fbd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 3 Dec 2014 11:30:59 -0800 Subject: [PATCH 283/835] Mocking HEAD request in unit test to match implementation code. --- .../OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 8dfd1da5f..378ae17de 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -97,7 +97,13 @@ public function test_NonCDN_Container() public function test_Delete() { $container = $this->container; + + $objectCountResponse = $this->makeResponse(null, 204); + $objectCountResponse->setHeader('X-Container-Object-Count', 5); + $this->addMockSubscriber($objectCountResponse); + $this->addMockSubscriber($this->makeResponse('[]', 200)); + $response = $container->delete(true); $this->isResponse($response); } From 4a0dc552411c88f2d6ce1547b891e7d443f0a3b4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 4 Dec 2014 10:53:15 +0000 Subject: [PATCH 284/835] Ensure token header is reset before authenticating; fixes #483 --- lib/OpenCloud/OpenStack.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index fc1ad9c49..9f6cbf36c 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -370,6 +370,10 @@ public function getUser() */ public function authenticate() { + // OpenStack APIs will return a 401 if an expired X-Auth-Token is sent, + // so we need to reset the value before authenticating for another one. + $this->updateTokenHeader(''); + $identity = IdentityService::factory($this); $response = $identity->generateToken($this->getCredentials()); @@ -384,7 +388,7 @@ public function authenticate() } // Set X-Auth-Token HTTP request header - $this->updateTokenHeader(); + $this->updateTokenHeader($this->getToken()); } /** @@ -422,7 +426,7 @@ public function importCredentials(array $values) { if (!empty($values['token'])) { $this->setToken($values['token']); - $this->updateTokenHeader(); + $this->updateTokenHeader($this->getToken()); } if (!empty($values['expiration'])) { $this->setExpiration($values['expiration']); @@ -438,13 +442,12 @@ public function importCredentials(array $values) /** * Sets the X-Auth-Token header. If no value is explicitly passed in, the current token is used. * - * @param string $token Optional value of token. + * @param string $token Value of header. * @return void */ - private function updateTokenHeader($token = null) + private function updateTokenHeader($token) { - $token = $token ? : $this->getToken(); - $this->setDefaultOption('headers/X-Auth-Token', (string)$token); + $this->setDefaultOption('headers/X-Auth-Token', (string) $token); } /** From cc90d230f1cf02b57f768da0fb962d0aeccffbbe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 4 Dec 2014 05:14:18 -0800 Subject: [PATCH 285/835] Correctly rounding numeric expression to integer. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index b81521370..321b5a11a 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -183,7 +183,7 @@ public function deleteWithObjects($secondsToWait = null) // estimate it based on number of objects in container if (null === $secondsToWait) { $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count'); - $secondsToWait = (int) $numObjects / 2; + $secondsToWait = round($numObjects / 2); } // Attempt to delete all objects and container From 4022318f92bb0799c7bc4674f04678e35d552823 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 4 Dec 2014 05:17:03 -0800 Subject: [PATCH 286/835] Constructing mock response in one line. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 378ae17de..2ada2c41c 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -98,10 +98,7 @@ public function test_Delete() { $container = $this->container; - $objectCountResponse = $this->makeResponse(null, 204); - $objectCountResponse->setHeader('X-Container-Object-Count', 5); - $this->addMockSubscriber($objectCountResponse); - + $this->addMockSubscriber(new Response(204, array('X-Container-Object-Count' => 5))); $this->addMockSubscriber($this->makeResponse('[]', 200)); $response = $container->delete(true); From ffca1d9af831e176797f0eec0ce77adfd3fe3a46 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 4 Dec 2014 06:23:09 -0800 Subject: [PATCH 287/835] Updating version in readiness for release. --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index d882bd145..6388986fc 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.10.0'; + const VERSION = '1.12.0'; /** * @return string Indicate current SDK version. From 7492590b3f8eba5a6d11b9380259f3fd630be852 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 4 Dec 2014 06:46:43 -0800 Subject: [PATCH 288/835] Documenting release steps. --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f02a508da..4224443c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,3 +52,13 @@ The code in this library is compliant with the [PSR-2 Coding Style Guide](https: $ vendor/bin/php-cs-fixer fix --level psr2 . Running this command will _change_ your code to become PSR-2 compliant. You will need to _commit_ these changes and make them part of your pull request. + +## Releasing a new version of php-opencloud +If you are a core contributor to php-opencloud, you have the power to release new versions of it. Here are the steps to follow to ensure a proper release: + +1. Update the value of the the [`VERSION` constant](/lib/OpenCloud/Version.php#L30). +2. Merge the `working` branch into the `master` branch. +3. [Run the smoke tests](#to-run-the-full-suite-of-acceptance-tests). If they fail, make necessary changes and go to step 2. +4. [Create new release notes](https://github.com/rackspace/php-opencloud/releases/new). +5. Publish release notes. +6. Announce release via appropriate channels. From 0c21756170250483d824ee1f1f49f8ba970e2ecb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 4 Dec 2014 07:02:47 -0800 Subject: [PATCH 289/835] Adding the all-important step 7. --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4224443c6..f3c3425de 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,3 +62,4 @@ If you are a core contributor to php-opencloud, you have the power to release ne 4. [Create new release notes](https://github.com/rackspace/php-opencloud/releases/new). 5. Publish release notes. 6. Announce release via appropriate channels. +7. Party :tada: :balloon: \ No newline at end of file From 8cfb1a64670c1bdaf455e7ea52782c418d39afbb Mon Sep 17 00:00:00 2001 From: Veres Lajos Date: Thu, 4 Dec 2014 21:32:31 +0000 Subject: [PATCH 290/835] typofixes - https://github.com/vlajos/misspell_fixer --- docs/changelog/1.7.0.md | 2 +- docs/userguide/Clients.md | 2 +- docs/userguide/ObjectStore/USERGUIDE.md | 2 +- docs/userguide/Queues/Claim.md | 2 +- lib/OpenCloud/Compute/Resource/Server.php | 2 +- lib/OpenCloud/Compute/Resource/ServerMetadata.php | 2 +- lib/OpenCloud/OpenStack.php | 2 +- lib/OpenCloud/Queues/Resource/Claim.php | 2 +- samples/Compute/create_server_and_get_ip.php | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/changelog/1.7.0.md b/docs/changelog/1.7.0.md index 482f5950b..8e22e3f9e 100644 --- a/docs/changelog/1.7.0.md +++ b/docs/changelog/1.7.0.md @@ -20,7 +20,7 @@ For a complete list of features, [view the official page](http://guzzlephp.org). ## Keypair support -Although code now exists in the SDK for API compatability for creating servers with keypairs, there has been +Although code now exists in the SDK for API compatibility for creating servers with keypairs, there has been a recent bug in Nova where certain endpoints (DFW, IAD and ORD) do not exhibit the desired functionality. A patch will be released in the near future. For reference, you just need to add in an extra array key: diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 954769de7..9a7efec8e 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -2,7 +2,7 @@ ## Overview -A Client is the object responsibile for issuing HTTP requests and receiving responses from the API. In short, it forms the core of the SDK because it controls how functionality is executed. All services depend on the client to work. +A Client is the object responsible for issuing HTTP requests and receiving responses from the API. In short, it forms the core of the SDK because it controls how functionality is executed. All services depend on the client to work. Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\Rackspace`. The latter extends the former, meaning that much of the functionality is shared between them. The OpenStack client extends functionality from other base classes, that trace all the way back to Guzzle's root class: diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index b1f0f1b0d..8bb3f3d45 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -334,7 +334,7 @@ The third parameter to `bulkExtract` is the type of the archive file being uploa * `UrlType:TAR_GZ` for tar archive files that are compressed with gzip, *or* * `UrlType::TAR_BZ` for tar archive file that are compressed with bzip -Note that the value of `$remotePath` could have been a (pseudo-hierarchical folder)[#psuedo-hierarchical-folders] such as `images/blog` as well. +Note that the value of `$remotePath` could have been a (pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as `images/blog` as well. ### List Objects in a Container You can list all the objects stored in a container. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index 13e12eac8..dbaa53ba9 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -8,7 +8,7 @@ workers from attempting to process the same messages. A Claim is initialized on its parent object, a Queue: ```php -// To intialize an empty object: +// To initialize an empty object: $claim = $queue->getClaim(); // or retrieve a specific claim: diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 9de1cba5a..11fb474ca 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -32,7 +32,7 @@ * A virtual machine (VM) instance in the Cloud Servers environment. * * @note This implementation supports extension attributes OS-DCF:diskConfig, - * RAX-SERVER:bandwidth, rax-bandwidth:bandwith. + * RAX-SERVER:bandwidth, rax-bandwidth:bandwidth. */ class Server extends NovaResource implements HasPtrRecordsInterface { diff --git a/lib/OpenCloud/Compute/Resource/ServerMetadata.php b/lib/OpenCloud/Compute/Resource/ServerMetadata.php index 28f2f8650..aba039563 100644 --- a/lib/OpenCloud/Compute/Resource/ServerMetadata.php +++ b/lib/OpenCloud/Compute/Resource/ServerMetadata.php @@ -38,7 +38,7 @@ class ServerMetadata extends Metadata private $url; // the URL of this particular metadata item or block /** - * Contructs a Metadata object associated with a Server or Image object + * Constructs a Metadata object associated with a Server or Image object * * @param object $parent either a Server or an Image object * @param string $key the (optional) key for the metadata item diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index fc1ad9c49..a2a941479 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -145,7 +145,7 @@ public function getToken() } /** - * Set the full toke object + * Set the full token object */ public function setTokenObject(Token $token) { diff --git a/lib/OpenCloud/Queues/Resource/Claim.php b/lib/OpenCloud/Queues/Resource/Claim.php index fcb6ad9c1..5d4e2dc21 100644 --- a/lib/OpenCloud/Queues/Resource/Claim.php +++ b/lib/OpenCloud/Queues/Resource/Claim.php @@ -114,7 +114,7 @@ public function create($params = array()) /** * Updates the current Claim. It is recommended that you periodically renew - * claims during long-running batches of work to avoid loosing a claim in + * claims during long-running batches of work to avoid losing a claim in * the middle of processing a message. This is done by setting a new TTL for * the claim (which may be different from the original TTL). The server will * then reset the age of the claim and apply the new TTL. diff --git a/samples/Compute/create_server_and_get_ip.php b/samples/Compute/create_server_and_get_ip.php index a6029bc7c..3f0bbfe6f 100644 --- a/samples/Compute/create_server_and_get_ip.php +++ b/samples/Compute/create_server_and_get_ip.php @@ -50,5 +50,5 @@ // 5. Wait for the server to become ACTIVE. $server->waitFor(ServerState::ACTIVE); -// 6. Retreive the server's IP +// 6. Retrieve the server's IP $ipv4Address = $server->accessIPv4; From 4ecf4587a98fabfd0cdb0392899d28bfb4212ca9 Mon Sep 17 00:00:00 2001 From: joshrencher Date: Sat, 6 Dec 2014 03:19:26 -0800 Subject: [PATCH 291/835] Update Container.php As is, deleteWithObjects() will fail if container is EMPTY. This is because an object count of zero means $secondsToWait = 0, which means $endTime = time(), which means WHILE loop never runs. Quickest solution was to change "time() < $endTime" to "time() <= $endTime, but it made more sense to just delete the container if it's empty. --- .../ObjectStore/Resource/Container.php | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 321b5a11a..2d15e8846 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -179,34 +179,38 @@ public function delete($deleteObjects = false) public function deleteWithObjects($secondsToWait = null) { - // If timeout (seconds to wait) is not specified by caller, try to - // estimate it based on number of objects in container - if (null === $secondsToWait) { - $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count'); - $secondsToWait = round($numObjects / 2); - } - - // Attempt to delete all objects and container - $endTime = time() + $secondsToWait; - $containerDeleted = false; - while ((time() < $endTime) && !$containerDeleted) { - $this->deleteAllObjects(); - try { - $response = $this->delete(); - $containerDeleted = true; - } catch (ContainerException $e) { - // Ignore exception and try again - } catch (ClientErrorResponseException $e) { - if ($e->getResponse()->getStatusCode() == 404) { - // Container has been deleted + // If container is empty, just delete it + $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count'); + if ($numObjects === 0) $response = $this->delete(); + + // Else attempt to delete all objects first, then delete container + else { + // If timeout (seconds to wait) is not specified by caller, try to + // estimate it based on number of objects in container + if (null === $secondsToWait) $secondsToWait = round($numObjects / 2); + + // Attempt to delete all objects and container + $endTime = time() + $secondsToWait; + $containerDeleted = false; + while ((time() < $endTime) && !$containerDeleted) { + $this->deleteAllObjects(); + try { + $response = $this->delete(); $containerDeleted = true; - } else { - throw $e; + } catch (ContainerException $e) { + // Ignore exception and try again + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + // Container has been deleted + $containerDeleted = true; + } else { + throw $e; + } } } - } - if (!$containerDeleted) { - throw new ContainerException('Container and all its objects cound not be deleted'); + if (!$containerDeleted) { + throw new ContainerException('Container and all its objects could not be deleted.'); + } } return $response; } From 0bb1e639219d11920a6fc9841e4f362fb9dcd497 Mon Sep 17 00:00:00 2001 From: Josh Rencher Date: Mon, 8 Dec 2014 12:55:06 -0800 Subject: [PATCH 292/835] make deleteWithObjects() work with empty containers 1) use getObjectCount() to check container size, 2) return early if empty container deleted, 3) conditional syntax changed for PSR-2 compliance, and 4) removed "and all its objects" from container exception message since we can assume objects in container were successfully deleted by deleteAllObjects(); --- .../ObjectStore/Resource/Container.php | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 2d15e8846..112141202 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -180,37 +180,36 @@ public function delete($deleteObjects = false) public function deleteWithObjects($secondsToWait = null) { // If container is empty, just delete it - $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count'); - if ($numObjects === 0) $response = $this->delete(); - - // Else attempt to delete all objects first, then delete container - else { - // If timeout (seconds to wait) is not specified by caller, try to - // estimate it based on number of objects in container - if (null === $secondsToWait) $secondsToWait = round($numObjects / 2); - - // Attempt to delete all objects and container - $endTime = time() + $secondsToWait; - $containerDeleted = false; - while ((time() < $endTime) && !$containerDeleted) { - $this->deleteAllObjects(); - try { - $response = $this->delete(); + $numObjects = $this->getObjectCount(); + if ($numObjects === 0) { + return $this->delete(); + } + // If timeout ($secondsToWait) is not specified by caller, + // try to estimate it based on number of objects in container + if (null === $secondsToWait) { + $secondsToWait = round($numObjects / 2); + } + // Attempt to delete all objects and container + $endTime = time() + $secondsToWait; + $containerDeleted = false; + while ((time() < $endTime) && !$containerDeleted) { + $this->deleteAllObjects(); + try { + $response = $this->delete(); + $containerDeleted = true; + } catch (ContainerException $e) { + // Ignore exception and try again + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + // Container has been deleted $containerDeleted = true; - } catch (ContainerException $e) { - // Ignore exception and try again - } catch (ClientErrorResponseException $e) { - if ($e->getResponse()->getStatusCode() == 404) { - // Container has been deleted - $containerDeleted = true; - } else { - throw $e; - } + } else { + throw $e; } } - if (!$containerDeleted) { - throw new ContainerException('Container and all its objects could not be deleted.'); - } + } + if (!$containerDeleted) { + throw new ContainerException('Container could not be deleted.'); } return $response; } From 3352966b3d14a16a1e2714284497554b03c62878 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 9 Dec 2014 16:06:25 +0530 Subject: [PATCH 293/835] If versionless service URL is found but no supported service version is specified, use URL as-is. --- lib/OpenCloud/Common/Service/Endpoint.php | 15 +++++++++------ .../Tests/Common/Service/EndpointTest.php | 10 +++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/OpenCloud/Common/Service/Endpoint.php b/lib/OpenCloud/Common/Service/Endpoint.php index 1b5dbc302..2388dc258 100644 --- a/lib/OpenCloud/Common/Service/Endpoint.php +++ b/lib/OpenCloud/Common/Service/Endpoint.php @@ -18,7 +18,7 @@ namespace OpenCloud\Common\Service; use Guzzle\Http\Url; -use OpenCloud\Common\Http\Client; +use OpenCloud\OpenStack; use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Exceptions\UnsupportedVersionError; @@ -48,10 +48,10 @@ class Endpoint /** * @param $object * @param string $supportedServiceVersion Service version supported by the SDK - * @param OpenCloud\Common\Http\Client $client HTTP client + * @param OpenCloud\OpenStack $client OpenStack client * @return Endpoint */ - public static function factory($object, $supportedServiceVersion, Client $client) + public static function factory($object, $supportedServiceVersion, OpenStack $client) { $endpoint = new self(); @@ -130,10 +130,10 @@ public function getRegion() * * @param string $url Endpoint URL * @param string $supportedServiceVersion Service version supported by the SDK - * @param OpenCloud\Common\Http\Client $client HTTP client + * @param OpenCloud\OpenStack $client OpenStack client * @return Guzzle/Http/Url Endpoint URL with version in it */ - private function getVersionedUrl($url, $supportedServiceVersion, Client $client) + private function getVersionedUrl($url, $supportedServiceVersion, OpenStack $client) { $versionRegex = '/\/[vV][0-9][0-9\.]*/'; if (1 === preg_match($versionRegex, $url)) { @@ -141,8 +141,11 @@ private function getVersionedUrl($url, $supportedServiceVersion, Client $client) return Url::factory($url); } + // If there is no version in $url but no $supportedServiceVersion + // is specified, just return $url as-is but log a warning if (is_null($supportedServiceVersion)) { - throw new UnsupportedVersionError('Service version supported by SDK not specified.'); + $client->getLogger()->warning('Service version supported by SDK not specified. Using versionless service URL as-is, without negotiating version.'); + return Url::factory($url); } // Make GET request to URL diff --git a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php index 1be4e09d9..babcc7209 100644 --- a/tests/OpenCloud/Tests/Common/Service/EndpointTest.php +++ b/tests/OpenCloud/Tests/Common/Service/EndpointTest.php @@ -72,18 +72,18 @@ public function testGetVersionedUrlWithVersionLessEndpointInvalidResponse() ); } - /** - * @expectedException OpenCloud\Common\Exceptions\UnsupportedVersionError - */ public function testGetVersionedUrlWithVersionLessEndpointSupportedVersionNotSpecified() { $endpoint = new Endpoint(); - $this->invokeMethod( + $expectedUrl = "http://hostport"; + $actualUrl = $this->invokeMethod( $endpoint, 'getVersionedUrl', - array('http://hostport', null, $this->client) + array($expectedUrl, null, $this->client) ); + + $this->assertEquals($expectedUrl, $actualUrl); } public function testGetVersionedUrlWithVersionedEndpointUrl() From 8cdd6c3d44e198dc162cb48e3a7cdda8570cc352 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 9 Dec 2014 16:26:10 +0530 Subject: [PATCH 294/835] Bumping up version for release. --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index 6388986fc..061ac53d9 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.12.0'; + const VERSION = '1.12.1'; /** * @return string Indicate current SDK version. From 971419aea92d78a0982dc4236deadb3c3bf832e3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 9 Dec 2014 17:39:53 +0530 Subject: [PATCH 295/835] Create test files in the correct place. --- tests/OpenCloud/Smoke/Unit/ObjectStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Smoke/Unit/ObjectStore.php b/tests/OpenCloud/Smoke/Unit/ObjectStore.php index dccb74f1b..a24b1a94f 100644 --- a/tests/OpenCloud/Smoke/Unit/ObjectStore.php +++ b/tests/OpenCloud/Smoke/Unit/ObjectStore.php @@ -39,7 +39,7 @@ private function createFiles($dir) { $content = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1000); for ($i = 1; $i <= 50; $i++) { - $fh = fopen($dir . self::OBJECT_NAME . "_$i", 'c+'); + $fh = fopen($dir . '/' . self::OBJECT_NAME . "_$i", 'c+'); fwrite($fh, $content); fclose($fh); } From c00d933355ac99754a0492e124e7fa49b4539e46 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 9 Dec 2014 19:12:15 +0530 Subject: [PATCH 296/835] Checking for the latest object count, not a possibly stale one. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 112141202..b5558d20f 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -180,8 +180,8 @@ public function delete($deleteObjects = false) public function deleteWithObjects($secondsToWait = null) { // If container is empty, just delete it - $numObjects = $this->getObjectCount(); - if ($numObjects === 0) { + $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count'); + if (0 === $numObjects) { return $this->delete(); } // If timeout ($secondsToWait) is not specified by caller, From 418f343dda9025978b199f434bfde033056bd0c0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 9 Dec 2014 19:16:14 +0530 Subject: [PATCH 297/835] Whitespace fixes to appease the linter and my OCD. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index b5558d20f..56702a913 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -184,11 +184,13 @@ public function deleteWithObjects($secondsToWait = null) if (0 === $numObjects) { return $this->delete(); } + // If timeout ($secondsToWait) is not specified by caller, // try to estimate it based on number of objects in container if (null === $secondsToWait) { $secondsToWait = round($numObjects / 2); - } + } + // Attempt to delete all objects and container $endTime = time() + $secondsToWait; $containerDeleted = false; @@ -208,9 +210,11 @@ public function deleteWithObjects($secondsToWait = null) } } } + if (!$containerDeleted) { throw new ContainerException('Container could not be deleted.'); } + return $response; } From 01f91734faa9515e98d76ebbd9f65960bd57c380 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 9 Dec 2014 19:16:41 +0530 Subject: [PATCH 298/835] Fixing exception message. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 56702a913..bc7951c15 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -212,7 +212,7 @@ public function deleteWithObjects($secondsToWait = null) } if (!$containerDeleted) { - throw new ContainerException('Container could not be deleted.'); + throw new ContainerException('Container and all its objects could not be deleted.'); } return $response; From e959b018e15f6ca2ef8a1a3ca75f19ca21b26efe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 18 Dec 2014 16:11:08 +0530 Subject: [PATCH 299/835] Allow using PUT (default) or PATCH as update method depending on property. --- .../Common/Resource/PersistentResource.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index c012b8e76..9ae6582e3 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -28,6 +28,8 @@ abstract class PersistentResource extends BaseResource { + const UPDATE_METHOD = 'PUT'; + /** * Create a new resource * @@ -79,7 +81,16 @@ public function update($params = array()) $this->checkJsonError(); // send the request - return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send(); + return $this->makeUpdateRequest($json); + } + + protected function makeUpdateRequest($json) + { + if ('PATCH' === static::UPDATE_METHOD) { + return $this->getClient()->patch($this->getUrl(), self::getJsonHeader(), $json)->send(); + } else { + return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send(); + } } /** From 87d31166e56409d49198dbac7648c0970b417e44 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 19 Dec 2014 11:45:40 +0530 Subject: [PATCH 300/835] Setting memory limit needed for code coverage generation. --- phpunit.xml.dist | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index cd44e77df..be5f3c7e6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -25,4 +25,8 @@ + + + + From 601e18c0af9dd62785c25dcb3bea9e351bdc855f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 19 Dec 2014 12:43:38 +0530 Subject: [PATCH 301/835] Parameterize type of service in catalog so it can be specified when building a service object. --- lib/OpenCloud/Common/Service/ServiceBuilder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Service/ServiceBuilder.php b/lib/OpenCloud/Common/Service/ServiceBuilder.php index cf59f4a2b..99747e900 100644 --- a/lib/OpenCloud/Common/Service/ServiceBuilder.php +++ b/lib/OpenCloud/Common/Service/ServiceBuilder.php @@ -37,6 +37,7 @@ class ServiceBuilder public static function factory(ClientInterface $client, $class, array $options = array()) { $name = isset($options['name']) ? $options['name'] : null; + $type = isset($options['type']) ? $options['type'] : null; $urlType = isset($options['urlType']) ? $options['urlType'] : null; if (isset($options['region'])) { @@ -47,6 +48,6 @@ public static function factory(ClientInterface $client, $class, array $options = $region = null; } - return new $class($client, null, $name, $region, $urlType); + return new $class($client, $type, $name, $region, $urlType); } } From 733ddd654af767ff5b9ec8a38c311857727f8514 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 19 Dec 2014 12:47:43 +0530 Subject: [PATCH 302/835] Adding tests and implementation for the CDN service. --- lib/OpenCloud/CDN/Resource/Flavor.php | 47 ++++++ lib/OpenCloud/CDN/Resource/Service.php | 90 +++++++++++ lib/OpenCloud/CDN/Service.php | 145 ++++++++++++++++++ lib/OpenCloud/OpenStack.php | 18 +++ lib/OpenCloud/Rackspace.php | 19 +++ tests/OpenCloud/Tests/CDN/CDNTestCase.php | 49 ++++++ .../Tests/CDN/Resource/ServiceTest.php | 40 +++++ tests/OpenCloud/Tests/CDN/ServiceTest.php | 82 ++++++++++ tests/OpenCloud/Tests/_response/Auth.resp | 11 ++ 9 files changed, 501 insertions(+) create mode 100644 lib/OpenCloud/CDN/Resource/Flavor.php create mode 100644 lib/OpenCloud/CDN/Resource/Service.php create mode 100644 lib/OpenCloud/CDN/Service.php create mode 100644 tests/OpenCloud/Tests/CDN/CDNTestCase.php create mode 100644 tests/OpenCloud/Tests/CDN/Resource/ServiceTest.php create mode 100644 tests/OpenCloud/Tests/CDN/ServiceTest.php diff --git a/lib/OpenCloud/CDN/Resource/Flavor.php b/lib/OpenCloud/CDN/Resource/Flavor.php new file mode 100644 index 000000000..28bf8c1b8 --- /dev/null +++ b/lib/OpenCloud/CDN/Resource/Flavor.php @@ -0,0 +1,47 @@ +noUpdate(); + } +} diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php new file mode 100644 index 000000000..00348129b --- /dev/null +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -0,0 +1,90 @@ + 'flavorId' + ); + + protected $createKeys = array( + 'name', + 'domains', + 'origins', + 'caching', + 'restrictions', + 'flavorId' + ); + + protected $updateKeys = array( + 'name', + 'domains', + 'origins', + 'caching', + 'restrictions', + 'flavorId' + ); + + public function purgeAssets($assetUrl = null) + { + $assetsUrl = $this->assetsUrl(); + if (null === $assetUrl) { + $assetsUrl->setQuery(array('all' => 'true')); + } else { + $assetsUrl->setQuery(array('url' => $assetUrl)); + } + + return $this->getClient()->delete($assetsUrl)->send(); + } + + protected function assetsUrl() + { + $url = clone $this->getUrl(); + $url->addPath('assets'); + + return $url; + } + + protected function primaryKeyField() + { + return 'name'; + } +} diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php new file mode 100644 index 000000000..12f8a928c --- /dev/null +++ b/lib/OpenCloud/CDN/Service.php @@ -0,0 +1,145 @@ +resource('Service', $id); + } + + /** + * Creates a new Service and returns it. + * + * @param array $params Service creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/CDN/USERGUIDE.md#create-a-service + * @return \OpenCloud\CDN\Resource\Service Object representing created service + */ + public function createService(array $params = array()) + { + $service = $this->service(); + $service->create($params); + return $service; + } + + /** + * Returns a Service object associated with this CDN service + * + * @param string $id ID of service to retrieve + * @return \OpenCloud\CDN\Resource\Service object + */ + public function getService($id) + { + return $this->service($id); + } + + /** + * Returns a list of services you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listServices(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(ServiceResource::resourceName())->setQuery($params); + + return $this->resourceList('Service', $url); + } + + /** + * Returns a Flavor object associated with this CDN service + * + * @param string $id ID of flavor to retrieve + * @return \OpenCloud\CDN\Resource\Flavor object + */ + public function flavor($id = null) + { + return $this->resource('Flavor', $id); + } + + /** + * Creates a new Flavor and returns it. + * + * @param array $params Flavor creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/CDN/USERGUIDE.md#create-a-flavor + * @return \OpenCloud\CDN\Resource\Flavor Object representing created flavor + */ + public function createFlavor(array $params = array()) + { + $flavor = $this->flavor(); + $flavor->create($params); + return $flavor; + } + + /** + * Returns a Flavor object associated with this CDN service + * + * @param string $id ID of flavor to retrieve + * @return \OpenCloud\CDN\Resource\Flavor object + */ + public function getFlavor($id) + { + return $this->flavor($id); + } + + /** + * Returns a list of flavors you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listFlavors(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Flavor::resourceName())->setQuery($params); + + return $this->resourceList('Flavor', $url); + } + + /** + * Return namespaces. + * + * @return array + */ + public function namespaces() + { + return array(); + } +} diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 25e576959..6bc6eeba1 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -566,4 +566,22 @@ public function networkingService($name = null, $region = null, $urltype = null) 'urlType' => $urltype )); } + + /** + * Creates a new CDN (Poppy) service object + * + * @param string $name The name of the service as it appears in the Catalog + * @param string $region The region (DFW, IAD, ORD, LON, SYD) + * @param string $urltype The URL type ("publicURL" or "internalURL") + * @return \OpenCloud\Cdn\Service + * @codeCoverageIgnore + */ + public function cdnService($name = null, $region = null, $urltype = null) + { + return ServiceBuilder::factory($this, 'OpenCloud\CDN\Service', array( + 'name' => $name, + 'region' => $region, + 'urlType' => $urltype + )); + } } diff --git a/lib/OpenCloud/Rackspace.php b/lib/OpenCloud/Rackspace.php index 8480a0d71..40473a4b0 100644 --- a/lib/OpenCloud/Rackspace.php +++ b/lib/OpenCloud/Rackspace.php @@ -174,4 +174,23 @@ public function queuesService($name = null, $region = null, $urltype = null) 'urlType' => $urltype )); } + + /** + * Creates a new CDN (Rackspace CDN) service object + * + * @param string $name The name of the service as it appears in the Catalog + * @param string $region The region (DFW, IAD, ORD, LON, SYD) + * @param string $urltype The URL type ("publicURL" or "internalURL") + * @return \OpenCloud\Cdn\Service + * @codeCoverageIgnore + */ + public function cdnService($name = null, $region = null, $urltype = null) + { + return ServiceBuilder::factory($this, 'OpenCloud\CDN\Service', array( + 'name' => $name, + 'type' => 'rax:cdn', + 'region' => $region, + 'urlType' => $urltype + )); + } } diff --git a/tests/OpenCloud/Tests/CDN/CDNTestCase.php b/tests/OpenCloud/Tests/CDN/CDNTestCase.php new file mode 100644 index 000000000..4f07d58f4 --- /dev/null +++ b/tests/OpenCloud/Tests/CDN/CDNTestCase.php @@ -0,0 +1,49 @@ +service = $this->getClient()->cdnService(); + + $this->addMockSubscriber($this->makeResponse('{"name":"mywebsite.com","domains":[{"domain":"blog.mywebsite.com"}],"origins":[{"origin":"mywebsite.com","port":80,"ssl":false},{"origin":"77.66.55.44","port":80,"ssl":false,"rules":[{"name":"videos","request_url":"^/videos/*.m3u"}]}],"caching":[{"name":"default","ttl":3600},{"name":"home","ttl":17200,"rules":[{"name":"index","request_url":"/index.htm"}]},{"name":"images","ttl":12800,"rules":[{"name":"images","request_url":"*.png"}]}],"restrictions":[{"name":"website only","rules":[{"name":"mywebsite.com","http_host":"www.mywebsite.com"}]}],"flavor_id":"cdn","status":"deployed","links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/services/mywebsite.com","rel":"self"},{"href":"mywebsite.com","rel":"access_url"}]}')); + $this->serviceResource = $this->service->getService('mywebsite.com'); + } + + protected function assertIsService($object) + { + $this->assertInstanceOf('OpenCloud\CDN\Service', $object); + } + + protected function assertIsServiceResource($object) + { + $this->assertInstanceOf('OpenCloud\CDN\Resource\Service', $object); + } + + protected function assertIsFlavorResource($object) + { + $this->assertInstanceOf('OpenCloud\CDN\Resource\Flavor', $object); + } +} diff --git a/tests/OpenCloud/Tests/CDN/Resource/ServiceTest.php b/tests/OpenCloud/Tests/CDN/Resource/ServiceTest.php new file mode 100644 index 000000000..37bd61a50 --- /dev/null +++ b/tests/OpenCloud/Tests/CDN/Resource/ServiceTest.php @@ -0,0 +1,40 @@ +addMockSubscriber($this->makeResponse(null, 202)); + + $actualResponse = $this->serviceResource->purgeAssets('/images/foo.png'); + $this->assertEquals(202, $actualResponse->getStatusCode()); + } + + public function testPurgeAllAssets() + { + $this->addMockSubscriber($this->makeResponse(null, 202)); + + $actualResponse = $this->serviceResource->purgeAssets(); + $this->assertEquals(202, $actualResponse->getStatusCode()); + } +} diff --git a/tests/OpenCloud/Tests/CDN/ServiceTest.php b/tests/OpenCloud/Tests/CDN/ServiceTest.php new file mode 100644 index 000000000..e24298e00 --- /dev/null +++ b/tests/OpenCloud/Tests/CDN/ServiceTest.php @@ -0,0 +1,82 @@ +getClient()->cdnService(); + $this->assertIsService($service); + } + + public function testCreateService() + { + $this->assertIsServiceResource($this->service->createService(array( + 'name' => 'mywebsite' + ))); + } + + public function testListServices() + { + $this->addMockSubscriber($this->makeResponse('{"links":[{"rel":"next","href":"https://global.cdn.api.rackspacecloud.com/v1.0/services?marker=www.myothersite.com&limit=20"}],"services":[{"name":"mywebsite.com","domains":[{"domain":"www.mywebsite.com"}],"origins":[{"origin":"mywebsite.com","port":80,"ssl":false}],"caching":[{"name":"default","ttl":3600},{"name":"home","ttl":17200,"rules":[{"name":"index","request_url":"/index.htm"}]},{"name":"images","ttl":12800,"rules":[{"name":"images","request_url":"*.png"}]}],"restrictions":[{"name":"website only","rules":[{"name":"mywebsite.com","http_host":"www.mywebsite.com"}]}],"flavor_id":"cdn","status":"deployed","links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/services/mywebsite.com","rel":"self"},{"href":"mywebsite.com","rel":"access_url"}]},{"name":"myothersite.com","domains":[{"domain":"www.myothersite.com"}],"origins":[{"origin":"44.33.22.11","port":80,"ssl":false},{"origin":"77.66.55.44","port":80,"ssl":false,"rules":[{"name":"videos","request_url":"^/videos/*.m3u"}]}],"caching":[{"name":"default","ttl":3600}],"restrictions":[{}],"flavor_id":"cdn","status":"deployed","links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/services/myothersite.com","rel":"self"},{"href":"myothersite.com","rel":"access_url"}]}]}')); + + $services = $this->service->listServices(); + $this->isCollection($services); + $this->assertIsServiceResource($services->getElement(0)); + } + + public function testGetService() + { + $this->addMockSubscriber($this->makeResponse('{"name":"mywebsite.com","domains":[{"domain":"blog.mywebsite.com"}],"origins":[{"origin":"mywebsite.com","port":80,"ssl":false},{"origin":"77.66.55.44","port":80,"ssl":false,"rules":[{"name":"videos","request_url":"^/videos/*.m3u"}]}],"caching":[{"name":"default","ttl":3600},{"name":"home","ttl":17200,"rules":[{"name":"index","request_url":"/index.htm"}]},{"name":"images","ttl":12800,"rules":[{"name":"images","request_url":"*.png"}]}],"restrictions":[{"name":"website only","rules":[{"name":"mywebsite.com","http_host":"www.mywebsite.com"}]}],"flavor_id":"cdn","status":"deployed","links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/services/mywebsite.com","rel":"self"},{"href":"mywebsite.com","rel":"access_url"}]}')); + + $service = $this->service->getService('mywebsite.com'); + $this->assertIsServiceResource($service); + $this->assertEquals('mywebsite.com', $service->getName()); + $this->assertEquals('cdn', $service->getFlavorId()); + } + + public function testCreateFlavor() + { + $this->assertIsFlavorResource($this->service->createFlavor(array( + 'id' => 'asia' + ))); + } + + public function testListFlavors() + { + $this->addMockSubscriber($this->makeResponse('{"flavors":[{"id":"cdn","limits":{"origins":{"min":1,"max":5},"domains":{"min":1,"max":5},"caching":{"min":3600,"max":604800,"incr":300}},"providers":[{"provider":"akamai","links":[{"href":"http://www.akamai.com","rel":"provider_url"}]}],"links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/flavors/cdn","rel":"self"}]} ]}')); + + $flavors = $this->service->listFlavors(); + $this->isCollection($flavors); + $this->assertIsFlavorResource($flavors->getElement(0)); + } + + public function testGetFlavor() + { + $this->addMockSubscriber($this->makeResponse('{"id":"cdn","providers":[{"provider":"akamai","links":[{"href":"http://www.akamai.com","rel":"provider_url"}]}],"links":[{"href":"http://preview.cdn.api.rackspacecloud.com/v1.0/flavors/cdn","rel":"self"}]}')); + + $flavor = $this->service->getFlavor('cdn'); + $this->assertIsFlavorResource($flavor); + $this->assertEquals('cdn', $flavor->getId()); + $this->assertEquals('akamai', $flavor->getProviders()[0]->provider); + } +} diff --git a/tests/OpenCloud/Tests/_response/Auth.resp b/tests/OpenCloud/Tests/_response/Auth.resp index 518a634e7..add5174dc 100644 --- a/tests/OpenCloud/Tests/_response/Auth.resp +++ b/tests/OpenCloud/Tests/_response/Auth.resp @@ -364,6 +364,17 @@ Front-End-Https: on } ], "type": "network" + }, + { + "name": "rackCDN", + "endpoints": [ + { + "tenantId": "123456", + "publicURL": "https://global.cdn.api.rackspacecloud.com/v1.0/123456", + "internalURL": "https://global.cdn.api.rackspacecloud.com/v1.0/123456" + } + ], + "type": "rax:cdn" } ], "token": { From f156f4774c05d58993279532174e9f3a64cfb8fa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 19 Dec 2014 12:50:40 +0530 Subject: [PATCH 303/835] Whitespace fixes. --- lib/OpenCloud/CDN/Resource/Flavor.php | 2 +- lib/OpenCloud/CDN/Resource/Service.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/CDN/Resource/Flavor.php b/lib/OpenCloud/CDN/Resource/Flavor.php index 28bf8c1b8..d8cdbad6a 100644 --- a/lib/OpenCloud/CDN/Resource/Flavor.php +++ b/lib/OpenCloud/CDN/Resource/Flavor.php @@ -22,7 +22,7 @@ /** * A service represents your application that has its content cached to the * edge nodes. - * + * * @package OpenCloud\CDN\Resource */ class Flavor extends PersistentResource diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 00348129b..521d2921a 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -22,7 +22,7 @@ /** * A service represents your application that has its content cached to the * edge nodes. - * + * * @package OpenCloud\CDN\Resource */ class Service extends PersistentResource From bde9c6606120ac60677b7cecb09ca1bbc31e5b1a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 19 Dec 2014 13:06:07 +0530 Subject: [PATCH 304/835] Fixing docblock for Flavor class. --- lib/OpenCloud/CDN/Resource/Flavor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/CDN/Resource/Flavor.php b/lib/OpenCloud/CDN/Resource/Flavor.php index d8cdbad6a..31788bd40 100644 --- a/lib/OpenCloud/CDN/Resource/Flavor.php +++ b/lib/OpenCloud/CDN/Resource/Flavor.php @@ -20,8 +20,8 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * A service represents your application that has its content cached to the - * edge nodes. + * A flavor is a configuration for the CDN service. A flavor enables you to + * choose from a generic setting that is powered by one or more CDN providers. * * @package OpenCloud\CDN\Resource */ From 95eada81549af52060ba5e81d6e09a4a0a5b7878 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 23 Dec 2014 08:37:36 +0530 Subject: [PATCH 305/835] Looping over object properties while changing them is a bad idea! --- lib/OpenCloud/Common/Resource/PersistentResource.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 9ae6582e3..f769f9856 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -276,7 +276,8 @@ protected function recursivelyAliasPropertyValue($propertyValue) } } } elseif (is_object($propertyValue) && ($propertyValue instanceof \stdClass)) { - foreach ($propertyValue as $key => $subValue) { + $objectVars = get_object_vars($propertyValue); + foreach ($objectVars as $key => $subValue) { unset($propertyValue->$key); $propertyValue->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($subValue); } From 6d65fca97fcd96298a36833bf998f27273995b38 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 23 Dec 2014 08:38:09 +0530 Subject: [PATCH 306/835] Use the maximum pagination limit supported by the API, unless client provides a lower one. --- lib/OpenCloud/CDN/Service.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php index 12f8a928c..fafc63d6e 100644 --- a/lib/OpenCloud/CDN/Service.php +++ b/lib/OpenCloud/CDN/Service.php @@ -32,6 +32,7 @@ class Service extends CatalogService const SUPPORTED_VERSION = 'v1.0'; const DEFAULT_TYPE = 'cdn'; const DEFAULT_NAME = 'rackCDN'; + const MAX_LIMIT = 20; protected $regionless = true; @@ -78,6 +79,8 @@ public function getService($id) */ public function listServices(array $params = array()) { + $params['limit'] = isset($params['limit']) && $params['limit'] <= self::MAX_LIMIT ?: self::MAX_LIMIT; + $url = clone $this->getUrl(); $url->addPath(ServiceResource::resourceName())->setQuery($params); From f963aefddf2a5ce733aa70ba62cb59fdc293e891 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 23 Dec 2014 08:39:29 +0530 Subject: [PATCH 307/835] Do not use the top-level property in the create JSON. --- lib/OpenCloud/CDN/Resource/Flavor.php | 6 ++++++ lib/OpenCloud/CDN/Resource/Service.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/OpenCloud/CDN/Resource/Flavor.php b/lib/OpenCloud/CDN/Resource/Flavor.php index 31788bd40..140516d65 100644 --- a/lib/OpenCloud/CDN/Resource/Flavor.php +++ b/lib/OpenCloud/CDN/Resource/Flavor.php @@ -44,4 +44,10 @@ public function update($params = array()) { return $this->noUpdate(); } + + protected function createJson() + { + $createJson = parent::createJson(); + return $createJson->{self::$json_name}; + } } diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 521d2921a..98ce5a2dc 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -87,4 +87,10 @@ protected function primaryKeyField() { return 'name'; } + + protected function createJson() + { + $createJson = parent::createJson(); + return $createJson->{self::$json_name}; + } } From 020f7b3fc33cf448185b4b5af23720d5148c20e9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 23 Dec 2014 08:43:17 +0530 Subject: [PATCH 308/835] Adding smoke tests for CDN. --- tests/OpenCloud/Smoke/Runner.php | 1 + tests/OpenCloud/Smoke/Unit/CDN.php | 87 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 tests/OpenCloud/Smoke/Unit/CDN.php diff --git a/tests/OpenCloud/Smoke/Runner.php b/tests/OpenCloud/Smoke/Runner.php index d1925ba83..57dc8852a 100644 --- a/tests/OpenCloud/Smoke/Runner.php +++ b/tests/OpenCloud/Smoke/Runner.php @@ -37,6 +37,7 @@ class Runner 'Autoscale', 'Compute', 'CloudMonitoring', + 'CDN', 'DNS', 'Database', 'Identity', diff --git a/tests/OpenCloud/Smoke/Unit/CDN.php b/tests/OpenCloud/Smoke/Unit/CDN.php new file mode 100644 index 000000000..7fba1cb6b --- /dev/null +++ b/tests/OpenCloud/Smoke/Unit/CDN.php @@ -0,0 +1,87 @@ +getConnection()->cdnService(); + // TODO: Remove shim below to replace prod with preview endpoint + $service->getEndpoint()->getPublicUrl()->setHost('preview.cdn.api.rackspacecloud.com'); + return $service; + + } + + public function main() + { + $this->step('List flavors'); + $flavors = $this->getService()->listFlavors(); + $this->stepInfo('%-40s | %s', 'Flavor ID', 'Number of providers'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($flavors as $flavor) { + $this->stepInfo('%-40s | %d', $flavor->getId(), count($flavor->getProviders())); + } + + $this->step('Create service'); + $createdService = $this->getService()->createService(array( + 'name' => 'php-opencloud.com', + 'domains' => array( + array( 'domain' => 'php-opencloud.com' ), + array( 'domain' => 'www.php-opencloud.com' ) + ), + 'origins' => array( + array( 'origin' => 'origin.php-opencloud.com' ) + ), + 'flavorId' => 'cdn' + )); + $this->stepInfo('Service name: ' . $createdService->getName()); + + $this->step('List services'); + $services = $this->getService()->listServices(); + $this->stepInfo('%-40s | %s', 'Service Name', 'Number of domains'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($services as $service) { + $this->stepInfo('%-40s | %d', $service->getName(), count($service->getDomains())); + } + + $this->step('Get service'); + $service = $this->getService()->getService('php-opencloud.com'); + $this->stepInfo('Service name: ' . $service->getName()); + $this->stepInfo('Status: ' . $service->getStatus()); + $this->stepInfo('Origin: ' . $service->getOrigins()[0]->origin); + + $this->step('Update service'); + $service->update(array( + 'origins' => array( + array( 'origin' => 'updated-origin.php-opencloud.com' ) + ) + )); + + $this->step('Purge ALL cached service assets'); + $service->purgeAssets(); + + $this->step('Delete service'); + $createdService->delete(); + } + + public function teardown() + { + } +} \ No newline at end of file From 1b0eef9c91cadce71fb020b1a0e6f7047db1cc2b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 23 Dec 2014 08:43:55 +0530 Subject: [PATCH 309/835] Adding code samples. --- samples/CDN/create-flavor.php | 47 +++++++++++++++++++++ samples/CDN/create-service.php | 47 +++++++++++++++++++++ samples/CDN/delete-flavor.php | 36 ++++++++++++++++ samples/CDN/delete-service.php | 36 ++++++++++++++++ samples/CDN/get-flavor.php | 34 +++++++++++++++ samples/CDN/get-service.php | 34 +++++++++++++++ samples/CDN/list-flavors.php | 36 ++++++++++++++++ samples/CDN/list-services.php | 36 ++++++++++++++++ samples/CDN/purge-cached-service-asset.php | 36 ++++++++++++++++ samples/CDN/purge-cached-service-assets.php | 36 ++++++++++++++++ samples/CDN/update-service.php | 44 +++++++++++++++++++ 11 files changed, 422 insertions(+) create mode 100644 samples/CDN/create-flavor.php create mode 100644 samples/CDN/create-service.php create mode 100644 samples/CDN/delete-flavor.php create mode 100644 samples/CDN/delete-service.php create mode 100644 samples/CDN/get-flavor.php create mode 100644 samples/CDN/get-service.php create mode 100644 samples/CDN/list-flavors.php create mode 100644 samples/CDN/list-services.php create mode 100644 samples/CDN/purge-cached-service-asset.php create mode 100644 samples/CDN/purge-cached-service-assets.php create mode 100644 samples/CDN/update-service.php diff --git a/samples/CDN/create-flavor.php b/samples/CDN/create-flavor.php new file mode 100644 index 000000000..c9811cf85 --- /dev/null +++ b/samples/CDN/create-flavor.php @@ -0,0 +1,47 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Create flavor. +$flavor = $cdnFlavor->createFlavor(array( + 'id' => '{id}', + 'providers' => array( + array( + 'provider' => 'akamai', + 'links' => array( + array( + 'rel' => 'provider_url', + 'href' => 'http://www.akamai.com' + ) + ) + ) + ) +)); +/** @var $flavor OpenCloud\CDN\Resource\Flavor **/ diff --git a/samples/CDN/create-service.php b/samples/CDN/create-service.php new file mode 100644 index 000000000..2df1f5c5e --- /dev/null +++ b/samples/CDN/create-service.php @@ -0,0 +1,47 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Create service. +$service = $cdnService->createService(array( + 'name' => '{name}', + 'domains' => array( + array( + 'domain' => '{domain name}' + ) + ), + 'origins' => array( + array( + 'origin' => '{origin address}' + ) + ), + 'flavorId' => '{flavor ID}' +)); +/** @var $service OpenCloud\CDN\Resource\Service **/ diff --git a/samples/CDN/delete-flavor.php b/samples/CDN/delete-flavor.php new file mode 100644 index 000000000..760a0306b --- /dev/null +++ b/samples/CDN/delete-flavor.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get flavor. +$flavor = $cdnFlavor->getFlavors('{id}'); + +// 4. Delete it. +$flavor->delete(); diff --git a/samples/CDN/delete-service.php b/samples/CDN/delete-service.php new file mode 100644 index 000000000..345479084 --- /dev/null +++ b/samples/CDN/delete-service.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get service. +$service = $cdnService->getServices('{name}'); + +// 4. Delete it. +$service->delete(); diff --git a/samples/CDN/get-flavor.php b/samples/CDN/get-flavor.php new file mode 100644 index 000000000..e63ffcc29 --- /dev/null +++ b/samples/CDN/get-flavor.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get flavor. +$flavor = $cdnService->getFlavor('{flavor ID}'); +/** @var $flavor OpenCloud\CDN\Resource\Flavor **/ diff --git a/samples/CDN/get-service.php b/samples/CDN/get-service.php new file mode 100644 index 000000000..7a4a72dae --- /dev/null +++ b/samples/CDN/get-service.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get service. +$service = $cdnService->getService('{name}'); +/** @var $service OpenCloud\CDN\Resource\Service **/ diff --git a/samples/CDN/list-flavors.php b/samples/CDN/list-flavors.php new file mode 100644 index 000000000..8ddf9c4ce --- /dev/null +++ b/samples/CDN/list-flavors.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get flavor list. +$flavors = $cdnService->listFlavors(); +foreach ($flavors as $flavor) { + /** @var $flavor OpenCloud\CDN\Resource\Flavor **/ +} diff --git a/samples/CDN/list-services.php b/samples/CDN/list-services.php new file mode 100644 index 000000000..67f5d63a4 --- /dev/null +++ b/samples/CDN/list-services.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get service list. +$services = $cdnService->listServices(); +foreach ($services as $service) { + /** @var $service OpenCloud\CDN\Resource\Service **/ +} diff --git a/samples/CDN/purge-cached-service-asset.php b/samples/CDN/purge-cached-service-asset.php new file mode 100644 index 000000000..6d5e83c5c --- /dev/null +++ b/samples/CDN/purge-cached-service-asset.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get service. +$service = $cdnService->getServices('{name}'); + +// 4. Purge a specific asset belonging to service. +$service->purgeAsset('{asset URL}'); diff --git a/samples/CDN/purge-cached-service-assets.php b/samples/CDN/purge-cached-service-assets.php new file mode 100644 index 000000000..0d032c89c --- /dev/null +++ b/samples/CDN/purge-cached-service-assets.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get service. +$service = $cdnService->getServices('{name}'); + +// 4. Purge all assets belonging to service. +$service->purgeAssets(); diff --git a/samples/CDN/update-service.php b/samples/CDN/update-service.php new file mode 100644 index 000000000..13b34b7bf --- /dev/null +++ b/samples/CDN/update-service.php @@ -0,0 +1,44 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an CDN service object from the client. +$cdnService = $client->cdnService(); + +// 3. Get service. +$service = $cdnService->getService('{name}'); + +// 4. Update it. +$service->update(array( + 'origins' => array( + array( + 'origin' => '44.33.22.11', + 'port' => 80, + 'ssl' => false + ) + ) +)); From 07d62a0fd6bf990e5db578ed6db07821fafa19fb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 08:46:03 +0530 Subject: [PATCH 310/835] Adding Getting Started Guide for CDN service. --- docs/userguide/CDN/README.md | 95 ++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 docs/userguide/CDN/README.md diff --git a/docs/userguide/CDN/README.md b/docs/userguide/CDN/README.md new file mode 100644 index 000000000..76ba95735 --- /dev/null +++ b/docs/userguide/CDN/README.md @@ -0,0 +1,95 @@ +# CDN + +**CDN** is a service that you can use to manage your CDN-enabled domains and the origins and assets associated with those domains. + +## Concepts + +To use the CDN service effectively, you should understand the following key concepts: + +* **Content delivery network**: A content delivery network (CDN) is a system of multiple computers that contains copies of data stored at various network nodes. A CDN is designed to improve performance of publicly distributed assets. Assets can be anything from website content, to web application components, to media such as videos, ads, and interactive experiences.  CDNs decrease the load time of these assets by caching them on edge servers, also called points of presence (PoPs).  Edge servers are distributed around the globe, meaning requests only travel to a local location to grab assets, rather than to and from a data center based far from the end user. + +* **Edge node**: CDN providers have many points of presence (PoP) servers around the world. These servers are known as edge nodes. These edge nodes cache the content and serve it directly to customers, thus reducing transit time to a customers location. + +* **Edge server**: An edge server is the same as an edge node. + +* **Origin**: An origin is an address (IP or domain) from which the CDN provider pulls content. A service can have multiple origins. + +* **Flavor**: A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers. + +* **Service**: A service represents your application that has its content cached to the edge nodes. + +* **Status**: The status indicates the current state of the service. The time it takes for a service configuration to be distributed amongst a CDN provider cache can vary. + +* **Purge**: Purging removes content from the edge servers - thus invalidating the content - so that it can be refreshed from your origin servers. + +* **Caching rule**: A caching rule provides you with fine-grained control over the time-to-live (TTL) of an object. When the TTL expires for an object, the edge node pulls the object from the origin again. + +* **Restriction**: A restriction enables you to define rules about who can or cannot access content from the cache. Examples of a restriction are allowing requests only from certain domains, geographies, or IP addresses. + +## Getting started + +### 1. Instantiate an OpenStack or Rackspace client. + +To use the CDN service, you must first instantiate a `OpenStack` or `Rackspace` client object. + +* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: + + ```php + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + ``` + +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: + + ```php + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +### 2. Obtain a CDN service object from the client. +All CDN operations are done via an _CDN service object_. To +instantiate this object, call the `cdnService` method on the `$client` +object. This method takes one argument: + +| Position | Description | Data type | Required? | Default value | Example value | +| -------- | ----------- | ----------| --------- | ------------- | ------------- | +| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `rackCDN` | + + +```php +$cdnService = $client->cdnService(); +``` + +### 3. Create a service. +```php +$service = $cdnService->createService(array( + 'name' => 'acme_site', + 'domains' => array( + array( + 'domain' => 'www.acme.com' + ), + array( + 'domain' => 'acme.com' + ) + ), + 'origins' => array( + array( + 'origin' => 'origin.acme.com' + ) + ), + 'flavorId' => 'cdn' +));``` + +[ [Get the executable PHP script for this example](/samples/CDN/create-cdn.php) ] + +## Next steps + +Once you have created a service, there is more you can do with it. See [complete user guide for CDN](USERGUIDE.md). From 8f54c4eafd302b678873b28b67914adf25f9b7c5 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 09:37:11 +0530 Subject: [PATCH 311/835] Adding another property alias. --- lib/OpenCloud/CDN/Resource/Service.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 98ce5a2dc..444266f12 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -42,7 +42,8 @@ class Service extends PersistentResource protected $links; protected $aliases = array( - 'flavor_id' => 'flavorId' + 'flavor_id' => 'flavorId', + 'http_host' => 'httpHost' ); protected $createKeys = array( From 260f68f3b1a89f79591b09ff81a4fe7747897518 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 09:38:21 +0530 Subject: [PATCH 312/835] Clarifying definition. --- docs/userguide/CDN/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/README.md b/docs/userguide/CDN/README.md index 76ba95735..6a7fc864f 100644 --- a/docs/userguide/CDN/README.md +++ b/docs/userguide/CDN/README.md @@ -16,7 +16,7 @@ To use the CDN service effectively, you should understand the following key conc * **Flavor**: A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers. -* **Service**: A service represents your application that has its content cached to the edge nodes. +* **Service**: A service represents your web application that has its content cached to the edge nodes. * **Status**: The status indicates the current state of the service. The time it takes for a service configuration to be distributed amongst a CDN provider cache can vary. From 038e2da00a463a158bac036bc893fb248f65c7ce Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 09:38:42 +0530 Subject: [PATCH 313/835] Disambiguating "service" by providing additional context. --- docs/userguide/CDN/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/README.md b/docs/userguide/CDN/README.md index 6a7fc864f..04ce14bc7 100644 --- a/docs/userguide/CDN/README.md +++ b/docs/userguide/CDN/README.md @@ -68,7 +68,7 @@ object. This method takes one argument: $cdnService = $client->cdnService(); ``` -### 3. Create a service. +### 3. Create a service (to represent your web application). ```php $service = $cdnService->createService(array( 'name' => 'acme_site', From f525941e65d71f71528aa98445ca6f22e02d57e9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 09:39:17 +0530 Subject: [PATCH 314/835] Fixing link. --- docs/userguide/CDN/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/README.md b/docs/userguide/CDN/README.md index 04ce14bc7..ba959ce11 100644 --- a/docs/userguide/CDN/README.md +++ b/docs/userguide/CDN/README.md @@ -88,7 +88,7 @@ $service = $cdnService->createService(array( 'flavorId' => 'cdn' ));``` -[ [Get the executable PHP script for this example](/samples/CDN/create-cdn.php) ] +[ [Get the executable PHP script for this example](/samples/CDN/create-service.php) ] ## Next steps From 157ea27bf0b3f3f95ffee62caa1d1e9479476d91 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 09:39:38 +0530 Subject: [PATCH 315/835] Adding Complete User Guide for CDN service. --- docs/userguide/CDN/USERGUIDE.md | 264 ++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 docs/userguide/CDN/USERGUIDE.md diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md new file mode 100644 index 000000000..8c0abb7d4 --- /dev/null +++ b/docs/userguide/CDN/USERGUIDE.md @@ -0,0 +1,264 @@ +# Complete User Guide for the CDN Service + +CDN is a service that you can use to manage your CDN-enabled domains and the origins and assets associated with those domains. + +## Table of contents + * [Concepts](#concepts) + * [Prerequisites](#prerequisites) + * [Client](#client) + * [CDN service](#cdn-service) + * [Services](#services) + * [Create a service](#create-a-service-to-represent-your-web-application) + * [List Services](#list-Services) + * [Get a service](#get-a-service) + * [Purge cached service assets](#purge-cached-service-assets) + * [Update a service](#update-a-service) + * [Delete a service](#delete-a-service) + * [Flavors](#flavors) + * [Create a flavor](#create-a-flavor) + * [List flavors](#list-flavors) + * [Get a flavor](#get-a-flavor) + * [Delete a flavor](#delete-a-flavor) + +## Concepts + +To use the CDN service effectively, you should understand the following +key concepts: + +* **Content delivery service**: A content delivery service (CDN) is a system of multiple computers that contains copies of data stored at various service nodes. A CDN is designed to improve performance of publicly distributed assets. Assets can be anything from website content, to web application components, to media such as videos, ads, and interactive experiences.  CDNs decrease the load time of these assets by caching them on edge servers, also called points of presence (PoPs).  Edge servers are distributed around the globe, meaning requests only travel to a local location to grab assets, rather than to and from a data center based far from the end user. + +* **Edge node**: CDN providers have many points of presence (PoP) servers around the world. These servers are known as edge nodes. These edge nodes cache the content and serve it directly to customers, thus reducing transit time to a customers location. + +* **Edge server**: An edge server is the same as an edge node. + +* **Origin**: An origin is an address (IP or domain) from which the CDN provider pulls content. A service can have multiple origins. + +* **Flavor**: A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers. + +* **Service**: A service represents your web application that has its content cached to the edge nodes. + +* **Status**: The status indicates the current state of the service. The time it takes for a service configuration to be distributed amongst a CDN provider cache can vary. + +* **Purge**: Purging removes content from the edge servers - thus invalidating the content - so that it can be refreshed from your origin servers. + +* **Caching rule**: A caching rule provides you with fine-grained control over the time-to-live (TTL) of an object. When the TTL expires for an object, the edge node pulls the object from the origin again. + +* **Restriction**: A restriction enables you to define rules about who can or cannot access content from the cache. Examples of a restriction are allowing requests only from certain domains, geographies, or IP addresses. + + +## Prerequisites + +### Client +To use the CDN service, you must first instantiate a `OpenStack` or `Rackspace` client object. + +* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: + + ```php + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + ``` + +* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: + + ```php + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + ``` + +### CDN service + +All CDN operations are done via an _CDN service object_. To +instantiate this object, call the `cdnService` method on the `$client` +object. This method takes one argument: + +| Position | Description | Data type | Required? | Default value | Example value | +| -------- | ----------- | ----------| --------- | ------------- | ------------- | +| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `rackCDN` | + + +```php +$cdnService = $client->cdnService(); +``` + +## Services + +A service represents your web application that has its content cached to the edge nodes. + +### Create a service (to represent your web application) + + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | A human-readable name for the service. This name must be unique. | String | No | `null` | `acme_site` | +| `domains` | An array of associative arrays, each specifying a domain name for your service. | Array of associative arrays | Yes | `null` | `array( array( 'domain' => 'www.acme.com' ), array ( 'domain' => 'acme.com' ) )` | +| `origins` | An array of associative arrays, each specifying an origin server for your service. The `port`, `ssl`, and `rules` elements for each origin server are optional. | Array of associative arrays | Yes | `null` | `array( array( 'origin' => 'origin.acme.com', 'port' => 80, 'ssl' => false, 'rules' => array() ) )` | +| `flavorId` | The flavor used to configure this service. Use the [list flavors](#list-flavors) operation to retrieve a list of all available flavors. | String | Yes| `null` | `cdn` | +| `restrictions` | An array of associative arrays, each specifying a restriction for who can or cannot access content from the CDN cache. | Array of associative arrays | No | `null` | `array( array( 'name' => 'website only', 'rules' => array( array( 'name' => 'mywebsite.com', 'httpHost' => 'www.mywebsite.com' ) ) ) )` | +| `caching` | An array of associative arrays, each specifying a caching rule for your service's assets. | No | `null` | `array( array( 'name' => 'default', 'ttl' => 3600 ) )` | + +You can create a service as shown in the following example: + +```php +$service = $cdnService->createService(array( + 'name' => 'acme_site', + 'domains' => array( + array( + 'domain' => 'www.acme.com' + ), + array( + 'domain' => 'acme.com' + ) + ), + 'origins' => array( + array( + 'origin' => 'origin.acme.com' + ) + ), + 'flavorId' => 'cdn' +));``` + +[ [Get the executable PHP script for this example](/samples/CDN/create-service.php) ] + +### List Services + +You can list all the services you have created as shown in the following example: + +```php +$services = $cdnService->listServices(); +foreach ($services as $service) { + /** @var $service OpenCloud\CDN\Resource\Service **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/CDN/list-services.php) ] + +### Get a service + +You can retrieve a specific service by using that service's name, as shown in the following example: + +```php +$service = $cdnService->getservice('acme_site'); +/** @var $service OpenCloud\CDN\Resource\Service **/ +``` + +[ [Get the executable PHP script for this example](/samples/CDN/get-service.php) ] + +### Update a service + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `domains` | An array of associative arrays, each specifying a domain name for your service. | Array of associative arrays | Yes | `null` | `array( array( 'domain' => 'www.acme.com' ), array ( 'domain' => 'acme.com' ) )` | +| `origins` | An array of associative arrays, each specifying an origin server for your service. The `port`, `ssl`, and `rules` elements for each origin server are optional. | Array of associative arrays | Yes | `null` | `array( array( 'origin' => 'origin.acme.com', 'port' => 80, 'ssl' => false, 'rules' => array() ) )` | +| `flavorId` | The flavor used to configure this service. Use the [list flavors](#list-flavors) operation to retrieve a list of all available flavors. | String | Yes| `null` | `cdn` | +| `restrictions` | An array of associative arrays, each specifying a restriction for who can or cannot access content from the CDN cache. | Array of associative arrays | No | `null` | `array( array( 'name' => 'website only', 'rules' => array( array( 'name' => 'mywebsite.com', 'httpHost' => 'www.mywebsite.com' ) ) ) )` | +| `caching` | An array of associative arrays, each specifying a caching rule for your service's assets. | No | `null` | `array( array( 'name' => 'default', 'ttl' => 3600 ) )` | + +You can update a service as shown in the following example: + +```php +$service->update(array( + 'origins' => array( + array( + 'origin' => '44.33.22.11', + 'port' => 80, + 'ssl' => false + ) + ) +)); +``` + +[ [Get the executable PHP script for this example](/samples/CDN/update-service.php) ] + +### Delete a service + +You can delete a service as shown in the following example: + +```php +$service->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/CDN/delete-service.php) ] + +## Flavors + +A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers. + +### Create a flavor + +Note: When working with the Rackspace Cloud, this operation requires the `cdn:operator` role. + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `id` | ID of flavor. This ID must be unique. | String | Yes | `null` | `cdn` | +| `providers` | An array of associative arrays, each representing a CDN provider. | Array of associative arrays | Yes | `null` | `array( array( 'provider' => 'akamai', 'links' => array( array( 'rel' => 'provider_url', 'href' => 'http://www.akamai.com' ) ) ) )` | + +You can create a flavor as shown in the following example: + +```php +$Flavor = $CDNService->createFlavor(array( + 'id' => 'cdn', + 'providers' => array( + array( + 'name' => 'akamai', + 'links' => array( + 'rel' => 'provider_url', + 'href' => 'http://www.akamai.com' + ) + ) + ) +)); +/** @var $Flavor OpenCloud\CDN\Resource\Flavor **/ +``` + +[ [Get the executable PHP script for this example](/samples/CDN/create-flavor.php) ] + +### List flavors + +You can list all available flavors as shown in the following example: + +```php +$flavors = $cdnService->listFlavors(); +foreach ($flavors as $flavor) { + /** @var $flavor OpenCloud\CDN\Resource\Flavor **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/CDN/list-flavors.php) ] + +### Get a flavor + +You can retrieve a specific flavor by using that flavor's ID, as shown in the +following example: + +```php +$flavor = $cdnService->getFlavor('cdn'); +/** @var $flavor OpenCloud\CDN\Resource\Flavor **/ +``` + +[ [Get the executable PHP script for this example](/samples/CDN/get-flavor.php) ] + +### Delete a flavor + +Note: When working with the Rackspace Cloud, this operation requires the `cdn:operator` role. + +You can delete a flavor as shown in the following example: + +```php +$flavor->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/CDN/delete-flavor.php) ] From ad715657092677debe1b6d22af15c1f736baf18c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 11:54:27 +0530 Subject: [PATCH 316/835] Whitespace lint fixes. --- tests/OpenCloud/Smoke/Unit/CDN.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/CDN.php b/tests/OpenCloud/Smoke/Unit/CDN.php index 7fba1cb6b..e996ab3d4 100644 --- a/tests/OpenCloud/Smoke/Unit/CDN.php +++ b/tests/OpenCloud/Smoke/Unit/CDN.php @@ -16,6 +16,7 @@ */ namespace OpenCloud\Smoke\Unit; + use Guzzle\Http\Exception\ClientErrorResponseException; class CDN extends AbstractUnit implements UnitInterface @@ -26,7 +27,6 @@ public function setupService() // TODO: Remove shim below to replace prod with preview endpoint $service->getEndpoint()->getPublicUrl()->setHost('preview.cdn.api.rackspacecloud.com'); return $service; - } public function main() @@ -84,4 +84,4 @@ public function main() public function teardown() { } -} \ No newline at end of file +} From 1c59c458dd7a4ac50e01c7718ead9ab913dcd273 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 25 Dec 2014 12:10:34 +0530 Subject: [PATCH 317/835] Removing limits. --- lib/OpenCloud/CDN/Resource/Flavor.php | 2 -- tests/OpenCloud/Tests/CDN/ServiceTest.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/OpenCloud/CDN/Resource/Flavor.php b/lib/OpenCloud/CDN/Resource/Flavor.php index 140516d65..de13eb9f2 100644 --- a/lib/OpenCloud/CDN/Resource/Flavor.php +++ b/lib/OpenCloud/CDN/Resource/Flavor.php @@ -31,12 +31,10 @@ class Flavor extends PersistentResource protected static $json_name = 'flavor'; protected $id; - protected $limits; protected $providers; protected $createKeys = array( 'id', - 'limits', 'providers' ); diff --git a/tests/OpenCloud/Tests/CDN/ServiceTest.php b/tests/OpenCloud/Tests/CDN/ServiceTest.php index e24298e00..dba96d576 100644 --- a/tests/OpenCloud/Tests/CDN/ServiceTest.php +++ b/tests/OpenCloud/Tests/CDN/ServiceTest.php @@ -63,7 +63,7 @@ public function testCreateFlavor() public function testListFlavors() { - $this->addMockSubscriber($this->makeResponse('{"flavors":[{"id":"cdn","limits":{"origins":{"min":1,"max":5},"domains":{"min":1,"max":5},"caching":{"min":3600,"max":604800,"incr":300}},"providers":[{"provider":"akamai","links":[{"href":"http://www.akamai.com","rel":"provider_url"}]}],"links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/flavors/cdn","rel":"self"}]} ]}')); + $this->addMockSubscriber($this->makeResponse('{"flavors":[{"id":"cdn","providers":[{"provider":"akamai","links":[{"href":"http://www.akamai.com","rel":"provider_url"}]}],"links":[{"href":"https://global.cdn.api.rackspacecloud.com/v1.0/flavors/cdn","rel":"self"}]} ]}')); $flavors = $this->service->listFlavors(); $this->isCollection($flavors); From ffb915dcd84ad5381e654f127106e73e33eef4aa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:06:05 -0800 Subject: [PATCH 318/835] Fixing formatting. --- docs/userguide/CDN/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/userguide/CDN/README.md b/docs/userguide/CDN/README.md index ba959ce11..a64cca833 100644 --- a/docs/userguide/CDN/README.md +++ b/docs/userguide/CDN/README.md @@ -86,7 +86,8 @@ $service = $cdnService->createService(array( ) ), 'flavorId' => 'cdn' -));``` +)); +``` [ [Get the executable PHP script for this example](/samples/CDN/create-service.php) ] From 92cbf9895b12fa7adb5f2d097123803ce0b8b9e4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:08:06 -0800 Subject: [PATCH 319/835] Fixing typo. --- docs/userguide/CDN/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 8c0abb7d4..9468f2404 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -25,7 +25,7 @@ CDN is a service that you can use to manage your CDN-enabled domains and the ori To use the CDN service effectively, you should understand the following key concepts: -* **Content delivery service**: A content delivery service (CDN) is a system of multiple computers that contains copies of data stored at various service nodes. A CDN is designed to improve performance of publicly distributed assets. Assets can be anything from website content, to web application components, to media such as videos, ads, and interactive experiences.  CDNs decrease the load time of these assets by caching them on edge servers, also called points of presence (PoPs).  Edge servers are distributed around the globe, meaning requests only travel to a local location to grab assets, rather than to and from a data center based far from the end user. +* **Content delivery network**: A content delivery network (CDN) is a system of multiple computers that contains copies of data stored at various network nodes. A CDN is designed to improve performance of publicly distributed assets. Assets can be anything from website content, to web application components, to media such as videos, ads, and interactive experiences.  CDNs decrease the load time of these assets by caching them on edge servers, also called points of presence (PoPs).  Edge servers are distributed around the globe, meaning requests only travel to a local location to grab assets, rather than to and from a data center based far from the end user. * **Edge node**: CDN providers have many points of presence (PoP) servers around the world. These servers are known as edge nodes. These edge nodes cache the content and serve it directly to customers, thus reducing transit time to a customers location. From ee471356b0af824fc772a1dcb92b4cbce528b12f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:37:01 -0800 Subject: [PATCH 320/835] Changing syntax to be PHP5.3-compliant. --- tests/OpenCloud/Smoke/Unit/CDN.php | 3 ++- tests/OpenCloud/Tests/CDN/ServiceTest.php | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/CDN.php b/tests/OpenCloud/Smoke/Unit/CDN.php index e996ab3d4..97921d11f 100644 --- a/tests/OpenCloud/Smoke/Unit/CDN.php +++ b/tests/OpenCloud/Smoke/Unit/CDN.php @@ -65,7 +65,8 @@ public function main() $service = $this->getService()->getService('php-opencloud.com'); $this->stepInfo('Service name: ' . $service->getName()); $this->stepInfo('Status: ' . $service->getStatus()); - $this->stepInfo('Origin: ' . $service->getOrigins()[0]->origin); + $origins = $service->getOrigins(); + $this->stepInfo('Origin: ' . $origins[0]->origin); $this->step('Update service'); $service->update(array( diff --git a/tests/OpenCloud/Tests/CDN/ServiceTest.php b/tests/OpenCloud/Tests/CDN/ServiceTest.php index dba96d576..e37be35b9 100644 --- a/tests/OpenCloud/Tests/CDN/ServiceTest.php +++ b/tests/OpenCloud/Tests/CDN/ServiceTest.php @@ -77,6 +77,8 @@ public function testGetFlavor() $flavor = $this->service->getFlavor('cdn'); $this->assertIsFlavorResource($flavor); $this->assertEquals('cdn', $flavor->getId()); - $this->assertEquals('akamai', $flavor->getProviders()[0]->provider); + + $providers = $flavor->getProviders(); + $this->assertEquals('akamai', $providers[0]->provider); } } From 5804006358b13cdf4352d37f9a0dfdc94c040c54 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:45:47 -0800 Subject: [PATCH 321/835] Fixing link. --- docs/userguide/CDN/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 9468f2404..a2cb7fceb 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -9,7 +9,7 @@ CDN is a service that you can use to manage your CDN-enabled domains and the ori * [CDN service](#cdn-service) * [Services](#services) * [Create a service](#create-a-service-to-represent-your-web-application) - * [List Services](#list-Services) + * [List Services](#list-services) * [Get a service](#get-a-service) * [Purge cached service assets](#purge-cached-service-assets) * [Update a service](#update-a-service) From 72182f3ddb6c567a9e15cc99b4e7a6011fdaaada Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:55:07 -0800 Subject: [PATCH 322/835] Adding section on service assets. --- docs/userguide/CDN/USERGUIDE.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index a2cb7fceb..eaff6aea6 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -11,9 +11,11 @@ CDN is a service that you can use to manage your CDN-enabled domains and the ori * [Create a service](#create-a-service-to-represent-your-web-application) * [List Services](#list-services) * [Get a service](#get-a-service) - * [Purge cached service assets](#purge-cached-service-assets) * [Update a service](#update-a-service) * [Delete a service](#delete-a-service) + * [Service Assets](#service-assets) + * [Purge all cached service assets](#purge-all-cached-service-assets) + * [Purge a specific cached service asset](#purge-a-specific-cached-service-asset) * [Flavors](#flavors) * [Create a flavor](#create-a-flavor) * [List flavors](#list-flavors) @@ -191,6 +193,29 @@ $service->delete(); [ [Get the executable PHP script for this example](/samples/CDN/delete-service.php) ] +## Service Assets +A service will have its assets distributed and cached across a CDN's edge nodes. + +### Purge all cached service assets + +You can purge all cached assets of a service as shown in the following example: + +```php +$service->purgeAssets(); +``` + +[ [Get the executable PHP script for this example](/samples/CDN/purge-cached-service-assets.php) ] + +### Purge a specific cached service asset + +You can purge a specific asset of a service by providing its relative URL, as shown in the following example: + +```php +$service->purgeAssets('/images/logo.png'); +``` + +[ [Get the executable PHP script for this example](/samples/CDN/purge-cached-service-asset.php) ] + ## Flavors A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers. From 8b179f62c5f108d71894a6c236d9b140f6b6a3f1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:55:21 -0800 Subject: [PATCH 323/835] Fixing typo in variable name. --- docs/userguide/CDN/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index eaff6aea6..4760e6da0 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -234,7 +234,7 @@ This operation takes one parameter, an associative array, with the following key You can create a flavor as shown in the following example: ```php -$Flavor = $CDNService->createFlavor(array( +$flavor = $CDNService->createFlavor(array( 'id' => 'cdn', 'providers' => array( array( From 5f4976e89ab6d790bde31198d7e6e1c8036d32f5 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 03:59:04 -0800 Subject: [PATCH 324/835] Fixing case in variable names. --- docs/userguide/CDN/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 4760e6da0..de26dac3a 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -234,7 +234,7 @@ This operation takes one parameter, an associative array, with the following key You can create a flavor as shown in the following example: ```php -$flavor = $CDNService->createFlavor(array( +$flavor = $cdnService->createFlavor(array( 'id' => 'cdn', 'providers' => array( array( @@ -246,7 +246,7 @@ $flavor = $CDNService->createFlavor(array( ) ) )); -/** @var $Flavor OpenCloud\CDN\Resource\Flavor **/ +/** @var $flavor OpenCloud\CDN\Resource\Flavor **/ ``` [ [Get the executable PHP script for this example](/samples/CDN/create-flavor.php) ] From f45dc272c6997947bda9bb75884e2517a6df5b84 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 26 Dec 2014 04:00:01 -0800 Subject: [PATCH 325/835] Fixing method name. --- samples/CDN/purge-cached-service-asset.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/CDN/purge-cached-service-asset.php b/samples/CDN/purge-cached-service-asset.php index 6d5e83c5c..715a80699 100644 --- a/samples/CDN/purge-cached-service-asset.php +++ b/samples/CDN/purge-cached-service-asset.php @@ -33,4 +33,4 @@ $service = $cdnService->getServices('{name}'); // 4. Purge a specific asset belonging to service. -$service->purgeAsset('{asset URL}'); +$service->purgeAssets('{asset URL}'); From c9f0bf6c80bc9ce1b8b7f45f4c264fa737107c85 Mon Sep 17 00:00:00 2001 From: Kyle Kelley Date: Fri, 26 Dec 2014 10:41:46 -0700 Subject: [PATCH 326/835] Disable Travis sudo for container based builds * http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/ --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d44595c93..7d45afa70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,8 @@ php: - "5.3" - hhvm +sudo: false + matrix: allow_failures: - php: hhvm From 36336cbc9662c05b2a7060b100ae1e14a99d30b2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 04:45:48 -0800 Subject: [PATCH 327/835] Adding support for GET / and GET /ping APIs. --- lib/OpenCloud/CDN/Service.php | 24 +++++++++++++++++++++++ tests/OpenCloud/Tests/CDN/ServiceTest.php | 17 ++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php index fafc63d6e..20143cc00 100644 --- a/lib/OpenCloud/CDN/Service.php +++ b/lib/OpenCloud/CDN/Service.php @@ -136,6 +136,30 @@ public function listFlavors(array $params = array()) return $this->resourceList('Flavor', $url); } + /** + * Returns the home document for the CDN service + * + * @return \stdClass home document response + */ + public function getHomeDocument() + { + $response = $this->getClient()->get($this->getUrl())->send(); + return Formatter::decode($response); + } + + /** + * Returns the ping (status) response for the CDN service + * + * @return Guzzle\Http\Message\Response + */ + public function getPing() + { + $url = clone $this->getUrl(); + $url->addPath('ping'); + + return $this->getClient()->get($url)->send(); + } + /** * Return namespaces. * diff --git a/tests/OpenCloud/Tests/CDN/ServiceTest.php b/tests/OpenCloud/Tests/CDN/ServiceTest.php index e37be35b9..b3a2bb7f7 100644 --- a/tests/OpenCloud/Tests/CDN/ServiceTest.php +++ b/tests/OpenCloud/Tests/CDN/ServiceTest.php @@ -81,4 +81,21 @@ public function testGetFlavor() $providers = $flavor->getProviders(); $this->assertEquals('akamai', $providers[0]->provider); } + + public function testGetHomeDocument() + { + $this->addMockSubscriber($this->makeResponse('{"resources":{"rel/cdn":{"href-template":"services{?marker,limit}","href-vars":{"marker":"param/marker","limit":"param/limit"},"hints":{"allow":["GET"],"formats":{"application/json":{}}}}}}')); + + $homeDocument = $this->service->getHomeDocument(); + $this->assertNotEmpty($homeDocument); + $this->assertEquals("services{?marker,limit}", $homeDocument->resources->{"rel/cdn"}->{"href-template"}); + } + + public function testGetPing() + { + $this->addMockSubscriber($this->makeResponse(null, 204)); + + $ping = $this->service->getPing(); + $this->assertEquals(204, $ping->getStatusCode()); + } } From f2fe5ab4b32cb45c3ca62ac673f1db2809bc0f64 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 05:38:25 -0800 Subject: [PATCH 328/835] Adding errors property to Service resource. --- lib/OpenCloud/CDN/Resource/Service.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 444266f12..beef278cd 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -40,6 +40,7 @@ class Service extends PersistentResource protected $flavorId; protected $status; protected $links; + protected $errors; protected $aliases = array( 'flavor_id' => 'flavorId', From 138e0962365cacf4ef40d75969f96c12dc9c986b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 05:39:19 -0800 Subject: [PATCH 329/835] Remove Accept request header for APIs that don't return responses. --- lib/OpenCloud/CDN/Resource/Service.php | 9 ++++++++- lib/OpenCloud/CDN/Service.php | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index beef278cd..2823286d1 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -74,7 +74,14 @@ public function purgeAssets($assetUrl = null) $assetsUrl->setQuery(array('url' => $assetUrl)); } - return $this->getClient()->delete($assetsUrl)->send(); + $request = $this->getClient()->delete($assetsUrl); + + // This is necessary because the response does not include a body + // and fails with a 406 Not Acceptable if the default + // 'Accept: application/json' header is used in the request. + $request->removeHeader('Accept'); + + return $request->send(); } protected function assetsUrl() diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php index 20143cc00..2fbef781c 100644 --- a/lib/OpenCloud/CDN/Service.php +++ b/lib/OpenCloud/CDN/Service.php @@ -156,8 +156,15 @@ public function getPing() { $url = clone $this->getUrl(); $url->addPath('ping'); - - return $this->getClient()->get($url)->send(); + + $request = $this->getClient()->get($url); + + // This is necessary because the response does not include a body + // and fails with a 406 Not Acceptable if the default + // 'Accept: application/json' header is used in the request. + $request->removeHeader('Accept'); + + return $request->send(); } /** From cae80de1d2a0dec9e2523a100f5e916eb45bf451 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 05:40:25 -0800 Subject: [PATCH 330/835] Force addition of trailing slash to URL in order to make GET / work. --- lib/OpenCloud/CDN/Service.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php index 2fbef781c..14f2281a7 100644 --- a/lib/OpenCloud/CDN/Service.php +++ b/lib/OpenCloud/CDN/Service.php @@ -143,8 +143,15 @@ public function listFlavors(array $params = array()) */ public function getHomeDocument() { - $response = $this->getClient()->get($this->getUrl())->send(); - return Formatter::decode($response); + $url = clone $this->getUrl(); + + // This hack is necessary otherwise Guzzle will remove the trailing + // slash from the URL and the request will fail because the service + // expects the trailing slash :( + $url->setPath($url->getPath() . '/'); + + $response = $this->getClient()->get($url)->send(); + return Formatter::decode($response); } /** From 97f8037cb0d8e971033275ca2a8f99e1014edf41 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 05:41:14 -0800 Subject: [PATCH 331/835] Adding smoke tests for GET / and GET /ping APIs. --- tests/OpenCloud/Smoke/Unit/CDN.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/CDN.php b/tests/OpenCloud/Smoke/Unit/CDN.php index 97921d11f..59366bb93 100644 --- a/tests/OpenCloud/Smoke/Unit/CDN.php +++ b/tests/OpenCloud/Smoke/Unit/CDN.php @@ -24,13 +24,19 @@ class CDN extends AbstractUnit implements UnitInterface public function setupService() { $service = $this->getConnection()->cdnService(); - // TODO: Remove shim below to replace prod with preview endpoint - $service->getEndpoint()->getPublicUrl()->setHost('preview.cdn.api.rackspacecloud.com'); return $service; } public function main() { + $this->step('Get home document'); + $homeDocument = $this->getService()->getHomeDocument(); + $this->stepInfo('Home document: %s', json_encode($homeDocument)); + + $this->step('Get ping'); + $ping = $this->getService()->getPing(); + $this->stepInfo('Ping successful'); + $this->step('List flavors'); $flavors = $this->getService()->listFlavors(); $this->stepInfo('%-40s | %s', 'Flavor ID', 'Number of providers'); From 9686882b3418fbad36e53412bf5140ffb32ca7d6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 05:41:53 -0800 Subject: [PATCH 332/835] Wait for service to return to a status where further changes can be made. --- tests/OpenCloud/Smoke/Unit/CDN.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/OpenCloud/Smoke/Unit/CDN.php b/tests/OpenCloud/Smoke/Unit/CDN.php index 59366bb93..c6ec30209 100644 --- a/tests/OpenCloud/Smoke/Unit/CDN.php +++ b/tests/OpenCloud/Smoke/Unit/CDN.php @@ -75,6 +75,9 @@ public function main() $this->stepInfo('Origin: ' . $origins[0]->origin); $this->step('Update service'); + $service->waitFor('deployed', null, function ($s) { + $this->stepInfo('Service is still being created. Waiting...'); + }); $service->update(array( 'origins' => array( array( 'origin' => 'updated-origin.php-opencloud.com' ) @@ -82,6 +85,9 @@ public function main() )); $this->step('Purge ALL cached service assets'); + $service->waitFor('deployed', null, function ($s) { + $this->stepInfo('Service is still being updated. Waiting...'); + }); $service->purgeAssets(); $this->step('Delete service'); From a9de5fcf5de9bafea3c028624f298cc52f495b1f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 30 Dec 2014 05:43:42 -0800 Subject: [PATCH 333/835] Removing trailing space to make the linter happy. --- lib/OpenCloud/CDN/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php index 14f2281a7..af4669e81 100644 --- a/lib/OpenCloud/CDN/Service.php +++ b/lib/OpenCloud/CDN/Service.php @@ -157,7 +157,7 @@ public function getHomeDocument() /** * Returns the ping (status) response for the CDN service * - * @return Guzzle\Http\Message\Response + * @return Guzzle\Http\Message\Response */ public function getPing() { From 3386d76ef298cf64f2c819a4395844e01e984453 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 31 Dec 2014 09:22:26 -0800 Subject: [PATCH 334/835] Adding ID property (primary key; generated) to Service resource. --- docs/userguide/CDN/USERGUIDE.md | 4 ++-- lib/OpenCloud/CDN/Resource/Service.php | 6 +----- samples/CDN/delete-service.php | 2 +- samples/CDN/get-service.php | 2 +- samples/CDN/purge-cached-service-asset.php | 2 +- samples/CDN/purge-cached-service-assets.php | 2 +- samples/CDN/update-service.php | 2 +- tests/OpenCloud/Smoke/Unit/CDN.php | 2 +- 8 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index de26dac3a..113fe46a4 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -146,10 +146,10 @@ foreach ($services as $service) { ### Get a service -You can retrieve a specific service by using that service's name, as shown in the following example: +You can retrieve a specific service by using that service's ID, as shown in the following example: ```php -$service = $cdnService->getservice('acme_site'); +$service = $cdnService->getService('0e09ad12-2bfe-4607-80fd-116fa68d9c79'); /** @var $service OpenCloud\CDN\Resource\Service **/ ``` diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 2823286d1..0a4977329 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -32,6 +32,7 @@ class Service extends PersistentResource const UPDATE_METHOD = 'PATCH'; + protected $id; protected $name; protected $domains; protected $origins; @@ -92,11 +93,6 @@ protected function assetsUrl() return $url; } - protected function primaryKeyField() - { - return 'name'; - } - protected function createJson() { $createJson = parent::createJson(); diff --git a/samples/CDN/delete-service.php b/samples/CDN/delete-service.php index 345479084..6b6fc1914 100644 --- a/samples/CDN/delete-service.php +++ b/samples/CDN/delete-service.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getServices('{name}'); +$service = $cdnService->getServices('{id}'); // 4. Delete it. $service->delete(); diff --git a/samples/CDN/get-service.php b/samples/CDN/get-service.php index 7a4a72dae..3b2d9044e 100644 --- a/samples/CDN/get-service.php +++ b/samples/CDN/get-service.php @@ -30,5 +30,5 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{name}'); +$service = $cdnService->getService('{id}'); /** @var $service OpenCloud\CDN\Resource\Service **/ diff --git a/samples/CDN/purge-cached-service-asset.php b/samples/CDN/purge-cached-service-asset.php index 715a80699..33b785209 100644 --- a/samples/CDN/purge-cached-service-asset.php +++ b/samples/CDN/purge-cached-service-asset.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getServices('{name}'); +$service = $cdnService->getServices('{id}'); // 4. Purge a specific asset belonging to service. $service->purgeAssets('{asset URL}'); diff --git a/samples/CDN/purge-cached-service-assets.php b/samples/CDN/purge-cached-service-assets.php index 0d032c89c..5e74c989e 100644 --- a/samples/CDN/purge-cached-service-assets.php +++ b/samples/CDN/purge-cached-service-assets.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getServices('{name}'); +$service = $cdnService->getServices('{id}'); // 4. Purge all assets belonging to service. $service->purgeAssets(); diff --git a/samples/CDN/update-service.php b/samples/CDN/update-service.php index 13b34b7bf..a839a07e2 100644 --- a/samples/CDN/update-service.php +++ b/samples/CDN/update-service.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{name}'); +$service = $cdnService->getService('{id}'); // 4. Update it. $service->update(array( diff --git a/tests/OpenCloud/Smoke/Unit/CDN.php b/tests/OpenCloud/Smoke/Unit/CDN.php index c6ec30209..da828676b 100644 --- a/tests/OpenCloud/Smoke/Unit/CDN.php +++ b/tests/OpenCloud/Smoke/Unit/CDN.php @@ -68,7 +68,7 @@ public function main() } $this->step('Get service'); - $service = $this->getService()->getService('php-opencloud.com'); + $service = $this->getService()->getService($createdService->getId()); $this->stepInfo('Service name: ' . $service->getName()); $this->stepInfo('Status: ' . $service->getStatus()); $origins = $service->getOrigins(); From 67da4ede71f0d97708824a0c0ad0704db3cddcbd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:41:18 -0800 Subject: [PATCH 335/835] Expanding on arguments. --- docs/userguide/CDN/USERGUIDE.md | 71 ++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 113fe46a4..1569b8d4b 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -101,12 +101,36 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the service. This name must be unique. | String | No | `null` | `acme_site` | -| `domains` | An array of associative arrays, each specifying a domain name for your service. | Array of associative arrays | Yes | `null` | `array( array( 'domain' => 'www.acme.com' ), array ( 'domain' => 'acme.com' ) )` | -| `origins` | An array of associative arrays, each specifying an origin server for your service. The `port`, `ssl`, and `rules` elements for each origin server are optional. | Array of associative arrays | Yes | `null` | `array( array( 'origin' => 'origin.acme.com', 'port' => 80, 'ssl' => false, 'rules' => array() ) )` | -| `flavorId` | The flavor used to configure this service. Use the [list flavors](#list-flavors) operation to retrieve a list of all available flavors. | String | Yes| `null` | `cdn` | -| `restrictions` | An array of associative arrays, each specifying a restriction for who can or cannot access content from the CDN cache. | Array of associative arrays | No | `null` | `array( array( 'name' => 'website only', 'rules' => array( array( 'name' => 'mywebsite.com', 'httpHost' => 'www.mywebsite.com' ) ) ) )` | -| `caching` | An array of associative arrays, each specifying a caching rule for your service's assets. | No | `null` | `array( array( 'name' => 'default', 'ttl' => 3600 ) )` | +| `name` | A human-readable name for the service. This name must be unique. | String | Yes | - | `acme_site` | +| `flavorId` | The ID of the flavor to use for this service. | String | Yes | - | `cdn` | +| `domains` | List of domain for your service. | Array of associative arrays | Yes | - | `array( ... )` | +| `domains[n]` | Information about a domain for your service. | Associative array | Yes | - | `array( ... )` | +| `domains[n]['domain'] | The domain name for your service. | String | Yes | - | 'www.acme.com' | +| `domains[n]['protocol'] | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | +| `origins` | List of origin servers for your service. | Array of associative arrays | Yes | - | `array( ... )` | +| `origins[n]` | Information about an origin server for your service. | Associative array | Yes | - | `array( ... )` | +| `origins[n]['origin']` | The origin server address, from where the CDN will pull your web site's assets. | String | Yes | - | `origin.acme.com` | +| `origins[n]['origin']['port']` | The origin server's port. | Integer | No | 80 | `8080` | +| `origins[n]['origin']['ssl']` | Whether origin server uses SSL. | Boolean | No | `false` | `true` | +| `origins[n]['origin']['rules'] | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n] | Information about an access rule. | Associative array | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n]['name'] | A human-readable name of the rule. | String | No | `null` | `images` | +| `origins[n]['origin']['rules'][n]['request_url'] | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `caching` | List of TTL rules for assets of this service. | Array of associative arrays | No | `null` | `array( ... )` | +| `caching[n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | +| `caching[n]['name']` | A human-readable name of the TTL rule. | String | No | `null` | `long_ttl` | +| `caching[n]['ttl']` | The TTL value, in seconds. | Integer | No | `null` | `604800` | +| `caching[n]['rules']` | List of rules that determine if this TTL should be applied to an asset. | Array of associative arrays | No | `null` | `array( ... )` | +| `caching[n]['rules'][n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | +| `caching[n]['rules'][n]['name']` | A human-readable name of the TTL rule. | No | `null` | `images` | +| `caching[n]['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `restrictions` | List of restrictions on who can access new service. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | +| `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | +| `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | +| `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | +| `restrictions[n]['rules'][n]['referrer'] | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | You can create a service as shown in the following example: @@ -161,11 +185,36 @@ This operation takes one parameter, an associative array, with the following key | Name | Description | Data type | Required? | Default value | Example value | | ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `domains` | An array of associative arrays, each specifying a domain name for your service. | Array of associative arrays | Yes | `null` | `array( array( 'domain' => 'www.acme.com' ), array ( 'domain' => 'acme.com' ) )` | -| `origins` | An array of associative arrays, each specifying an origin server for your service. The `port`, `ssl`, and `rules` elements for each origin server are optional. | Array of associative arrays | Yes | `null` | `array( array( 'origin' => 'origin.acme.com', 'port' => 80, 'ssl' => false, 'rules' => array() ) )` | -| `flavorId` | The flavor used to configure this service. Use the [list flavors](#list-flavors) operation to retrieve a list of all available flavors. | String | Yes| `null` | `cdn` | -| `restrictions` | An array of associative arrays, each specifying a restriction for who can or cannot access content from the CDN cache. | Array of associative arrays | No | `null` | `array( array( 'name' => 'website only', 'rules' => array( array( 'name' => 'mywebsite.com', 'httpHost' => 'www.mywebsite.com' ) ) ) )` | -| `caching` | An array of associative arrays, each specifying a caching rule for your service's assets. | No | `null` | `array( array( 'name' => 'default', 'ttl' => 3600 ) )` | +| `name` | A human-readable name for the service. This name must be unique. | String | Yes | - | `acme_site` | +| `flavorId` | The ID of the flavor to use for this service. | String | Yes | - | `cdn` | +| `domains` | List of domain for your service. | Array of associative arrays | Yes | - | `array( ... )` | +| `domains[n]` | Information about a domain for your service. | Associative array | Yes | - | `array( ... )` | +| `domains[n]['domain'] | The domain name for your service. | String | Yes | - | 'www.acme.com' | +| `domains[n]['protocol'] | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | +| `origins` | List of origin servers for your service. | Array of associative arrays | Yes | - | `array( ... )` | +| `origins[n]` | Information about an origin server for your service. | Associative array | Yes | - | `array( ... )` | +| `origins[n]['origin']` | The origin server address, from where the CDN will pull your web site's assets. | String | Yes | - | `origin.acme.com` | +| `origins[n]['origin']['port']` | The origin server's port. | Integer | No | 80 | `8080` | +| `origins[n]['origin']['ssl']` | Whether origin server uses SSL. | Boolean | No | `false` | `true` | +| `origins[n]['origin']['rules'] | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n] | Information about an access rule. | Associative array | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n]['name'] | A human-readable name of the rule. | String | No | `null` | `images` | +| `origins[n]['origin']['rules'][n]['request_url'] | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `caching` | List of TTL rules for assets of this service. | Array of associative arrays | No | `null` | `array( ... )` | +| `caching[n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | +| `caching[n]['name']` | A human-readable name of the TTL rule. | String | No | `null` | `long_ttl` | +| `caching[n]['ttl']` | The TTL value, in seconds. | Integer | No | `null` | `604800` | +| `caching[n]['rules']` | List of rules that determine if this TTL should be applied to an asset. | Array of associative arrays | No | `null` | `array( ... )` | +| `caching[n]['rules'][n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | +| `caching[n]['rules'][n]['name']` | A human-readable name of the TTL rule. | No | `null` | `images` | +| `caching[n]['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `restrictions` | List of restrictions on who can access new service. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | +| `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | +| `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | +| `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | +| `restrictions[n]['rules'][n]['referrer'] | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | You can update a service as shown in the following example: From e29681eda1c5d505f9836e44d1f01beb014104a0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:42:49 -0800 Subject: [PATCH 336/835] Moving @see annotations to new line. --- lib/OpenCloud/CDN/Service.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/CDN/Service.php b/lib/OpenCloud/CDN/Service.php index af4669e81..57c880052 100644 --- a/lib/OpenCloud/CDN/Service.php +++ b/lib/OpenCloud/CDN/Service.php @@ -50,7 +50,8 @@ public function service($id = null) /** * Creates a new Service and returns it. * - * @param array $params Service creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/CDN/USERGUIDE.md#create-a-service + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/CDN/USERGUIDE.md#create-a-service + * @param array $params Service creation parameters. * @return \OpenCloud\CDN\Resource\Service Object representing created service */ public function createService(array $params = array()) @@ -101,7 +102,8 @@ public function flavor($id = null) /** * Creates a new Flavor and returns it. * - * @param array $params Flavor creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/CDN/USERGUIDE.md#create-a-flavor + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/CDN/USERGUIDE.md#create-a-flavor + * @param array $params Flavor creation parameters. * @return \OpenCloud\CDN\Resource\Flavor Object representing created flavor */ public function createFlavor(array $params = array()) From b7b020e5e3a22d5d275897a4f8e048f9838151ff Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:46:47 -0800 Subject: [PATCH 337/835] Doing the same things with fewer lines of code. --- lib/OpenCloud/Common/Resource/PersistentResource.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index f769f9856..bd6111675 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -86,11 +86,8 @@ public function update($params = array()) protected function makeUpdateRequest($json) { - if ('PATCH' === static::UPDATE_METHOD) { - return $this->getClient()->patch($this->getUrl(), self::getJsonHeader(), $json)->send(); - } else { - return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send(); - } + $updateMethod = ('PATCH' === static::UPDATE_METHOD) ? 'patch' : 'post'; + return $this->getClient()->$updateMethod($this->getUrl(), self::getJsonHeader(), $json)->send(); } /** From 70be88b5bcefdc4be5af2d9570d40b49f33f545e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:48:34 -0800 Subject: [PATCH 338/835] Refactoring to remove single-use local variable. --- lib/OpenCloud/Common/Resource/PersistentResource.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index bd6111675..35e86529e 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -273,8 +273,7 @@ protected function recursivelyAliasPropertyValue($propertyValue) } } } elseif (is_object($propertyValue) && ($propertyValue instanceof \stdClass)) { - $objectVars = get_object_vars($propertyValue); - foreach ($objectVars as $key => $subValue) { + foreach (get_object_vars($propertyValue) as $key => $subValue) { unset($propertyValue->$key); $propertyValue->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($subValue); } From 5dc7b411c0c3882f97d57997ad8fc819e08bed10 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:52:45 -0800 Subject: [PATCH 339/835] Condensing single-element array literals to single line. --- samples/CDN/create-service.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/samples/CDN/create-service.php b/samples/CDN/create-service.php index 2df1f5c5e..a35c8ec61 100644 --- a/samples/CDN/create-service.php +++ b/samples/CDN/create-service.php @@ -33,14 +33,10 @@ $service = $cdnService->createService(array( 'name' => '{name}', 'domains' => array( - array( - 'domain' => '{domain name}' - ) + array( 'domain' => '{domain name}' ) ), 'origins' => array( - array( - 'origin' => '{origin address}' - ) + array( 'origin' => '{origin address}' ) ), 'flavorId' => '{flavor ID}' )); From 8908d8a5a170145b9425080c04dd3c450fa14d5e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:53:14 -0800 Subject: [PATCH 340/835] Fixing typo in method names. --- samples/CDN/delete-flavor.php | 2 +- samples/CDN/delete-service.php | 2 +- samples/CDN/purge-cached-service-asset.php | 2 +- samples/CDN/purge-cached-service-assets.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/CDN/delete-flavor.php b/samples/CDN/delete-flavor.php index 760a0306b..9d2f291ef 100644 --- a/samples/CDN/delete-flavor.php +++ b/samples/CDN/delete-flavor.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get flavor. -$flavor = $cdnFlavor->getFlavors('{id}'); +$flavor = $cdnFlavor->getFlavor('{id}'); // 4. Delete it. $flavor->delete(); diff --git a/samples/CDN/delete-service.php b/samples/CDN/delete-service.php index 6b6fc1914..0947bcd60 100644 --- a/samples/CDN/delete-service.php +++ b/samples/CDN/delete-service.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getServices('{id}'); +$service = $cdnService->getService('{id}'); // 4. Delete it. $service->delete(); diff --git a/samples/CDN/purge-cached-service-asset.php b/samples/CDN/purge-cached-service-asset.php index 33b785209..8bbf61a38 100644 --- a/samples/CDN/purge-cached-service-asset.php +++ b/samples/CDN/purge-cached-service-asset.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getServices('{id}'); +$service = $cdnService->getService('{id}'); // 4. Purge a specific asset belonging to service. $service->purgeAssets('{asset URL}'); diff --git a/samples/CDN/purge-cached-service-assets.php b/samples/CDN/purge-cached-service-assets.php index 5e74c989e..36a5e37b5 100644 --- a/samples/CDN/purge-cached-service-assets.php +++ b/samples/CDN/purge-cached-service-assets.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getServices('{id}'); +$service = $cdnService->getService('{id}'); // 4. Purge all assets belonging to service. $service->purgeAssets(); From 9acbae5aa4af846b808cdc554dd70faea8d69f2c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 04:57:34 -0800 Subject: [PATCH 341/835] Using better ID placeholder names. --- samples/CDN/create-flavor.php | 2 +- samples/CDN/delete-flavor.php | 2 +- samples/CDN/delete-service.php | 2 +- samples/CDN/get-flavor.php | 2 +- samples/CDN/get-service.php | 2 +- samples/CDN/purge-cached-service-asset.php | 2 +- samples/CDN/purge-cached-service-assets.php | 2 +- samples/CDN/update-service.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/CDN/create-flavor.php b/samples/CDN/create-flavor.php index c9811cf85..5e2433ad7 100644 --- a/samples/CDN/create-flavor.php +++ b/samples/CDN/create-flavor.php @@ -31,7 +31,7 @@ // 3. Create flavor. $flavor = $cdnFlavor->createFlavor(array( - 'id' => '{id}', + 'id' => '{flavorId}', 'providers' => array( array( 'provider' => 'akamai', diff --git a/samples/CDN/delete-flavor.php b/samples/CDN/delete-flavor.php index 9d2f291ef..28f901c50 100644 --- a/samples/CDN/delete-flavor.php +++ b/samples/CDN/delete-flavor.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get flavor. -$flavor = $cdnFlavor->getFlavor('{id}'); +$flavor = $cdnFlavor->getFlavor('{flavorId}'); // 4. Delete it. $flavor->delete(); diff --git a/samples/CDN/delete-service.php b/samples/CDN/delete-service.php index 0947bcd60..2b223cdf7 100644 --- a/samples/CDN/delete-service.php +++ b/samples/CDN/delete-service.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{id}'); +$service = $cdnService->getService('{serviceId}'); // 4. Delete it. $service->delete(); diff --git a/samples/CDN/get-flavor.php b/samples/CDN/get-flavor.php index e63ffcc29..90e605c21 100644 --- a/samples/CDN/get-flavor.php +++ b/samples/CDN/get-flavor.php @@ -30,5 +30,5 @@ $cdnService = $client->cdnService(); // 3. Get flavor. -$flavor = $cdnService->getFlavor('{flavor ID}'); +$flavor = $cdnService->getFlavor('{flavorId}'); /** @var $flavor OpenCloud\CDN\Resource\Flavor **/ diff --git a/samples/CDN/get-service.php b/samples/CDN/get-service.php index 3b2d9044e..113d4ae24 100644 --- a/samples/CDN/get-service.php +++ b/samples/CDN/get-service.php @@ -30,5 +30,5 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{id}'); +$service = $cdnService->getService('{serviceId}'); /** @var $service OpenCloud\CDN\Resource\Service **/ diff --git a/samples/CDN/purge-cached-service-asset.php b/samples/CDN/purge-cached-service-asset.php index 8bbf61a38..2ab5141d0 100644 --- a/samples/CDN/purge-cached-service-asset.php +++ b/samples/CDN/purge-cached-service-asset.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{id}'); +$service = $cdnService->getService('{serviceId}'); // 4. Purge a specific asset belonging to service. $service->purgeAssets('{asset URL}'); diff --git a/samples/CDN/purge-cached-service-assets.php b/samples/CDN/purge-cached-service-assets.php index 36a5e37b5..afc9cb1c6 100644 --- a/samples/CDN/purge-cached-service-assets.php +++ b/samples/CDN/purge-cached-service-assets.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{id}'); +$service = $cdnService->getService('{serviceId}'); // 4. Purge all assets belonging to service. $service->purgeAssets(); diff --git a/samples/CDN/update-service.php b/samples/CDN/update-service.php index a839a07e2..8f5c9db6f 100644 --- a/samples/CDN/update-service.php +++ b/samples/CDN/update-service.php @@ -30,7 +30,7 @@ $cdnService = $client->cdnService(); // 3. Get service. -$service = $cdnService->getService('{id}'); +$service = $cdnService->getService('{serviceId}'); // 4. Update it. $service->update(array( From e16e9a03b55691f1647deb17c39cc09e12641fb9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 05:46:37 -0800 Subject: [PATCH 342/835] Fixing formatting issues due to missing backticks. --- docs/userguide/CDN/USERGUIDE.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 1569b8d4b..cc285834d 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -105,17 +105,17 @@ This operation takes one parameter, an associative array, with the following key | `flavorId` | The ID of the flavor to use for this service. | String | Yes | - | `cdn` | | `domains` | List of domain for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `domains[n]` | Information about a domain for your service. | Associative array | Yes | - | `array( ... )` | -| `domains[n]['domain'] | The domain name for your service. | String | Yes | - | 'www.acme.com' | -| `domains[n]['protocol'] | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | +| `domains[n]['domain']` | The domain name for your service. | String | Yes | - | 'www.acme.com' | +| `domains[n]['protocol']` | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | | `origins` | List of origin servers for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `origins[n]` | Information about an origin server for your service. | Associative array | Yes | - | `array( ... )` | | `origins[n]['origin']` | The origin server address, from where the CDN will pull your web site's assets. | String | Yes | - | `origin.acme.com` | | `origins[n]['origin']['port']` | The origin server's port. | Integer | No | 80 | `8080` | | `origins[n]['origin']['ssl']` | Whether origin server uses SSL. | Boolean | No | `false` | `true` | -| `origins[n]['origin']['rules'] | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | -| `origins[n]['origin']['rules'][n] | Information about an access rule. | Associative array | No | `null` | `array( ... )` | -| `origins[n]['origin']['rules'][n]['name'] | A human-readable name of the rule. | String | No | `null` | `images` | -| `origins[n]['origin']['rules'][n]['request_url'] | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `origins[n]['origin']['rules']` | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n]` | Information about an access rule. | Associative array | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n]['name']` | A human-readable name of the rule. | String | No | `null` | `images` | +| `origins[n]['origin']['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | | `caching` | List of TTL rules for assets of this service. | Array of associative arrays | No | `null` | `array( ... )` | | `caching[n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['name']` | A human-readable name of the TTL rule. | String | No | `null` | `long_ttl` | @@ -130,7 +130,7 @@ This operation takes one parameter, an associative array, with the following key | `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | -| `restrictions[n]['rules'][n]['referrer'] | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | +| `restrictions[n]['rules'][n]['referrer']` | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | You can create a service as shown in the following example: @@ -189,17 +189,17 @@ This operation takes one parameter, an associative array, with the following key | `flavorId` | The ID of the flavor to use for this service. | String | Yes | - | `cdn` | | `domains` | List of domain for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `domains[n]` | Information about a domain for your service. | Associative array | Yes | - | `array( ... )` | -| `domains[n]['domain'] | The domain name for your service. | String | Yes | - | 'www.acme.com' | -| `domains[n]['protocol'] | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | +| `domains[n]['domain']` | The domain name for your service. | String | Yes | - | 'www.acme.com' | +| `domains[n]['protocol']` | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | | `origins` | List of origin servers for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `origins[n]` | Information about an origin server for your service. | Associative array | Yes | - | `array( ... )` | | `origins[n]['origin']` | The origin server address, from where the CDN will pull your web site's assets. | String | Yes | - | `origin.acme.com` | | `origins[n]['origin']['port']` | The origin server's port. | Integer | No | 80 | `8080` | | `origins[n]['origin']['ssl']` | Whether origin server uses SSL. | Boolean | No | `false` | `true` | -| `origins[n]['origin']['rules'] | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | -| `origins[n]['origin']['rules'][n] | Information about an access rule. | Associative array | No | `null` | `array( ... )` | -| `origins[n]['origin']['rules'][n]['name'] | A human-readable name of the rule. | String | No | `null` | `images` | -| `origins[n]['origin']['rules'][n]['request_url'] | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `origins[n]['origin']['rules']` | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n]` | Information about an access rule. | Associative array | No | `null` | `array( ... )` | +| `origins[n]['origin']['rules'][n]['name']` | A human-readable name of the rule. | String | No | `null` | `images` | +| `origins[n]['origin']['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | | `caching` | List of TTL rules for assets of this service. | Array of associative arrays | No | `null` | `array( ... )` | | `caching[n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['name']` | A human-readable name of the TTL rule. | String | No | `null` | `long_ttl` | @@ -214,7 +214,7 @@ This operation takes one parameter, an associative array, with the following key | `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | -| `restrictions[n]['rules'][n]['referrer'] | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | +| `restrictions[n]['rules'][n]['referrer']` | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | You can update a service as shown in the following example: From 7f1b97127dd01c1d255332efac0ee467b7f1b806 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 05:48:26 -0800 Subject: [PATCH 343/835] Trimming whitespace. --- samples/CDN/create-service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/CDN/create-service.php b/samples/CDN/create-service.php index a35c8ec61..33967d470 100644 --- a/samples/CDN/create-service.php +++ b/samples/CDN/create-service.php @@ -33,10 +33,10 @@ $service = $cdnService->createService(array( 'name' => '{name}', 'domains' => array( - array( 'domain' => '{domain name}' ) + array('domain' => '{domain name}') ), 'origins' => array( - array( 'origin' => '{origin address}' ) + array('origin' => '{origin address}') ), 'flavorId' => '{flavor ID}' )); From c908eb9ade929c045988846d3668b9600a296cc8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 05:49:58 -0800 Subject: [PATCH 344/835] Standardizing placeholders to use camelCase. --- samples/CDN/create-service.php | 6 +++--- samples/CDN/purge-cached-service-asset.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/CDN/create-service.php b/samples/CDN/create-service.php index 33967d470..102fd1f18 100644 --- a/samples/CDN/create-service.php +++ b/samples/CDN/create-service.php @@ -33,11 +33,11 @@ $service = $cdnService->createService(array( 'name' => '{name}', 'domains' => array( - array('domain' => '{domain name}') + array('domain' => '{domainName}') ), 'origins' => array( - array('origin' => '{origin address}') + array('origin' => '{originAddress}') ), - 'flavorId' => '{flavor ID}' + 'flavorId' => '{flavorId}' )); /** @var $service OpenCloud\CDN\Resource\Service **/ diff --git a/samples/CDN/purge-cached-service-asset.php b/samples/CDN/purge-cached-service-asset.php index 2ab5141d0..fdb2f97c4 100644 --- a/samples/CDN/purge-cached-service-asset.php +++ b/samples/CDN/purge-cached-service-asset.php @@ -33,4 +33,4 @@ $service = $cdnService->getService('{serviceId}'); // 4. Purge a specific asset belonging to service. -$service->purgeAssets('{asset URL}'); +$service->purgeAssets('{assetUrl}'); From 959a48975eea23b11485a8b7a62dc95a3c62c211 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 3 Jan 2015 05:00:22 -0800 Subject: [PATCH 345/835] Fixing parameter paths. --- docs/userguide/CDN/USERGUIDE.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index cc285834d..ff908e1d7 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -110,12 +110,12 @@ This operation takes one parameter, an associative array, with the following key | `origins` | List of origin servers for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `origins[n]` | Information about an origin server for your service. | Associative array | Yes | - | `array( ... )` | | `origins[n]['origin']` | The origin server address, from where the CDN will pull your web site's assets. | String | Yes | - | `origin.acme.com` | -| `origins[n]['origin']['port']` | The origin server's port. | Integer | No | 80 | `8080` | -| `origins[n]['origin']['ssl']` | Whether origin server uses SSL. | Boolean | No | `false` | `true` | -| `origins[n]['origin']['rules']` | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | -| `origins[n]['origin']['rules'][n]` | Information about an access rule. | Associative array | No | `null` | `array( ... )` | -| `origins[n]['origin']['rules'][n]['name']` | A human-readable name of the rule. | String | No | `null` | `images` | -| `origins[n]['origin']['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `origins[n]['port']` | The origin server's port. | Integer | No | 80 | `8080` | +| `origins[n]['ssl']` | Whether origin server uses SSL. | Boolean | No | `false` | `true` | +| `origins[n]['rules']` | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | +| `origins[n]['rules'][n]` | Information about an access rule. | Associative array | No | `null` | `array( ... )` | +| `origins[n]['rules'][n]['name']` | A human-readable name of the rule. | String | No | `null` | `images` | +| `origins[n]['rules'][n]['requestUrl']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | | `caching` | List of TTL rules for assets of this service. | Array of associative arrays | No | `null` | `array( ... )` | | `caching[n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['name']` | A human-readable name of the TTL rule. | String | No | `null` | `long_ttl` | From 6d0a4e0ca5a75649425fd9afddaa7f12bb871de9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 3 Jan 2015 05:00:44 -0800 Subject: [PATCH 346/835] Aliasing requestUrl -> request_url. --- docs/userguide/CDN/USERGUIDE.md | 6 +++--- lib/OpenCloud/CDN/Resource/Service.php | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index ff908e1d7..be9404778 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -123,7 +123,7 @@ This operation takes one parameter, an associative array, with the following key | `caching[n]['rules']` | List of rules that determine if this TTL should be applied to an asset. | Array of associative arrays | No | `null` | `array( ... )` | | `caching[n]['rules'][n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['rules'][n]['name']` | A human-readable name of the TTL rule. | No | `null` | `images` | -| `caching[n]['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `caching[n]['rules'][n]['requestUrl']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | | `restrictions` | List of restrictions on who can access new service. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | @@ -199,7 +199,7 @@ This operation takes one parameter, an associative array, with the following key | `origins[n]['origin']['rules']` | List of rules defining the conditions when this origin should be accessed. | Array of associative arrays | No | `null` | `array( ... )` | | `origins[n]['origin']['rules'][n]` | Information about an access rule. | Associative array | No | `null` | `array( ... )` | | `origins[n]['origin']['rules'][n]['name']` | A human-readable name of the rule. | String | No | `null` | `images` | -| `origins[n]['origin']['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `origins[n]['origin']['rules'][n]['requestUrl']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | | `caching` | List of TTL rules for assets of this service. | Array of associative arrays | No | `null` | `array( ... )` | | `caching[n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['name']` | A human-readable name of the TTL rule. | String | No | `null` | `long_ttl` | @@ -207,7 +207,7 @@ This operation takes one parameter, an associative array, with the following key | `caching[n]['rules']` | List of rules that determine if this TTL should be applied to an asset. | Array of associative arrays | No | `null` | `array( ... )` | | `caching[n]['rules'][n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['rules'][n]['name']` | A human-readable name of the TTL rule. | No | `null` | `images` | -| `caching[n]['rules'][n]['request_url']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | +| `caching[n]['rules'][n]['requestUrl']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | | `restrictions` | List of restrictions on who can access new service. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 0a4977329..233faacc9 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -45,7 +45,8 @@ class Service extends PersistentResource protected $aliases = array( 'flavor_id' => 'flavorId', - 'http_host' => 'httpHost' + 'http_host' => 'httpHost', + 'request_url' => 'requestUrl' ); protected $createKeys = array( From 7a7ff518c58373be9ded148b9891774d0547a9f7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 3 Jan 2015 05:09:31 -0800 Subject: [PATCH 347/835] Clarifying parameter descriptions. --- docs/userguide/CDN/USERGUIDE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index be9404778..e02599b5d 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -124,13 +124,13 @@ This operation takes one parameter, an associative array, with the following key | `caching[n]['rules'][n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['rules'][n]['name']` | A human-readable name of the TTL rule. | No | `null` | `images` | | `caching[n]['rules'][n]['requestUrl']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | -| `restrictions` | List of restrictions on who can access new service. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions` | List of restrictions on where the service can be accessed from. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | | `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | -| `restrictions[n]['rules'][n]['referrer']` | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | +| `restrictions[n]['rules'][n]['referrer']` | The domain from which the service can be accessed. | String | No | `null` | `www.wilecoyote.com` | You can create a service as shown in the following example: @@ -208,13 +208,13 @@ This operation takes one parameter, an associative array, with the following key | `caching[n]['rules'][n]` | Information about a TTL rule. | Associative array | No | `null` | `array( ... )` | | `caching[n]['rules'][n]['name']` | A human-readable name of the TTL rule. | No | `null` | `images` | | `caching[n]['rules'][n]['requestUrl']` | The request URL this rule should match (regex supported). | String | No | `null` | `^/images/.+$` | -| `restrictions` | List of restrictions on who can access new service. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions` | List of restrictions on where the service can be accessed from. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | | `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | -| `restrictions[n]['rules'][n]['referrer']` | The domain from which the new service can be accessed. | String | No | `null` | `www.wilecoyote.com` | +| `restrictions[n]['rules'][n]['referrer']` | The domain from which the service can be accessed. | String | No | `null` | `www.wilecoyote.com` | You can update a service as shown in the following example: From 5f550f9aef8a21ba71367f60266c77f735bef892 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 3 Jan 2015 05:09:40 -0800 Subject: [PATCH 348/835] Fixing typos. --- docs/userguide/CDN/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index e02599b5d..8093b7c39 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -127,7 +127,7 @@ This operation takes one parameter, an associative array, with the following key | `restrictions` | List of restrictions on where the service can be accessed from. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | -| `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions[n]['rules']` | List of restriction rules. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | | `restrictions[n]['rules'][n]['referrer']` | The domain from which the service can be accessed. | String | No | `null` | `www.wilecoyote.com` | @@ -211,7 +211,7 @@ This operation takes one parameter, an associative array, with the following key | `restrictions` | List of restrictions on where the service can be accessed from. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]` | Information about an access restriction. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['name']` | A human-readable name of the restriction. | String | No | `null` | `affiliate_sites_only` | -| `restrictions[n]['rules']` | List of restrition rules. | Array of associative arrays | No | `null` | `array( ... )` | +| `restrictions[n]['rules']` | List of restriction rules. | Array of associative arrays | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]` | Information about a restriction rule. | Associative array | No | `null` | `array( ... )` | | `restrictions[n]['rules'][n]['name']` | A human-readable name of the restriction rule. | String | No | `null` | `Wile E. Coyote's site` | | `restrictions[n]['rules'][n]['referrer']` | The domain from which the service can be accessed. | String | No | `null` | `www.wilecoyote.com` | From 013d768e8af72143e0476e2ba9fc200cf4ab02a7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sat, 3 Jan 2015 05:15:38 -0800 Subject: [PATCH 349/835] Clarifying parameter description. --- docs/userguide/CDN/USERGUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 8093b7c39..1db7acebe 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -105,7 +105,7 @@ This operation takes one parameter, an associative array, with the following key | `flavorId` | The ID of the flavor to use for this service. | String | Yes | - | `cdn` | | `domains` | List of domain for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `domains[n]` | Information about a domain for your service. | Associative array | Yes | - | `array( ... )` | -| `domains[n]['domain']` | The domain name for your service. | String | Yes | - | 'www.acme.com' | +| `domains[n]['domain']` | A domain name used by your service. | String | Yes | - | 'www.acme.com' | | `domains[n]['protocol']` | The protocol used by your service web site, `http` or `https`. | String | No | `http` | `http` | | `origins` | List of origin servers for your service. | Array of associative arrays | Yes | - | `array( ... )` | | `origins[n]` | Information about an origin server for your service. | Associative array | Yes | - | `array( ... )` | From a0671b8c46dfb615d61c92f617e0ad80ae3efe99 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2015 12:17:10 -0800 Subject: [PATCH 350/835] Adding helper methods to generate JSON Patch representation for resource. --- composer.json | 9 ++- .../Common/Resource/PersistentResource.php | 43 +++++++++++ .../Resource/PersistentResourceTest.php | 74 +++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index eed385e82..71d8b7903 100644 --- a/composer.json +++ b/composer.json @@ -20,10 +20,17 @@ "OpenCloud": ["lib/", "tests/"] } }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/ycombinator/json-patch-php" + } + ], "require": { "php" : ">=5.3.3", "guzzle/http" : "~3.8", - "psr/log": "~1.0" + "psr/log": "~1.0", + "mikemccabe/json-patch-php": "dev-master" }, "require-dev" : { "phpunit/phpunit": "4.3.*", diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 35e86529e..57d2dca8e 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -25,6 +25,7 @@ use OpenCloud\Common\Exceptions\NameError; use OpenCloud\Common\Exceptions\UnsupportedExtensionError; use OpenCloud\Common\Exceptions\UpdateError; +use mikemccabe\JsonPatch\JsonPatch; abstract class PersistentResource extends BaseResource { @@ -345,6 +346,48 @@ public function checkExtension($alias) return true; } + /** + * Returns the object's properties as an array + */ + protected function getPropertiesAsArray() + { + $properties = get_object_vars($this); + + $propertiesToRemove = array('aliases', 'service', 'parent', 'metadata'); + foreach ($propertiesToRemove as $property) { + unset($properties[$property]); + } + + return $properties; + } + + /** + * Generates a JSON Patch representation and return its + * + * @param mixed $updatedProperties Properties of the resource to update + * @return String JSON Patch representation for updates + */ + protected function generateJsonPatch($updatedProperties) + { + // Normalize current and updated properties into nested arrays + $currentProperties = json_decode(json_encode($this->getPropertiesAsArray()), true); + $updatedProperties = json_decode(json_encode($updatedProperties), true); + + // Add any properties that haven't changed to generate the correct patch + // (otherwise unchanging properties are marked as removed in the patch) + foreach ($currentProperties as $key => $value) { + if (!array_key_exists($key, $updatedProperties)) { + $updatedProperties[$key] = $value; + } + } + + // Generate JSON Patch representation + $json = json_encode(JsonPatch::diff($currentProperties, $updatedProperties)); + $this->checkJsonError(); + + return $json; + } + /******** DEPRECATED METHODS ********/ /** diff --git a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php index cd81b93ab..b0dffeb80 100644 --- a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php +++ b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php @@ -31,6 +31,16 @@ public function recursivelyAliasPropertyValue($propertyValue) { return parent::recursivelyAliasPropertyValue($propertyValue); } + + public function getPropertiesAsArray() + { + return parent::getPropertiesAsArray(); + } + + public function generateJsonPatch($updateParams) + { + return parent::generateJsonPatch($updateParams); + } } class PersistentResourceTest extends OpenCloudTestCase @@ -106,4 +116,68 @@ public function testRecursivelyAliasPropertyValueWithObjects() $this->assertEquals($obj3Expected, $this->persistentResource->recursivelyAliasPropertyValue($obj3)); } + + public function testGetPropertiesAsArray() + { + $this->persistentResource->id = 17; + $this->persistentResource->tags = array('foo', 'bar'); + $this->persistentResource->domains = array( + (object) array('domain' => 'foo.phpopencloud.com'), + array('domain' => 'bar.phpopencloud.com') + ); + $this->persistentResource->origins = array( + array('origin' => 'origin1.phpopencloud.com') + ); + $this->persistentResource->status = (object) array('message' => 'Creation in progress'); + + $expectedArray = array( + 'id' => 17, + 'tags' => array('foo', 'bar'), + 'domains' => array( + (object) array('domain' => 'foo.phpopencloud.com'), + array('domain' => 'bar.phpopencloud.com') + ), + 'origins' => array( + array('origin' => 'origin1.phpopencloud.com'), + ), + 'status' => (object) array('message' => 'Creation in progress'), + ); + + $this->assertEquals($expectedArray, $this->persistentResource->getPropertiesAsArray()); + } + + public function testGenerateJsonPatch() + { + $this->persistentResource->id = 17; + $this->persistentResource->tags = array('foo', 'bar'); + $this->persistentResource->domains = array( + array('domain' => 'foo.phpopencloud.com'), + array('domain' => 'bar.phpopencloud.com') + ); + $this->persistentResource->origins = array( + array('origin' => 'origin1.phpopencloud.com') + ); + $this->persistentResource->status = array('message' => 'Creation in progress'); + + $updateParams = array( + 'tags' => array('foo', 'qux', 'baz'), + 'domains' => array( + array('domain' => 'foo.phpopencloud.com') + ), + 'origins' => array( + array('origin' => 'origin1.phpopencloud.com'), + array('origin' => 'origin2.phpopencloud.com') + ) + ); + + $expectedJsonPatch = json_encode(array( + array('op' => 'add', 'path' => '/tags/2', 'value' => 'baz'), + array('op' => 'replace', 'path' => '/tags/1', 'value' => 'qux'), + array('op' => 'remove', 'path' => '/domains/1'), + array('op' => 'add', 'path' => '/origins/1', 'value' => array("origin" => "origin2.phpopencloud.com")) + )); + + $actualJsonPatch = $this->persistentResource->generateJsonPatch($updateParams); + $this->assertEquals($expectedJsonPatch, $actualJsonPatch); + } } From 703941cb665f3efbda968fdc7d9be4150ea405d3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2015 13:00:09 -0800 Subject: [PATCH 351/835] Refactoring PATCH headers to Base. --- lib/OpenCloud/Common/Base.php | 7 +++++++ lib/OpenCloud/Common/Constants/Mime.php | 1 + lib/OpenCloud/Image/Resource/Image.php | 4 +++- lib/OpenCloud/Image/Service.php | 13 ------------- tests/OpenCloud/Tests/Common/BaseTest.php | 15 +++++++++++++++ .../Tests/Image/Resource/ImageTest.php | 19 ++++++++++++++++++- 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Common/Base.php b/lib/OpenCloud/Common/Base.php index 3e0bc3dfb..fd638cc81 100644 --- a/lib/OpenCloud/Common/Base.php +++ b/lib/OpenCloud/Common/Base.php @@ -32,6 +32,8 @@ */ abstract class Base { + const PATCH_CONTENT_TYPE = MimeConst::JSON_PATCH; + /** * Holds all the properties added by overloading. * @@ -419,4 +421,9 @@ protected static function getJsonHeader() { return array(HeaderConst::CONTENT_TYPE => MimeConst::JSON); } + + protected static function getPatchHeaders() + { + return array(HeaderConst::CONTENT_TYPE => static::PATCH_CONTENT_TYPE); + } } diff --git a/lib/OpenCloud/Common/Constants/Mime.php b/lib/OpenCloud/Common/Constants/Mime.php index 23df6cfe9..4e2942ebb 100644 --- a/lib/OpenCloud/Common/Constants/Mime.php +++ b/lib/OpenCloud/Common/Constants/Mime.php @@ -21,4 +21,5 @@ class Mime { const JSON = 'application/json'; const TEXT = 'text/plain'; + const JSON_PATCH = 'application/json-patch+json'; } diff --git a/lib/OpenCloud/Image/Resource/Image.php b/lib/OpenCloud/Image/Resource/Image.php index b8ab7407c..f24eb510b 100644 --- a/lib/OpenCloud/Image/Resource/Image.php +++ b/lib/OpenCloud/Image/Resource/Image.php @@ -35,6 +35,8 @@ class Image extends AbstractSchemaResource implements ImageInterface protected static $json_name = ''; protected static $json_collection_name = 'images'; + const PATCH_CONTENT_TYPE = 'application/openstack-images-v2.1-json-patch'; + /** * Update this resource * @@ -85,7 +87,7 @@ public function update(array $params, Schema $schema = null) $body = $document->toString(); return $this->getClient() - ->patch($this->getUrl(), $this->getService()->getPatchHeaders(), $body) + ->patch($this->getUrl(), $this->getPatchHeaders(), $body) ->send(); } diff --git a/lib/OpenCloud/Image/Service.php b/lib/OpenCloud/Image/Service.php index 86462c8d6..f0ed4d72a 100644 --- a/lib/OpenCloud/Image/Service.php +++ b/lib/OpenCloud/Image/Service.php @@ -17,7 +17,6 @@ namespace OpenCloud\Image; -use OpenCloud\Common\Constants\Header; use OpenCloud\Common\Service\CatalogService; use OpenCloud\Image\Resource\Image; use OpenCloud\Image\Resource\Schema\Schema; @@ -32,18 +31,6 @@ class Service extends CatalogService const DEFAULT_TYPE = 'image'; const DEFAULT_NAME = 'cloudImages'; - const PATCH_CONTENT_TYPE = 'application/openstack-images-v2.1-json-patch'; - - /** - * Get the default headers to send for PATCH requests - * - * @return array - */ - public function getPatchHeaders() - { - return array(Header::CONTENT_TYPE => self::PATCH_CONTENT_TYPE); - } - /** * This operation returns images you created, shared images that you accepted, and standard images. * diff --git a/tests/OpenCloud/Tests/Common/BaseTest.php b/tests/OpenCloud/Tests/Common/BaseTest.php index c0a1c44ea..625863fb7 100644 --- a/tests/OpenCloud/Tests/Common/BaseTest.php +++ b/tests/OpenCloud/Tests/Common/BaseTest.php @@ -36,6 +36,11 @@ public function getBar() { return $this->bar; } + + public static function getPatchHeaders() + { + return parent::getPatchHeaders(); + } } class BaseTest extends \OpenCloud\Tests\OpenCloudTestCase @@ -100,4 +105,14 @@ public function testSetProperty() $this->my->setBaz('goodbye'); $this->assertEquals('goodbye', $this->my->getBaz()); } + + public function testGetPatchHeaders() + { + $expectedHeaders = array( + 'Content-Type' => 'application/json-patch+json' + ); + + $my = $this->my; + $this->assertEquals($expectedHeaders, $my::getPatchHeaders()); + } } diff --git a/tests/OpenCloud/Tests/Image/Resource/ImageTest.php b/tests/OpenCloud/Tests/Image/Resource/ImageTest.php index 5b9fc03b4..bcea3e089 100644 --- a/tests/OpenCloud/Tests/Image/Resource/ImageTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/ImageTest.php @@ -22,11 +22,19 @@ use OpenCloud\Image\Resource\Schema\Schema; use OpenCloud\Tests\OpenCloudTestCase; +class PublicImage extends Image +{ + public static function getPatchHeaders() + { + return parent::getPatchHeaders(); + } +} + class ImageTest extends OpenCloudTestCase { public function setupObjects() { - $this->image = new Image($this->getClient()->imageService('cloudImages', 'IAD')); + $this->image = new PublicImage($this->getClient()->imageService('cloudImages', 'IAD')); } protected function getSchemaData() @@ -139,4 +147,13 @@ public function test_Delete_Tag() $this->assertInstanceOf('Guzzle\Http\Message\Response', $this->image->deleteTag(12345)); } + + public function testGetPatchHeaders() + { + $expectedHeaders = array( + 'Content-Type' => 'application/openstack-images-v2.1-json-patch' + ); + + $this->assertEquals($expectedHeaders, $this->image->getPatchHeaders()); + } } From 4d8688166543a52ef97746d60f18a83df09fd47a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2015 13:51:34 -0800 Subject: [PATCH 352/835] Overriding update() for Service resource; getting rid of update logic in PersistentResource. --- lib/OpenCloud/CDN/Resource/Service.php | 17 +++++++++++++++-- .../Common/Resource/PersistentResource.php | 10 +--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/OpenCloud/CDN/Resource/Service.php b/lib/OpenCloud/CDN/Resource/Service.php index 233faacc9..5e3a2ff48 100644 --- a/lib/OpenCloud/CDN/Resource/Service.php +++ b/lib/OpenCloud/CDN/Resource/Service.php @@ -30,8 +30,6 @@ class Service extends PersistentResource protected static $url_resource = 'services'; protected static $json_name = 'service'; - const UPDATE_METHOD = 'PATCH'; - protected $id; protected $name; protected $domains; @@ -99,4 +97,19 @@ protected function createJson() $createJson = parent::createJson(); return $createJson->{self::$json_name}; } + + /** + * Update this resource + * + * @param array $params + * @return \Guzzle\Http\Message\Response + */ + public function update($params = array()) + { + $json = $this->generateJsonPatch($params); + + return $this->getClient() + ->patch($this->getUrl(), $this->getPatchHeaders(), $json) + ->send(); + } } diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 57d2dca8e..6a3bc19d8 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -29,8 +29,6 @@ abstract class PersistentResource extends BaseResource { - const UPDATE_METHOD = 'PUT'; - /** * Create a new resource * @@ -82,13 +80,7 @@ public function update($params = array()) $this->checkJsonError(); // send the request - return $this->makeUpdateRequest($json); - } - - protected function makeUpdateRequest($json) - { - $updateMethod = ('PATCH' === static::UPDATE_METHOD) ? 'patch' : 'post'; - return $this->getClient()->$updateMethod($this->getUrl(), self::getJsonHeader(), $json)->send(); + return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send(); } /** From 650e80dcaa83032f98f00fa011785bad4b472ebe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2015 18:27:42 -0800 Subject: [PATCH 353/835] Removing newline to trigger build. --- docs/userguide/CDN/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/CDN/README.md b/docs/userguide/CDN/README.md index a64cca833..fbe813627 100644 --- a/docs/userguide/CDN/README.md +++ b/docs/userguide/CDN/README.md @@ -93,4 +93,4 @@ $service = $cdnService->createService(array( ## Next steps -Once you have created a service, there is more you can do with it. See [complete user guide for CDN](USERGUIDE.md). +Once you have created a service, there is more you can do with it. See [complete user guide for CDN](USERGUIDE.md). \ No newline at end of file From b610917b7ebeb4d33e49ac5e4a727cf4b4199dc9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 02:56:13 -0800 Subject: [PATCH 354/835] Removing docs for administrative operations. --- docs/userguide/CDN/USERGUIDE.md | 45 --------------------------------- 1 file changed, 45 deletions(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index 1db7acebe..b59ffdb2e 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -17,10 +17,8 @@ CDN is a service that you can use to manage your CDN-enabled domains and the ori * [Purge all cached service assets](#purge-all-cached-service-assets) * [Purge a specific cached service asset](#purge-a-specific-cached-service-asset) * [Flavors](#flavors) - * [Create a flavor](#create-a-flavor) * [List flavors](#list-flavors) * [Get a flavor](#get-a-flavor) - * [Delete a flavor](#delete-a-flavor) ## Concepts @@ -269,37 +267,6 @@ $service->purgeAssets('/images/logo.png'); A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers. -### Create a flavor - -Note: When working with the Rackspace Cloud, this operation requires the `cdn:operator` role. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `id` | ID of flavor. This ID must be unique. | String | Yes | `null` | `cdn` | -| `providers` | An array of associative arrays, each representing a CDN provider. | Array of associative arrays | Yes | `null` | `array( array( 'provider' => 'akamai', 'links' => array( array( 'rel' => 'provider_url', 'href' => 'http://www.akamai.com' ) ) ) )` | - -You can create a flavor as shown in the following example: - -```php -$flavor = $cdnService->createFlavor(array( - 'id' => 'cdn', - 'providers' => array( - array( - 'name' => 'akamai', - 'links' => array( - 'rel' => 'provider_url', - 'href' => 'http://www.akamai.com' - ) - ) - ) -)); -/** @var $flavor OpenCloud\CDN\Resource\Flavor **/ -``` - -[ [Get the executable PHP script for this example](/samples/CDN/create-flavor.php) ] - ### List flavors You can list all available flavors as shown in the following example: @@ -324,15 +291,3 @@ $flavor = $cdnService->getFlavor('cdn'); ``` [ [Get the executable PHP script for this example](/samples/CDN/get-flavor.php) ] - -### Delete a flavor - -Note: When working with the Rackspace Cloud, this operation requires the `cdn:operator` role. - -You can delete a flavor as shown in the following example: - -```php -$flavor->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/CDN/delete-flavor.php) ] From 2377b064c15f7a959309d7926b3b4827960c0d8c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 02:56:30 -0800 Subject: [PATCH 355/835] Fixing ``` alignment. --- docs/userguide/CDN/USERGUIDE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/userguide/CDN/USERGUIDE.md b/docs/userguide/CDN/USERGUIDE.md index b59ffdb2e..971175219 100644 --- a/docs/userguide/CDN/USERGUIDE.md +++ b/docs/userguide/CDN/USERGUIDE.md @@ -149,7 +149,8 @@ $service = $cdnService->createService(array( ) ), 'flavorId' => 'cdn' -));``` +)); +``` [ [Get the executable PHP script for this example](/samples/CDN/create-service.php) ] From 49d5ed85c0168f32429a00f3376bb1823894a729 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 03:42:41 -0800 Subject: [PATCH 356/835] Using $this->updateKeys to determine the current values of updateable properties. --- .../Common/Resource/PersistentResource.php | 14 ++++++++------ .../Resource/PersistentResourceTest.php | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 6a3bc19d8..803112472 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -341,16 +341,18 @@ public function checkExtension($alias) /** * Returns the object's properties as an array */ - protected function getPropertiesAsArray() + protected function getUpdateablePropertiesAsArray() { $properties = get_object_vars($this); - $propertiesToRemove = array('aliases', 'service', 'parent', 'metadata'); - foreach ($propertiesToRemove as $property) { - unset($properties[$property]); + $propertiesToKeep = array(); + foreach ($this->updateKeys as $key) { + if (isset($properties[$key])) { + $propertiesToKeep[$key] = $properties[$key]; + } } - return $properties; + return $propertiesToKeep; } /** @@ -362,7 +364,7 @@ protected function getPropertiesAsArray() protected function generateJsonPatch($updatedProperties) { // Normalize current and updated properties into nested arrays - $currentProperties = json_decode(json_encode($this->getPropertiesAsArray()), true); + $currentProperties = json_decode(json_encode($this->getUpdateablePropertiesAsArray()), true); $updatedProperties = json_decode(json_encode($updatedProperties), true); // Add any properties that haven't changed to generate the correct patch diff --git a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php index b0dffeb80..0cf9342f0 100644 --- a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php +++ b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php @@ -27,14 +27,22 @@ class PublicPersistentResource extends PersistentResource 'foo_bar' => 'fooBar' ); + protected $updateKeys = array( + 'baz', + 'tags', + 'domains', + 'origins', + 'status' + ); + public function recursivelyAliasPropertyValue($propertyValue) { return parent::recursivelyAliasPropertyValue($propertyValue); } - public function getPropertiesAsArray() + public function getUpdateablePropertiesAsArray() { - return parent::getPropertiesAsArray(); + return parent::getUpdateablePropertiesAsArray(); } public function generateJsonPatch($updateParams) @@ -117,7 +125,7 @@ public function testRecursivelyAliasPropertyValueWithObjects() $this->persistentResource->recursivelyAliasPropertyValue($obj3)); } - public function testGetPropertiesAsArray() + public function testGetUpdateablePropertiesAsArray() { $this->persistentResource->id = 17; $this->persistentResource->tags = array('foo', 'bar'); @@ -131,7 +139,6 @@ public function testGetPropertiesAsArray() $this->persistentResource->status = (object) array('message' => 'Creation in progress'); $expectedArray = array( - 'id' => 17, 'tags' => array('foo', 'bar'), 'domains' => array( (object) array('domain' => 'foo.phpopencloud.com'), @@ -140,10 +147,10 @@ public function testGetPropertiesAsArray() 'origins' => array( array('origin' => 'origin1.phpopencloud.com'), ), - 'status' => (object) array('message' => 'Creation in progress'), + 'status' => (object) array('message' => 'Creation in progress') ); - $this->assertEquals($expectedArray, $this->persistentResource->getPropertiesAsArray()); + $this->assertEquals($expectedArray, $this->persistentResource->getUpdateablePropertiesAsArray()); } public function testGenerateJsonPatch() From 28a423451ac9c5b52f4519ee1b6bfce91ebb3543 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 03:43:17 -0800 Subject: [PATCH 357/835] Using property aliases when generating JSON Patch representation. --- lib/OpenCloud/Common/Resource/PersistentResource.php | 4 ++++ .../Tests/Common/Resource/PersistentResourceTest.php | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index 803112472..bb11b79be 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -375,6 +375,10 @@ protected function generateJsonPatch($updatedProperties) } } + // Recursively alias current and updated properties + $currentProperties = $this->recursivelyAliasPropertyValue($currentProperties); + $updatedProperties = $this->recursivelyAliasPropertyValue($updatedProperties); + // Generate JSON Patch representation $json = json_encode(JsonPatch::diff($currentProperties, $updatedProperties)); $this->checkJsonError(); diff --git a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php index 0cf9342f0..79d350e3b 100644 --- a/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php +++ b/tests/OpenCloud/Tests/Common/Resource/PersistentResourceTest.php @@ -165,6 +165,7 @@ public function testGenerateJsonPatch() array('origin' => 'origin1.phpopencloud.com') ); $this->persistentResource->status = array('message' => 'Creation in progress'); + $this->persistentResource->baz = (object) array( 'fooBar' => 'barbar'); $updateParams = array( 'tags' => array('foo', 'qux', 'baz'), @@ -174,10 +175,12 @@ public function testGenerateJsonPatch() 'origins' => array( array('origin' => 'origin1.phpopencloud.com'), array('origin' => 'origin2.phpopencloud.com') - ) + ), + 'baz' => array('fooBar' => 'barbarbar') ); $expectedJsonPatch = json_encode(array( + array('op' => 'replace', 'path' => '/baz/foo_bar', 'value' => 'barbarbar'), array('op' => 'add', 'path' => '/tags/2', 'value' => 'baz'), array('op' => 'replace', 'path' => '/tags/1', 'value' => 'qux'), array('op' => 'remove', 'path' => '/domains/1'), From 8257700c121704eddf0abf59ef23331e475bbd42 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 03:53:24 -0800 Subject: [PATCH 358/835] Accessing static method... statically. --- tests/OpenCloud/Tests/Image/Resource/ImageTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Image/Resource/ImageTest.php b/tests/OpenCloud/Tests/Image/Resource/ImageTest.php index bcea3e089..4a7554edc 100644 --- a/tests/OpenCloud/Tests/Image/Resource/ImageTest.php +++ b/tests/OpenCloud/Tests/Image/Resource/ImageTest.php @@ -154,6 +154,7 @@ public function testGetPatchHeaders() 'Content-Type' => 'application/openstack-images-v2.1-json-patch' ); - $this->assertEquals($expectedHeaders, $this->image->getPatchHeaders()); + $image = $this->image; + $this->assertEquals($expectedHeaders, $image::getPatchHeaders()); } } From 74a900a6803645169a2457505aa091429bd0aa29 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 05:34:25 -0800 Subject: [PATCH 359/835] Trying GC disabling to kill the segfault. --- phpunit.xml.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index be5f3c7e6..1ec5cd508 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -27,6 +27,7 @@ + From 859c7251d1631fa40972a63581175a290bfc9f75 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 6 Jan 2015 05:44:14 -0800 Subject: [PATCH 360/835] Increasing memory limit further to try and kill the segfault. --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1ec5cd508..3d4b02cd2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -26,7 +26,7 @@ - + From 0c6b1db3dd78993dad507a1ddb42410d7fcbd32e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 2 Jan 2015 15:18:16 +0100 Subject: [PATCH 361/835] Clarify openstack setup; fixes #507 --- docs/getting-started-openstack.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index a76134fe7..b00e58693 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -48,9 +48,20 @@ In this case, we want to use the Nova service: $compute = $client->computeService('nova', 'regionOne'); ``` -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. If in doubt, you can -leave blank and it will revert to the default name for the service. The second argument is the region. The third and -last argument is the type of URL; you may use either `publicURL` or `internalURL`. +The first argument is the __name__ of the service as it appears in the OpenStack service catalog. For OpenStack users, this must be retrieved and entered in your code. If you are unsure how to retrieve the service name, follow these steps: + +1. Setup the `$client` object, as above +2. Copy and run this code: + + ```php + $client->authenticate(); + + print_r($client->getCatalog()->getItems()); + ``` + +3. This will output all the items in your service catalog. Go through the outputted list and find your service, making note of the "name" field. This is the name you will need to enter as the first argument. You will also be able to see the available regions. + +The second argument is the region. The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. ### 3. Select your server image @@ -187,4 +198,4 @@ $server->delete(); Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or [Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to e-mail sdk-support@rackspace.com or open a Github issue with details. \ No newline at end of file +troubles, feel free to e-mail sdk-support@rackspace.com or open a Github issue with details. From 475b74e4e1c6c343ad4ec08d2b1d36e7cd0758cd Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 5 Jan 2015 06:03:19 -0800 Subject: [PATCH 362/835] Adding Shaunak as author to composer.json. --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index eed385e82..98d7455df 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,11 @@ { "name": "Glen Campbell", "email": "glen.campbell@rackspace.com" + }, + { + "name": "Shaunak Kashyap", + "email": "shaunak.kashyap@rackspace.com", + "homepage": "https://github.com/ycombinator" } ], "autoload": { From 3361c9c637d37f0cc8ef7abbb9b9a5f2929656c7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 05:55:39 -0800 Subject: [PATCH 363/835] Explaining WIP PRs. --- CONTRIBUTING.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3c3425de..79cf868d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,13 @@ efforts. Here are a few general guidelines to follow: minor typographical error, create a description which explains your changes and, where relevant, references the existing issue you're hoping to fix. -5. Document your code! +5. If your pull request introduces a large change or addition, please consider + creating a work-in-progress (WIP) pull request. This lets us review your changes + and provide feedback early and often rather than all at once when the entire pull + request is ready. To denote a pull request as WIP, simply add the "PR: Work In Progress" + label to it. + +6. Document your code! If you submit code, please add your name and email address to the CONTRIBUTORS file. From 201e88457f945fcf8489cb1ac635367a4624479b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 2 Jan 2015 06:04:32 -0800 Subject: [PATCH 364/835] [skip ci] Explain when to remove WIP label. --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79cf868d2..4764be882 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,9 @@ efforts. Here are a few general guidelines to follow: creating a work-in-progress (WIP) pull request. This lets us review your changes and provide feedback early and often rather than all at once when the entire pull request is ready. To denote a pull request as WIP, simply add the "PR: Work In Progress" - label to it. + label to it. When you are finished with your work in the pull request and + are ready for a final review and merge, please remove the "PR: Work In Progress" + label. 6. Document your code! From 526da68f8f9884d9060926cb0c7e0197b1a10348 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 15:22:51 +0100 Subject: [PATCH 365/835] fix docblock typo --- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index bc7951c15..18e541bc7 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -253,7 +253,7 @@ public function deleteAllObjects() * names nested in the container are returned. * @link http://api.openstack.org for a list of possible parameter * names and values - * @return 'OpenCloud\Common\Collection + * @return \OpenCloud\Common\Collection * @throws ObjFetchError */ public function objectList(array $params = array()) From a85a1a4c4d444244cd437a3f8d762540ae478e19 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 15:12:39 +0100 Subject: [PATCH 366/835] replace Logger::deprecated with PRS-3 Logger::warning --- lib/OpenCloud/Common/Collection.php | 3 ++- lib/OpenCloud/Common/Collection/ResourceIterator.php | 3 ++- lib/OpenCloud/Common/Log/Logger.php | 4 ++++ lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 6 ++++-- lib/OpenCloud/LoadBalancer/Service.php | 3 ++- lib/OpenCloud/ObjectStore/Service.php | 3 ++- lib/OpenCloud/OpenStack.php | 9 ++++++--- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index f2710639e..b6fe7471b 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -50,7 +50,8 @@ class Collection extends Base */ public function __construct($service, $class, array $array = array()) { - $service->getLogger()->deprecated(__METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder'); + $service->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); $this->setService($service); diff --git a/lib/OpenCloud/Common/Collection/ResourceIterator.php b/lib/OpenCloud/Common/Collection/ResourceIterator.php index 627ed78eb..a95455386 100644 --- a/lib/OpenCloud/Common/Collection/ResourceIterator.php +++ b/lib/OpenCloud/Common/Collection/ResourceIterator.php @@ -223,7 +223,8 @@ public function getElement($offset) */ public function first() { - Logger::newInstance()->deprecated(__METHOD__, 'getElement'); + Logger::newInstance()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'getElement')); return $this->getElement(0); } diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index 7eb48cdc0..56cb7cbf2 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -239,6 +239,10 @@ private function dispatch($message, $context) } } + /** + * @deprecated use warning for deprecated messages + * @see http://www.php-fig.org/psr/psr-3/ + */ public function deprecated($method, $new) { $string = sprintf('The %s method is deprecated, please use %s instead', $method, $new); diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index bd0742bcb..080dbb732 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -621,7 +621,8 @@ public function enableConnectionLogging($bool) */ public function connectionLogging() { - $this->getLogger()->deprecated(__METHOD__, 'hasConnectionLogging or enableConnectionLogging'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasConnectionLogging or enableConnectionLogging')); } /** @@ -663,7 +664,8 @@ public function enableContentCaching($bool) */ public function contentCaching() { - $this->getLogger()->deprecated(__METHOD__, 'hasContentCaching or setContentCaching'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasContentCaching or setContentCaching')); } /** diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 8ba659f87..c11022114 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -62,7 +62,8 @@ public function loadBalancerList($detail = true, array $filter = array()) */ public function billableLoadBalancer($id = null) { - $this->getLogger()->deprecated(__METHOD__, 'loadBalancer'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'loadBalancer')); return $this->resource('LoadBalancer', $id); } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index a2d01a479..ce3446880 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -174,7 +174,8 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ */ public function bulkDelete(array $paths) { - $this->getLogger()->deprecated(__METHOD__, '::batchDelete()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, '::batchDelete()')); return $this->executeBatchDeleteRequest($paths); } diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 25e576959..7f3c33a5e 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -165,7 +165,8 @@ public function getTokenObject() */ public function setExpiration($expiration) { - $this->getLogger()->deprecated(__METHOD__, '::getTokenObject()->setExpires()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->setExpires()')); if ($this->getTokenObject()) { $this->getTokenObject()->setExpires($expiration); } @@ -178,7 +179,8 @@ public function setExpiration($expiration) */ public function getExpiration() { - $this->getLogger()->deprecated(__METHOD__, '::getTokenObject()->getExpires()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->getExpires()')); if ($this->getTokenObject()) { return $this->getTokenObject()->getExpires(); } @@ -290,7 +292,8 @@ public function getLogger() */ public function hasExpired() { - $this->getLogger()->deprecated(__METHOD__, 'getTokenObject()->hasExpired()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'getTokenObject()->hasExpired()')); return $this->getTokenObject() && $this->getTokenObject()->hasExpired(); } From c8e2de19afe41cfe98ebc2e0a64b0060c13d1701 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 17:41:36 +0100 Subject: [PATCH 367/835] move deprecation message to static function --- lib/OpenCloud/Common/Collection.php | 5 +++-- lib/OpenCloud/Common/Collection/ResourceIterator.php | 3 +-- lib/OpenCloud/Common/Log/Logger.php | 8 +++----- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 4 ++-- lib/OpenCloud/LoadBalancer/Service.php | 4 ++-- lib/OpenCloud/ObjectStore/Service.php | 4 ++-- lib/OpenCloud/OpenStack.php | 10 ++++------ tests/OpenCloud/Tests/Common/Log/LoggerTest.php | 8 ++++++++ 8 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index b6fe7471b..f15e83c47 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -17,6 +17,8 @@ namespace OpenCloud\Common; +use OpenCloud\Common\Log\Logger; + /** * @deprecated * @codeCoverageIgnore @@ -50,8 +52,7 @@ class Collection extends Base */ public function __construct($service, $class, array $array = array()) { - $service->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); + $service->getLogger()->warning(Logger::deprecated(__METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); $this->setService($service); diff --git a/lib/OpenCloud/Common/Collection/ResourceIterator.php b/lib/OpenCloud/Common/Collection/ResourceIterator.php index a95455386..c0054b8d1 100644 --- a/lib/OpenCloud/Common/Collection/ResourceIterator.php +++ b/lib/OpenCloud/Common/Collection/ResourceIterator.php @@ -223,8 +223,7 @@ public function getElement($offset) */ public function first() { - Logger::newInstance()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'getElement')); + Logger::newInstance()->warning(Logger::deprecated(__METHOD__, 'getElement')); return $this->getElement(0); } diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index 56cb7cbf2..b054fc004 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -240,13 +240,11 @@ private function dispatch($message, $context) } /** - * @deprecated use warning for deprecated messages + * Helper method, use PSR-3 warning function for deprecation warnings * @see http://www.php-fig.org/psr/psr-3/ */ - public function deprecated($method, $new) + public static function deprecated($method, $new) { - $string = sprintf('The %s method is deprecated, please use %s instead', $method, $new); - - return $this->warning($string); + return sprintf('The %s method is deprecated, please use %s instead', $method, $new); } } diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 080dbb732..cc3e85339 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -18,6 +18,7 @@ namespace OpenCloud\LoadBalancer\Resource; use OpenCloud\Common\Exceptions; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Resource\PersistentResource; use OpenCloud\DNS\Resource\HasPtrRecordsInterface; use OpenCloud\LoadBalancer\Enum\NodeCondition; @@ -621,8 +622,7 @@ public function enableConnectionLogging($bool) */ public function connectionLogging() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasConnectionLogging or enableConnectionLogging')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'hasConnectionLogging or enableConnectionLogging')); } /** diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index c11022114..93905a0d2 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -17,6 +17,7 @@ namespace OpenCloud\LoadBalancer; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\NovaService; /** @@ -62,8 +63,7 @@ public function loadBalancerList($detail = true, array $filter = array()) */ public function billableLoadBalancer($id = null) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'loadBalancer')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'loadBalancer')); return $this->resource('LoadBalancer', $id); } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index ce3446880..66735dea8 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -24,6 +24,7 @@ use OpenCloud\Common\Exceptions\InvalidArgumentError; use OpenCloud\Common\Http\Client; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\ServiceBuilder; use OpenCloud\ObjectStore\Constants\UrlType; use OpenCloud\ObjectStore\Resource\Container; @@ -174,8 +175,7 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ */ public function bulkDelete(array $paths) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, '::batchDelete()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::batchDelete()')); return $this->executeBatchDeleteRequest($paths); } diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 7f3c33a5e..6eced81cc 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -23,6 +23,7 @@ use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Http\Message\RequestSubscriber; use OpenCloud\Common\Lang; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\Catalog; use OpenCloud\Common\Service\ServiceBuilder; use OpenCloud\Identity\Resource\Tenant; @@ -165,8 +166,7 @@ public function getTokenObject() */ public function setExpiration($expiration) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->setExpires()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->setExpires()')); if ($this->getTokenObject()) { $this->getTokenObject()->setExpires($expiration); } @@ -179,8 +179,7 @@ public function setExpiration($expiration) */ public function getExpiration() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->getExpires()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->getExpires()')); if ($this->getTokenObject()) { return $this->getTokenObject()->getExpires(); } @@ -292,8 +291,7 @@ public function getLogger() */ public function hasExpired() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'getTokenObject()->hasExpired()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'getTokenObject()->hasExpired()')); return $this->getTokenObject() && $this->getTokenObject()->hasExpired(); } diff --git a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php index da06da6d0..c18f38c62 100644 --- a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php +++ b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php @@ -85,4 +85,12 @@ public function testOutputFailsWithIncorrectFile() $this->logger->emergency('Can anyone see this?'); } + + public function testDeprecationMessage() + { + $this->assertEquals( + 'The OpenCloud\Tests\Common\Log\LoggerTest::testDeprecationMessage method is deprecated, please use testMethod instead', + $this->logger->deprecated(__METHOD__, 'testMethod') + ); + } } From 1b9f7dea7cc43a468953f3aceeb3c2be8441327f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 7 Jan 2015 04:46:24 -0800 Subject: [PATCH 368/835] Setting version for release. --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index 061ac53d9..3e8504cfb 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.12.1'; + const VERSION = '1.13.0-alpha1'; /** * @return string Indicate current SDK version. From 39c5b7cfc4b6269499f474a1aad8d75f2eecdef3 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 7 Jan 2015 10:47:14 +0100 Subject: [PATCH 369/835] + inject Logger in service when available in Client * allow OpenStack Logger to be set via options array --- lib/OpenCloud/Common/Base.php | 8 +++++ .../Common/Service/CatalogService.php | 6 ++++ lib/OpenCloud/Identity/Service.php | 7 ++++ lib/OpenCloud/OpenStack.php | 12 +++++++ tests/OpenCloud/Tests/OpenStackTest.php | 35 +++++++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/lib/OpenCloud/Common/Base.php b/lib/OpenCloud/Common/Base.php index 3e0bc3dfb..076642a36 100644 --- a/lib/OpenCloud/Common/Base.php +++ b/lib/OpenCloud/Common/Base.php @@ -264,6 +264,14 @@ public function getLogger() return $this->logger; } + /** + * @return bool + */ + public function hasLogger() + { + return (null !== $this->logger); + } + /** * @deprecated */ diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index addcd170b..69e5c659d 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -20,8 +20,10 @@ use Guzzle\Http\ClientInterface; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Url; +use OpenCloud\Common\Base; use OpenCloud\Common\Exceptions; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\OpenStack; use Symfony\Component\EventDispatcher\EventSubscriberInterface; abstract class CatalogService extends AbstractService @@ -71,6 +73,10 @@ abstract class CatalogService extends AbstractService */ public function __construct(ClientInterface $client, $type = null, $name = null, $region = null, $urlType = null) { + if (($client instanceof Base || $client instanceof OpenStack) && $client->hasLogger()) { + $this->setLogger($client->getLogger()); + } + $this->setClient($client); $this->name = $name ? : static::DEFAULT_NAME; diff --git a/lib/OpenCloud/Identity/Service.php b/lib/OpenCloud/Identity/Service.php index 9c90d2811..4e34f1e24 100644 --- a/lib/OpenCloud/Identity/Service.php +++ b/lib/OpenCloud/Identity/Service.php @@ -18,11 +18,13 @@ namespace OpenCloud\Identity; use Guzzle\Http\ClientInterface; +use OpenCloud\Common\Base; use OpenCloud\Common\Collection\PaginatedIterator; use OpenCloud\Common\Collection\ResourceIterator; use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Service\AbstractService; use OpenCloud\Identity\Constants\User as UserConst; +use OpenCloud\OpenStack; /** * Class responsible for working with Rackspace's Cloud Identity service. @@ -40,6 +42,11 @@ class Service extends AbstractService public static function factory(ClientInterface $client) { $identity = new self(); + + if (($client instanceof Base || $client instanceof OpenStack) && $client->hasLogger()) { + $identity->setLogger($client->getLogger()); + } + $identity->setClient($client); $identity->setEndpoint(clone $client->getAuthUrl()); diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 6eced81cc..198472365 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -79,6 +79,10 @@ class OpenStack extends Client public function __construct($url, array $secret, array $options = array()) { + if (isset($options['logger']) && $options['logger'] instanceof LoggerInterface) { + $this->setLogger($options['logger']); + } + $this->setSecret($secret); $this->setAuthUrl($url); @@ -286,6 +290,14 @@ public function getLogger() return $this->logger; } + /** + * @return bool + */ + public function hasLogger() + { + return (null !== $this->logger); + } + /** * @deprecated */ diff --git a/tests/OpenCloud/Tests/OpenStackTest.php b/tests/OpenCloud/Tests/OpenStackTest.php index f38f3777b..8ff20892a 100644 --- a/tests/OpenCloud/Tests/OpenStackTest.php +++ b/tests/OpenCloud/Tests/OpenStackTest.php @@ -184,4 +184,39 @@ public function test_Import_Credentials_Numeric_Tenant() $this->assertEquals('{expiration}', $this->client->getExpiration()); $this->assertEquals($randomNumericTenant, $this->client->getTenant()); } + + public function testLoggerServiceInjection() + { + // Create a new client, pass stub via constructor options argument + $stubLogger = $this->getMock('Psr\Log\NullLogger'); + $client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, $this->credentials, array( + 'logger' => $stubLogger, + )); + $client->addSubscriber(new MockSubscriber()); + + // Test all OpenStack factory methods on proper Logger service injection + $service = $client->objectStoreService('cloudFiles', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $service->getCdnService(); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->computeService('cloudServersOpenStack', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->orchestrationService(null, 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->volumeService('cloudBlockStorage', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->identityService(); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->imageService('cloudImages', 'IAD'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->networkingService(null, 'IAD'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + } } From 8bd0e0b1388f82d2c85626e80016f7c955238fd9 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:22:14 +0100 Subject: [PATCH 370/835] Update getting-started.md added information about injecting `Logger` in the client --- docs/getting-started.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 48f99fb86..f47daf5cf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -55,6 +55,11 @@ You can see in the first example that the constant `Rackspace::US_IDENTITY_ENDPO Rackspace's identity endpoint (`https://identity.api.rackspacecloud.com/v2.0/`). Another difference is that Rackspace uses API key for authentication, whereas OpenStack uses a generic password. +#### 1.2 Logger injection +As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. + +Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. + ### 2. Pick what service you want to use In this case, we want to use the Compute (Nova) service: @@ -175,4 +180,4 @@ $callback = function($server) { $server->waitFor(ServerState::ACTIVE, 600, $callback); ``` So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. \ No newline at end of file +callback function is executed - which in this case just logs some output. From 717b2a393a0752bc51bb5585eefd41a0c7b37456 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:26:47 +0100 Subject: [PATCH 371/835] Update Clients.md also add `Logger` injection to Clients userguide --- docs/userguide/Clients.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 9a7efec8e..70f1a8131 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -11,7 +11,7 @@ Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\R 3. `OpenCloud\OpenStack` 4. `OpenCloud\Rackspace` -## Initializing a client +## 1. Initializing a client ### Rackspace @@ -44,7 +44,29 @@ $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( )); ``` -## Authentication +#### 1.2 Logger injection +As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. + +Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. + +To inject a `LoggerInterface` compatible logger into a new `Client`: + +```php +use Monolog\Logger; +use OpenCloud\OpenStack; + +// create a log channel +$log = new Logger('name'); + +$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' +), array( + 'logger' => $log, +)); +``` + +## 2. Authentication The Client does not automatically authenticate against the API on object creation - it waits for an API call. When this happens, it checks whether the current "token" has expired, and (re-)authenticates if necessary. @@ -56,7 +78,7 @@ $client->authenticate(); If the credentials are incorrect, a `401` error will be returned. If credentials are correct, a `200` status is returned with your Service Catalog. -## Service Catalog +## 3. Service Catalog The Service Catalog is returned on successful authentication, and is composed of all the different API services available to the current tenant. All of this functionality is encapsulated in the `Catalog` object, which allows you greater control and interactivity. @@ -86,7 +108,7 @@ foreach ($catalog->getItems() as $catalogItem) { As you can see, you have access to each Service's name, type and list of endpoints. Each endpoint provides access to the specific region, along with its public and private endpoint URLs. -## Default HTTP headers +## 4. Default HTTP headers To set default HTTP headers: @@ -94,6 +116,6 @@ To set default HTTP headers: $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); ``` -## Other functionality +## 5. Other functionality -For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). \ No newline at end of file +For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). From 29cfa914cc4b7565e44ede96b815116a14ecd461 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:28:26 +0100 Subject: [PATCH 372/835] Link getting-started.md to the clients userguide --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index f47daf5cf..e661e0ce8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -58,7 +58,7 @@ uses API key for authentication, whereas OpenStack uses a generic password. #### 1.2 Logger injection As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. -Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. +Prerequisities and usage example can be found in [the Clients userguide](/docs/userguide/Clients.md#12-logger-injection) ### 2. Pick what service you want to use From a6217b8f242beac89201a7f9c71ef8b3d5a6b4d6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 7 Jan 2015 14:13:30 -0800 Subject: [PATCH 373/835] Using a stable version of the json-patch-php package. --- composer.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 76027703a..9c1752463 100644 --- a/composer.json +++ b/composer.json @@ -25,17 +25,11 @@ "OpenCloud": ["lib/", "tests/"] } }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/ycombinator/json-patch-php" - } - ], "require": { "php" : ">=5.3.3", "guzzle/http" : "~3.8", "psr/log": "~1.0", - "mikemccabe/json-patch-php": "dev-master" + "mikemccabe/json-patch-php": "~0.1" }, "require-dev" : { "phpunit/phpunit": "4.3.*", From f9653481bee48f7d0b29e24d1efbf0472280ba45 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:12:13 +0100 Subject: [PATCH 374/835] Add mock HTTP responses --- .../_response/LoadBalancers1.resp | 80 +++++++++++++++++++ .../_response/LoadBalancers2.resp | 80 +++++++++++++++++++ .../_response/LoadBalancers3.resp | 34 ++++++++ 3 files changed, 194 insertions(+) create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp new file mode 100644 index 000000000..1827f2b44 --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site1", + "id":1, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":3, + "virtualIps":[ + { + "id":403, + "address":"206.55.130.1", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site2", + "id":2, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site3", + "id":3, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp new file mode 100644 index 000000000..2ab7b2381 --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site3", + "id":3, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site4", + "id":4, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site5", + "id":5, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp new file mode 100644 index 000000000..554a4430d --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp @@ -0,0 +1,34 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site5", + "id":5, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file From e5f57b5faef28fbdb72204928b47132696328da7 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:22 +0100 Subject: [PATCH 375/835] Decouple the process of retrieving last element from setting marker --- lib/OpenCloud/Common/Collection/PaginatedIterator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/OpenCloud/Common/Collection/PaginatedIterator.php b/lib/OpenCloud/Common/Collection/PaginatedIterator.php index a0628993d..ef827ac80 100644 --- a/lib/OpenCloud/Common/Collection/PaginatedIterator.php +++ b/lib/OpenCloud/Common/Collection/PaginatedIterator.php @@ -145,7 +145,11 @@ public function updateMarkerToCurrent() } $element = $this->elements[$this->position]; + $this->setMarkerFromElement($element); + } + protected function setMarkerFromElement($element) + { $key = $this->getOption('key.marker'); if (isset($element->$key)) { From 9cd0db166d718fb4ab9e53f280f815c008326133 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:39 +0100 Subject: [PATCH 376/835] Add new iterator to handle differences --- .../Collection/LoadBalancerIterator.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php new file mode 100644 index 000000000..f554c6512 --- /dev/null +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -0,0 +1,43 @@ +getQuery(); + $query['limit'] = $query['limit'] + 1; + $url->setQuery($query); + + return $url; + } + + public function updateMarkerToCurrent() + { + $this->setMarkerFromElement($this->nextElement); + } + + public function parseResponseBody($body) + { + $response = parent::parseResponseBody($body); + + if (count($response) >= $this->getOption('limit.page')) { + // Save last element (we will need it for the next marker) + $this->nextElement = end($response); + + // Since we previously asked for n+1 elements, pop the unwanted element + array_pop($response); + reset($response); + } + + return $response; + } +} \ No newline at end of file From 3f69dfd76398cd446e4fb839c8622de8c9e921cc Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:51 +0100 Subject: [PATCH 377/835] Implement into service and refactor tests --- lib/OpenCloud/LoadBalancer/Service.php | 12 ++++- .../Tests/LoadBalancer/ServiceTest.php | 48 ++++++++++++++----- tests/OpenCloud/Tests/OpenCloudTestCase.php | 13 ++--- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 93905a0d2..6f5052f2c 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -19,6 +19,7 @@ use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\NovaService; +use OpenCloud\LoadBalancer\Collection\LoadBalancerIterator; /** * Class that encapsulates the Rackspace Cloud Load Balancers service @@ -51,11 +52,20 @@ public function loadBalancer($id = null) */ public function loadBalancerList($detail = true, array $filter = array()) { + $options = $this->makeResourceIteratorOptions($this->resolveResourceClass('LoadBalancer')); + + if (isset($filter['limit'])) { + $options['limit.page'] = $filter['limit']; + unset($filter['limit']); + } + $url = $this->getUrl(); $url->addPath(Resource\LoadBalancer::resourceName()); $url->setQuery($filter); - return $this->resourceList('LoadBalancer', $url); + $options += array('baseUrl' => $url, 'key.marker' => 'id'); + + return LoadBalancerIterator::factory($this, $options); } /** diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 6bd047064..2d01e2f93 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -27,15 +27,13 @@ namespace OpenCloud\Tests\LoadBalancer; -class ServiceTest extends \OpenCloud\Tests\OpenCloudTestCase -{ - private $service; - - public function setupObjects() - { - $this->service = $this->getClient()->loadBalancerService('cloudLoadBalancers', 'DFW', 'publicURL'); - } +use Guzzle\Http\Message\Response; +use Guzzle\Plugin\Mock\MockPlugin; +use OpenCloud\Rackspace; +use OpenCloud\Tests\MockSubscriber; +class ServiceTest extends LoadBalancerTestCase +{ public function test__construct() { $this->assertInstanceOf( @@ -52,12 +50,36 @@ public function testLoadBalancer() ); } - public function testLoadBalancerList() + public function test_Listing_Load_Balancers() { - $this->assertInstanceOf( - self::COLLECTION_CLASS, - $this->service->loadBalancerList() - ); + // Load JSON HTTP data + $authData = file_get_contents($this->getTestFilePath('Auth', './')); + $data1 = file_get_contents($this->getTestFilePath('LoadBalancers1')); + $data2 = file_get_contents($this->getTestFilePath('LoadBalancers2')); + $data3 = file_get_contents($this->getTestFilePath('LoadBalancers3')); + + // Populate mock response queue + $mock = new MockPlugin(); + $mock->addResponse(Response::fromMessage($authData)) + ->addResponse(Response::fromMessage($data1)) + ->addResponse(Response::fromMessage($data2)) + ->addResponse(Response::fromMessage($data3)); + + // We need to define our own setup because *jazz hands* + $client = $this->newClient(); + $client->addSubscriber($mock); + $service = $client->loadBalancerService(null, 'IAD'); + + // Ensure that a series of paginated calls return a holistic collection + $lbs = $service->loadBalancerList(false, array('limit' => 2)); + $ids = array(); + foreach ($lbs as $lb) { + $ids[] = $lb->id; + } + + // Check our assumptions + $this->isCollection($lbs); + $this->assertEquals($ids, array(1,2,3,4,5)); } public function testBillableLoadBalancer() diff --git a/tests/OpenCloud/Tests/OpenCloudTestCase.php b/tests/OpenCloud/Tests/OpenCloudTestCase.php index 628881fbb..8d7582c1f 100644 --- a/tests/OpenCloud/Tests/OpenCloudTestCase.php +++ b/tests/OpenCloud/Tests/OpenCloudTestCase.php @@ -38,17 +38,10 @@ abstract class OpenCloudTestCase extends \PHPUnit_Framework_TestCase public function newClient() { - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + return new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => 'foo', 'apiKey' => 'bar' )); - - $client->addSubscriber(new MockSubscriber()); - //$client->addSubscriber(LogPlugin::getDebugPlugin()); - - $client->authenticate(); - - return $client; } public function getClient() @@ -59,6 +52,10 @@ public function getClient() public function setUp() { $this->client = $this->newClient(); + + $this->client->addSubscriber(new MockSubscriber()); + $this->client->authenticate(); + $this->setupObjects(); $this->handleMockSubscribers(); } From 622d76d436ee98fec854f77699f7bf5a8db37fd5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 9 Jan 2015 11:25:08 +0100 Subject: [PATCH 378/835] Appease PSR-2 gods --- lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php | 2 +- tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php index f554c6512..d6326048e 100644 --- a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -40,4 +40,4 @@ public function parseResponseBody($body) return $response; } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 2d01e2f93..9beb0c620 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -79,7 +79,7 @@ public function test_Listing_Load_Balancers() // Check our assumptions $this->isCollection($lbs); - $this->assertEquals($ids, array(1,2,3,4,5)); + $this->assertEquals($ids, array(1, 2, 3, 4, 5)); } public function testBillableLoadBalancer() From 6895f8484bb8cbc855c9b3ea86fe1d1f5efc3bb3 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 9 Jan 2015 11:32:29 +0100 Subject: [PATCH 379/835] Attempt to fix 5.3 segfault --- phpunit.xml.dist | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index cd44e77df..c2aeed2d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -25,4 +25,9 @@ + + + + + From 48e1df45489198b3812d97057a2b4c29fc897232 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 13 Jan 2015 12:36:20 +0100 Subject: [PATCH 380/835] Tweaks based on code review --- .../LoadBalancer/Collection/LoadBalancerIterator.php | 8 ++------ lib/OpenCloud/LoadBalancer/Service.php | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php index d6326048e..7febf1af3 100644 --- a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -30,12 +30,8 @@ public function parseResponseBody($body) $response = parent::parseResponseBody($body); if (count($response) >= $this->getOption('limit.page')) { - // Save last element (we will need it for the next marker) - $this->nextElement = end($response); - - // Since we previously asked for n+1 elements, pop the unwanted element - array_pop($response); - reset($response); + // Pop last element and save (we will need it for the next marker) + $this->nextElement = array_pop($response); } return $response; diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 6f5052f2c..7f8d01f74 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -63,7 +63,7 @@ public function loadBalancerList($detail = true, array $filter = array()) $url->addPath(Resource\LoadBalancer::resourceName()); $url->setQuery($filter); - $options += array('baseUrl' => $url, 'key.marker' => 'id'); + $options = array_merge($options, array('baseUrl' => $url, 'key.marker' => 'id')); return LoadBalancerIterator::factory($this, $options); } From 83025a8244e6a76929aa659aaddc0c54143d615e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 13 Jan 2015 17:28:57 -0800 Subject: [PATCH 381/835] Add comment to clarify intent of code. --- lib/OpenCloud/Common/Service/CatalogItem.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/OpenCloud/Common/Service/CatalogItem.php b/lib/OpenCloud/Common/Service/CatalogItem.php index b55274762..5d0e44d1a 100644 --- a/lib/OpenCloud/Common/Service/CatalogItem.php +++ b/lib/OpenCloud/Common/Service/CatalogItem.php @@ -141,6 +141,8 @@ public function getEndpoints() public function getEndpointFromRegion($region) { foreach ($this->endpoints as $endpoint) { + // Return the endpoint if it is regionless OR if the endpoint's + // region matches the $region supplied by the caller. if (!isset($endpoint->region) || $endpoint->region == $region) { return $endpoint; } From faa1f4b97426a8aba31bb434f325d3fe692b8970 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 14 Jan 2015 03:41:21 -0800 Subject: [PATCH 382/835] Clarifying where to place autoloader require statement. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b05d1d58..42948e8c3 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,15 @@ php composer.phar require rackspace/php-opencloud ``` Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): +namespaces). To do this, place the following line of PHP code at the top of your application's PHP files: ```php require 'vendor/autoload.php'; ``` +**Note**: this assumes your application's PHP files are located in the same folder as `vendor/`. If your files are located +elsewhere, please supply the path to `vendor/autoload.php` in the `require` statement above. + And you're ready to go! You can also check out the [Getting Started guide](docs/getting-started.md) for a quick tutorial. From 8c0e31edacf29f9dd9869c3a8f9a6649ab4eb280 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 14 Jan 2015 12:53:14 -0500 Subject: [PATCH 383/835] Update documentation Document the correct returned objects. --- lib/OpenCloud/Compute/Service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Compute/Service.php b/lib/OpenCloud/Compute/Service.php index d34f6101a..bf8e4a571 100644 --- a/lib/OpenCloud/Compute/Service.php +++ b/lib/OpenCloud/Compute/Service.php @@ -102,7 +102,7 @@ public function server($id = null) * not having all the information you need. * @param array $filter - a set of key/value pairs that is passed to the * servers list for filtering - * @returns \OpenCloud\Common\Collection + * @returns \OpenCloud\Common\Collection\PaginatedIterator */ public function serverList($details = true, array $filter = array()) { @@ -128,7 +128,7 @@ public function network($id = null) * * @api * @param array $filter array of filter key/value pairs - * @return \OpenCloud\Common\Collection + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function networkList($filter = array()) { From 9f1f98dad7065ae18c9beef3657c7d6993811663 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 14 Jan 2015 13:01:54 -0500 Subject: [PATCH 384/835] Change @returns to @return --- lib/OpenCloud/Compute/Service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Compute/Service.php b/lib/OpenCloud/Compute/Service.php index bf8e4a571..37d66a036 100644 --- a/lib/OpenCloud/Compute/Service.php +++ b/lib/OpenCloud/Compute/Service.php @@ -81,7 +81,7 @@ public function __construct(Client $client, $type = null, $name = null, $region * * @api * @param string $id - if specified, the server with the ID is retrieved - * @returns Resource\Server object + * @return Resource\Server object */ public function server($id = null) { @@ -102,7 +102,7 @@ public function server($id = null) * not having all the information you need. * @param array $filter - a set of key/value pairs that is passed to the * servers list for filtering - * @returns \OpenCloud\Common\Collection\PaginatedIterator + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function serverList($details = true, array $filter = array()) { From 8267af859f0c7ca7128cb1f9a3921acbdf7f0885 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 10:42:32 +0100 Subject: [PATCH 385/835] Fix CDN container population when iterating --- lib/OpenCloud/ObjectStore/AbstractService.php | 16 --------- lib/OpenCloud/ObjectStore/CDNService.php | 35 +++++++++++++++++++ lib/OpenCloud/ObjectStore/Service.php | 13 +++++++ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/AbstractService.php b/lib/OpenCloud/ObjectStore/AbstractService.php index 8e24c0b96..8f92874f8 100644 --- a/lib/OpenCloud/ObjectStore/AbstractService.php +++ b/lib/OpenCloud/ObjectStore/AbstractService.php @@ -29,22 +29,6 @@ abstract class AbstractService extends CatalogService const MAX_OBJECT_NAME_LEN = 1024; const MAX_OBJECT_SIZE = 5102410241025; - /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. - * - * @param array $filter - * @return Collection - */ - public function listContainers(array $filter = array()) - { - $filter['format'] = 'json'; - - $class = ($this instanceof Service) ? 'Container' : 'CDNContainer'; - - return $this->resourceList($class, $this->getUrl(null, $filter), $this); - } - /** * @return Resource\Account */ diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index e6b20cd5c..7d38c9956 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -16,6 +16,8 @@ */ namespace OpenCloud\ObjectStore; +use OpenCloud\ObjectStore\Resource\CDNContainer; +use OpenCloud\ObjectStore\Resource\ContainerMetadata; /** * This is the CDN version of the ObjectStore service. @@ -24,4 +26,37 @@ class CDNService extends AbstractService { const DEFAULT_NAME = 'cloudFilesCDN'; const DEFAULT_TYPE = 'rax:object-cdn'; + + /** + * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular + * service, normal containers are returned. + * + * @param array $filter + * @return CDNContainer + */ + public function listContainers(array $filter = array()) + { + $filter['format'] = 'json'; + return $this->resourceList('CDNContainer', $this->getUrl(null, $filter), $this); + } + + public function cdnContainer($data) + { + $container = new CDNContainer($this, $data); + + $metadata = new ContainerMetadata(); + $metadata->setArray(array( + 'Streaming-Uri' => $data->cdn_streaming_uri, + 'Ios-Uri' => $data->cdn_ios_uri, + 'Ssl-Uri' => $data->cdn_ssl_uri, + 'Enabled' => $data->cdn_enabled, + 'Ttl' => $data->ttl, + 'Log-Retention' => $data->log_retention, + 'Uri' => $data->cdn_uri, + )); + + $container->setMetadata($metadata); + + return $container; + } } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index 66735dea8..df9ffa6c8 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -66,6 +66,19 @@ public function getCdnService() return $this->cdnService; } + /** + * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular + * service, normal containers are returned. + * + * @param array $filter + * @return Container + */ + public function listContainers(array $filter = array()) + { + $filter['format'] = 'json'; + return $this->resourceList('Container', $this->getUrl(null, $filter), $this); + } + /** * @param $data * @return Container From 8c0a43ccab333a80bf85293d8b33b9d4320bcfdd Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 10:50:13 +0100 Subject: [PATCH 386/835] Minor fixes --- lib/OpenCloud/ObjectStore/CDNService.php | 6 +++--- lib/OpenCloud/ObjectStore/Service.php | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index 7d38c9956..d45fc494d 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -16,6 +16,7 @@ */ namespace OpenCloud\ObjectStore; + use OpenCloud\ObjectStore\Resource\CDNContainer; use OpenCloud\ObjectStore\Resource\ContainerMetadata; @@ -28,11 +29,10 @@ class CDNService extends AbstractService const DEFAULT_TYPE = 'rax:object-cdn'; /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. + * List CDN-enabled containers. * * @param array $filter - * @return CDNContainer + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function listContainers(array $filter = array()) { diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index df9ffa6c8..f0e1c3dc9 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -67,11 +67,10 @@ public function getCdnService() } /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. + * List all available containers. * * @param array $filter - * @return Container + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function listContainers(array $filter = array()) { From a9f8bdd3b265b7928992acd26f95369512f78357 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 15:13:34 +0100 Subject: [PATCH 387/835] [ci skip] Adding better documentation --- docs/userguide/ObjectStore/CDN/Container.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 3d06216fc..642ee968d 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -21,7 +21,16 @@ $cdn = $container->getCdn(); ## List CDN-enabled container To list CDN-only containers, follow the same operation for Storage which lists all containers. The only difference is -which service object you execute the method on. +which service object you execute the method on: + +```php +$cdnService = $service->getCdnService(); +$cdnContainers = $cdnService->listContainers(); + +foreach ($cdnContainers as $cdnContainer) { + +} +``` ## CDN-enable and -disable a container @@ -66,4 +75,4 @@ To enable and disable logging for your CDN: ```php $cdn->enableCdnLogging(); $cdn->disableCdnLogging(); -``` \ No newline at end of file +``` From d40196e4319eab57fbce163c8126fb3a2804be92 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:24:42 -0800 Subject: [PATCH 388/835] Generate and publish SDK API reference from Travis builds of master branch. --- .travis.yml | 3 +++ composer.json | 3 ++- docs/generate.sh | 50 +++++++++++++++++++++++++++++++----------------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d45afa70..fe792bec4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,6 @@ notifications: - jamie.hannaford@rackspace.com - glen.campbell@rackspace.com - shaunak.kashyap@rackspace.com + +env: + - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" \ No newline at end of file diff --git a/composer.json b/composer.json index 98d7455df..594b980c1 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", - "fabpot/php-cs-fixer": "1.0.*@dev" + "fabpot/php-cs-fixer": "1.0.*@dev", + "apigen/apigen": "~4.0" } } diff --git a/docs/generate.sh b/docs/generate.sh index 01a43a58e..3970fe309 100644 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -1,25 +1,39 @@ -#!/bin/bash -# (c)2013 Rackspace Hosting. See COPYING for license. +#!/bin/sh -DOC_DIR=docs/api -LIB_DIR=lib -BIN_FILE=vendor/bin/apigen.php +# Script to be used by Travis CI builds to generate the php-opencloud SDK API +# reference and publish it to the gh-pages branch of the rackerlabs/php-opencloud +# repository. -if [ ! -f $BIN_FILE ]; then - rm composer.lock - php composer.phar require apigen/apigen:dev-master --dev -fi +SOURCE_DIR=lib +WORK_DIR=build/api +API_DOCS_DIR=docs/api +REPO_REMOTE_URL=https://$GH_TOKEN@github.com/rackspace/php-opencloud -if [ ! -d $DOC_DIR ]; then - mkdir $DOC_DIR +# We want our generated API reference to reflect what is +# on the master branch. So if we aren't currently on +# the master branch, or we aren't part of a PR targetted +# to the master branch, do nothing. +if [ "$TRAVIS_BRANCH" != "master" ]; then + exit 0 fi -if [ ! -d docs ]; then - echo "No docs/ directory found; run this script from the top directory" - exit; -fi +# Generate the API references +rm -rf $API_DOCS_DIR && \ +./vendor/bin/apigen generate \ + --source $SOURCE_DIR \ + --destination $WORK_DIR + +# Switch the branch to gh-pages +git checkout gh-pages + +# Commit the generated API references +rm -rf $API_DOCS_DIR +mv $WORK_DIR $API_DOCS_DIR +git add -f $API_DOCS_DIR +git commit -m "Re-generated API documentation" -rm -rf DOCS_DIR +# Push to the remote gh-pages branch so +# changes show up on php-opencloud.com +git push $REPO_REMOTE_URL gh-pages -# regenerate all the docs! -php $BIN_FILE -s $LIB_DIR -d $DOC_DIR --title="PHP OpenCloud API" --groups="namespaces" --download --progressbar \ No newline at end of file +git checkout master From 9593334de68b44c5888b8ded47e4f5f497fca399 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:46:19 -0800 Subject: [PATCH 389/835] Using an older version of apigen that works with PHP 5.3. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 594b980c1..83d4420b3 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,6 @@ "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", - "apigen/apigen": "~4.0" + "apigen/apigen": "~2.8" } } From 1cf864f6f2b5625138f5b047062356d3551aa430 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:59:52 -0800 Subject: [PATCH 390/835] Making generation+publish script executable. --- docs/generate.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 docs/generate.sh diff --git a/docs/generate.sh b/docs/generate.sh old mode 100644 new mode 100755 From 3eb8aae512a9ee9c205063dab02123bec60daa12 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:11:02 -0800 Subject: [PATCH 391/835] Pull in latest changes before making more changes. --- docs/generate.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generate.sh b/docs/generate.sh index 3970fe309..ed06cb3a6 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -25,6 +25,7 @@ rm -rf $API_DOCS_DIR && \ # Switch the branch to gh-pages git checkout gh-pages +git pull $REPO_REMOTE_URL gh-pages # Commit the generated API references rm -rf $API_DOCS_DIR From 50d90905ec5eff0167a226fe841ab4eaf41fb7db Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:14:17 -0800 Subject: [PATCH 392/835] Adding error checking if the pull/merge failed. --- docs/generate.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/generate.sh b/docs/generate.sh index ed06cb3a6..a27c6e404 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -27,6 +27,10 @@ rm -rf $API_DOCS_DIR && \ git checkout gh-pages git pull $REPO_REMOTE_URL gh-pages +if [ $? -ne 0 ]; then + exit 1 +fi + # Commit the generated API references rm -rf $API_DOCS_DIR mv $WORK_DIR $API_DOCS_DIR From 6507b807fb4f4b6ad83364a97c7fd5680516cd51 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:20:58 -0800 Subject: [PATCH 393/835] Attempt merge commit if necessary. --- docs/generate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generate.sh b/docs/generate.sh index a27c6e404..d7965bfb7 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -25,7 +25,7 @@ rm -rf $API_DOCS_DIR && \ # Switch the branch to gh-pages git checkout gh-pages -git pull $REPO_REMOTE_URL gh-pages +git pull --commit $REPO_REMOTE_URL gh-pages if [ $? -ne 0 ]; then exit 1 From 419363ec8c7adb7e8097aa5658b99c12db69e2d4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 23 Jan 2015 03:48:41 -0800 Subject: [PATCH 394/835] Removing unnecessary line. --- docs/generate.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/generate.sh b/docs/generate.sh index d7965bfb7..2c88f3eca 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -18,7 +18,6 @@ if [ "$TRAVIS_BRANCH" != "master" ]; then fi # Generate the API references -rm -rf $API_DOCS_DIR && \ ./vendor/bin/apigen generate \ --source $SOURCE_DIR \ --destination $WORK_DIR From deea831cfd027dacb875341472906943aa9f383f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 23 Jan 2015 04:17:32 -0800 Subject: [PATCH 395/835] Adding apigen config file. --- apigen.neon | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 apigen.neon diff --git a/apigen.neon b/apigen.neon new file mode 100644 index 000000000..1907d65f4 --- /dev/null +++ b/apigen.neon @@ -0,0 +1,3 @@ +templateTheme: bootstrap + +accessLevels: [public] From d94547f70eef20654e561418df9cd1e37903ba57 Mon Sep 17 00:00:00 2001 From: Glen Campbell Date: Sat, 7 Feb 2015 11:25:26 -0600 Subject: [PATCH 396/835] Docs said 'COMPLETE' but code uses 'COMPLETED' --- docs/userguide/DNS/Domains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index 827020256..f0d94efee 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -90,7 +90,7 @@ This call provides the BIND (Berkeley Internet Name Domain) 9 formatted contents ```php $asyncResponse = $domain->export(); -$body = $asyncResponse->waitFor('COMPLETE'); +$body = $asyncResponse->waitFor('COMPLETED'); echo $body['contents']; ``` From af23542b221adddeed201cf06491eaf55ed09f1b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 14:20:34 +0100 Subject: [PATCH 397/835] Adding explanation for prefixing user-agents --- docs/userguide/Clients.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 70f1a8131..6f1e35075 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -116,6 +116,26 @@ To set default HTTP headers: $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); ``` +## User agents + +php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: + +> OpenCloud/xxx cURL/yyy PHP/zzz + +where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: + +```php +$client->setUserAgent('MyCustomUserAgent'); +``` + +If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: + +```php +$client->setUserAgent('MyPrefix', true); +``` + +where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. + ## 5. Other functionality For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). From eb37287a68069ea03ef10f8eed204cac9744edf4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 15:03:22 +0100 Subject: [PATCH 398/835] Install entire Guzzle package --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 83d4420b3..693e986ec 100644 --- a/composer.json +++ b/composer.json @@ -27,12 +27,11 @@ }, "require": { "php" : ">=5.3.3", - "guzzle/http" : "~3.8", + "guzzle/guzzle" : "~3.8", "psr/log": "~1.0" }, "require-dev" : { "phpunit/phpunit": "4.3.*", - "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", From 11ed0ad8e4662667049c73cbadb6aa726dfaafcb Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 20:25:43 +0100 Subject: [PATCH 399/835] Add example outputs --- docs/userguide/Clients.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 6f1e35075..c7270d69e 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -120,7 +120,7 @@ $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: -> OpenCloud/xxx cURL/yyy PHP/zzz +> User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: @@ -128,12 +128,20 @@ where `xxx` is the current version of the SDK, `yyy` is the current version of c $client->setUserAgent('MyCustomUserAgent'); ``` +which will result in: + +> User-Agent: MyCustomUserAgent + If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: ```php $client->setUserAgent('MyPrefix', true); ``` +which will result in: + +> User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. ## 5. Other functionality From 43f4fa3388b051f8e3ffc10e76516f2b182244b2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 25 Feb 2015 15:17:22 -0800 Subject: [PATCH 400/835] Return array of DataObjects if such is requested. --- .../ObjectStore/Resource/Container.php | 23 +++++++++++++++---- .../ObjectStore/Resource/ContainerTest.php | 14 +++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 18e541bc7..ef8f27e68 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -30,6 +30,7 @@ use OpenCloud\ObjectStore\Exception\ObjectNotFoundException; use OpenCloud\ObjectStore\Upload\DirectorySync; use OpenCloud\ObjectStore\Upload\TransferBuilder; +use OpenCloud\ObjectStore\Enum\ReturnType; /** * A container is a storage compartment for your data and provides a way for you @@ -475,7 +476,7 @@ public function uploadObject($name, $data, array $headers = array()) * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError * @return \Guzzle\Http\Message\Response */ - public function uploadObjects(array $files, array $commonHeaders = array()) + public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY) { $requests = $entities = array(); @@ -514,11 +515,23 @@ public function uploadObjects(array $files, array $commonHeaders = array()) $responses = $this->getClient()->send($requests); - foreach ($entities as $entity) { - $entity->close(); + if (ReturnType::RESPONSE_ARRAY === $returnType) { + foreach ($entities as $entity) { + $entity->close(); + } + return $responses; + } else { + // Convert responses to DataObjects before returning + $dataObjects = array(); + foreach ($responses as $index => $response) { + $dataObject = $this->dataObject() + ->populateFromResponse($response) + ->setName($files[$index]['name']) + ->setContent($entities[$index]); + $dataObjects[] = $dataObject; + } + return $dataObjects; } - - return $responses; } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 2ada2c41c..5e3171e6e 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -20,6 +20,7 @@ use Guzzle\Http\Message\Response; use OpenCloud\Common\Constants\Size; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; +use OpenCloud\ObjectStore\Enum\ReturnType; class ContainerTest extends ObjectStoreTestCase { @@ -225,6 +226,19 @@ public function test_Upload_Multiple() )); } + public function test_Upload_Multiple_Return_DataObject_Array() + { + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test', 'body' => 'FOOBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); + $this->assertEquals('test', $dataObjects[0]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[0]->getContent()); + $this->assertEquals('FOOBAR', (string) $dataObjects[0]->getContent()); + } + public function test_Upload() { $this->assertInstanceOf( From 7b05d2f8c555f134d839b6726441d8f73db3546d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 25 Feb 2015 16:35:05 -0800 Subject: [PATCH 401/835] Adding documentation. --- docs/userguide/ObjectStore/USERGUIDE.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 8bb3f3d45..fa04bdeaf 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -198,7 +198,8 @@ $localFileName = '/path/to/local/php-elephant.jpg'; $remoteFileName = 'php-elephant.jpg'; $fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); +$object = $container->uploadObject($remoteFileName, $fileData); +/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object.php) ] @@ -258,7 +259,10 @@ $objects = array( a ); -$container->uploadObjects($objects); +$responses = $container->uploadObjects($objects); +foreach ($responses as $response) { + /** @var $response \Guzzle\Http\Message\Response **/ +} ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects.php) ] @@ -266,7 +270,7 @@ In the above example, the contents of two files present on the local filesystem Instead of specifying the `path` key in an element of the `$objects` array, you can specify a `body` key whose value is a string or a stream representation. -Finally, you can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. +You can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. ``` $metadata = array('author' => 'Jane Doe'); @@ -281,6 +285,17 @@ $container->uploadObjects($objects, $allHeaders); In the example above, every object referenced within the `$objects` array will be uploaded with the same metadata. +Finally, if you want the `uploadObjects` method to return an array of `OpenCloud\ObjectStore\Resource\DataObject` objects instead of an array of `Guzzle\Http\Message\Response` objects, you can specify a third parameter to the `uploadObjects` method: + +``` +use OpenCloud\ObjectStore\Enum\ReturnType; + +$dataObjects = $container->uploadObjects($objects, $allHeaders, ReturnType::DATA_OBJECT_ARRAY); +foreach ($dataObjects as $dataObject) { + /** @var $dataObject OpenCloud\ObjectStore\Resource\DataObject **/ +} +``` + ### Large Objects If you want to upload objects larger than 5GB in size, you must use a different upload process. From 81004923d7a50ef83dc5d51b7753b7c1a0b554a3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 05:57:35 -0800 Subject: [PATCH 402/835] Adding more test data. --- .../ObjectStore/Resource/ContainerTest.php | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 5e3171e6e..e5e002eda 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -228,15 +228,38 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { - $container = $this->container; + $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); + echo $tempFileName; + try { + $tempFile = fopen($tempFileName, 'w+'); + fwrite($tempFile, 'BAZQUX'); + + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test1', 'body' => 'FOOBAR'), + array('name' => 'test2', 'path' => $tempFileName), + array('name' => 'test2', 'body' => 'BARBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + } finally { + fclose($tempFile); + unlink($tempFileName); + } - $dataObjects = $container->uploadObjects(array( - array('name' => 'test', 'body' => 'FOOBAR') - ), array(), ReturnType::DATA_OBJECT_ARRAY); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); - $this->assertEquals('test', $dataObjects[0]->getName()); + $this->assertEquals('test1', $dataObjects[0]->getName()); $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[0]->getContent()); $this->assertEquals('FOOBAR', (string) $dataObjects[0]->getContent()); + + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[1]); + $this->assertEquals('test2', $dataObjects[1]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[1]->getContent()); + $this->assertEquals('BAZQUX', (string) $dataObjects[1]->getContent()); + + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[2]); + $this->assertEquals('test2', $dataObjects[2]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[2]->getContent()); + $this->assertEquals('BARBAR', (string) $dataObjects[2]->getContent()); } public function test_Upload() From 16d493a671843d0328e4a74ed3f454ed1f525ed3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:14:30 -0800 Subject: [PATCH 403/835] Adding inline documentation. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index ef8f27e68..edcea7397 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -472,9 +472,12 @@ public function uploadObject($name, $data, array $headers = array()) * `path' Path to an existing file, OR * `body' Either a string or stream representation of the file contents to be uploaded. * @param array $headers Optional headers that will be sent with the request (useful for object metadata). + * @param string $returnType One of OpenCloud\ObjectStore\Enum\ReturnType::RESPONSE_ARRAY (to return an array of + * Guzzle\Http\Message\Response objects) or OpenCloud\ObjectStore\Enum\ReturnType::DATA_OBJECT_ARRAY + * (to return an array of OpenCloud\ObjectStore\Resource\DataObject objects). * * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError - * @return \Guzzle\Http\Message\Response + * @return Guzzle\Http\Message\Response[] or OpenCloud\ObjectStore\Resource\DataObject[] depending on $returnType */ public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY) { From 872e8010f5051b41cd084c517ee9649c21a09488 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:14:52 -0800 Subject: [PATCH 404/835] Oops. Forgot to check-in this file! --- lib/OpenCloud/ObjectStore/Enum/ReturnType.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 lib/OpenCloud/ObjectStore/Enum/ReturnType.php diff --git a/lib/OpenCloud/ObjectStore/Enum/ReturnType.php b/lib/OpenCloud/ObjectStore/Enum/ReturnType.php new file mode 100644 index 000000000..ac9193ed4 --- /dev/null +++ b/lib/OpenCloud/ObjectStore/Enum/ReturnType.php @@ -0,0 +1,29 @@ + Date: Thu, 26 Feb 2015 06:15:57 -0800 Subject: [PATCH 405/835] Condensing to one line. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index edcea7397..0f7b0df08 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -527,11 +527,10 @@ public function uploadObjects(array $files, array $commonHeaders = array(), $ret // Convert responses to DataObjects before returning $dataObjects = array(); foreach ($responses as $index => $response) { - $dataObject = $this->dataObject() - ->populateFromResponse($response) - ->setName($files[$index]['name']) - ->setContent($entities[$index]); - $dataObjects[] = $dataObject; + $dataObjects[] = $this->dataObject() + ->populateFromResponse($response) + ->setName($files[$index]['name']) + ->setContent($entities[$index]); } return $dataObjects; } From 6d546ca8506d60e96592bd9ff1bbab595fed12f2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:16:09 -0800 Subject: [PATCH 406/835] Remove debugging statement. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index e5e002eda..12daa574f 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -229,7 +229,6 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); - echo $tempFileName; try { $tempFile = fopen($tempFileName, 'w+'); fwrite($tempFile, 'BAZQUX'); From 22aabafc9b898936342f1a8648f1d7c3b65667ff Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 06:22:06 -0800 Subject: [PATCH 407/835] Adding catch block so this works with PHP 5.4. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 12daa574f..11786c4dc 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -240,6 +240,8 @@ public function test_Upload_Multiple_Return_DataObject_Array() array('name' => 'test2', 'path' => $tempFileName), array('name' => 'test2', 'body' => 'BARBAR') ), array(), ReturnType::DATA_OBJECT_ARRAY); + } catch (Exception $e) { + throw $e; } finally { fclose($tempFile); unlink($tempFileName); From f8f3012a848bb869af8b977caaac3c43440f3ed7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 07:33:35 -0800 Subject: [PATCH 408/835] Adding directive to let Travis builds finish fast. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index fe792bec4..bdd5f76af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ sudo: false matrix: allow_failures: - php: hhvm + fast_finish: true branches: only: From 3ae1c7077fc3a22e95199d468f2d16acddb4b86a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 07:38:48 -0800 Subject: [PATCH 409/835] Duh! PHP5.4 and below don't support finally at all. Removing altogether :( --- .../ObjectStore/Resource/ContainerTest.php | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 11786c4dc..b5d2bc602 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -229,23 +229,19 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); - try { - $tempFile = fopen($tempFileName, 'w+'); - fwrite($tempFile, 'BAZQUX'); - - $container = $this->container; - - $dataObjects = $container->uploadObjects(array( - array('name' => 'test1', 'body' => 'FOOBAR'), - array('name' => 'test2', 'path' => $tempFileName), - array('name' => 'test2', 'body' => 'BARBAR') - ), array(), ReturnType::DATA_OBJECT_ARRAY); - } catch (Exception $e) { - throw $e; - } finally { - fclose($tempFile); - unlink($tempFileName); - } + + $tempFile = fopen($tempFileName, 'w+'); + fwrite($tempFile, 'BAZQUX'); + + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test1', 'body' => 'FOOBAR'), + array('name' => 'test2', 'path' => $tempFileName), + array('name' => 'test2', 'body' => 'BARBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + fclose($tempFile); + unlink($tempFileName); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); $this->assertEquals('test1', $dataObjects[0]->getName()); From f6ab60d588f7020caa133e7f53909ab2a9cc6209 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:02:14 +0100 Subject: [PATCH 410/835] Add gitignore to prevent builds from being pushed --- doc/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/.gitignore diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..69fa449dd --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +_build/ From a7d062e2928dadc00acaf458d08e01972b014a78 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:02:47 +0100 Subject: [PATCH 411/835] Add basic config and make scripts --- doc/Makefile | 177 +++++++++++++++++++++++++++++++++++++ doc/conf.py | 63 ++++++++++++++ doc/make.bat | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 doc/Makefile create mode 100644 doc/conf.py create mode 100644 doc/make.bat diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..b7805cd67 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/php-opencloud.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/php-opencloud.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/php-opencloud" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/php-opencloud" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000..7f9e29414 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# +# php-opencloud documentation build configuration file, created by +# sphinx-quickstart on Tue Mar 3 12:28:19 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'php-opencloud' +copyright = u'2015, Jamie Hannaford, Shaunak Kashyap' +version = '1.12' +release = '1.12.1' +exclude_patterns = ['_build'] +pygments_style = 'sphinx' +html_theme = 'default' + +if not on_rtd: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "_templates"] + +html_static_path = ['_static'] +html_use_index = True + +# Output file base name for HTML help builder. +htmlhelp_basename = 'php-openclouddoc' + +latex_documents = [ + ('index', 'php-opencloud.tex', u'php-opencloud Documentation', + u'Jamie Hannaford, Shaunak Kashyap', 'manual'), +] + +man_pages = [ + ('index', 'php-opencloud', u'php-opencloud Documentation', + [u'Jamie Hannaford, Shaunak Kashyap'], 1) +] + +texinfo_documents = [ + ('index', 'php-opencloud', u'php-opencloud Documentation', + u'Jamie Hannaford, Shaunak Kashyap', 'php-opencloud', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 000000000..219d9213f --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\php-opencloud.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\php-opencloud.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end From 028f12d47524e9611a801df3df8d645262082240 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:03:16 +0100 Subject: [PATCH 412/835] Add initial AutoScale --- doc/services/autoscale/group-config.rst | 63 ++++++++++++++++ doc/services/autoscale/groups.rst | 90 +++++++++++++++++++++++ doc/services/autoscale/index.rst | 51 +++++++++++++ doc/services/autoscale/policies.rst | 83 +++++++++++++++++++++ doc/services/autoscale/service.sample.rst | 7 ++ doc/services/autoscale/webhooks.rst | 64 ++++++++++++++++ 6 files changed, 358 insertions(+) create mode 100644 doc/services/autoscale/group-config.rst create mode 100644 doc/services/autoscale/groups.rst create mode 100644 doc/services/autoscale/index.rst create mode 100644 doc/services/autoscale/policies.rst create mode 100644 doc/services/autoscale/service.sample.rst create mode 100644 doc/services/autoscale/webhooks.rst diff --git a/doc/services/autoscale/group-config.rst b/doc/services/autoscale/group-config.rst new file mode 100644 index 000000000..a0d7c98f7 --- /dev/null +++ b/doc/services/autoscale/group-config.rst @@ -0,0 +1,63 @@ +Group configurations +==================== + +.. contents:: + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with the functionality of a group's configuration, +you must first retrieve the details of the group itself. To do this, you must +substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get group configuration +----------------------- + +.. code-block:: php + + /** @var */ + $groupConfig = $group->getGroupConfig(); + + +Edit group configuration +------------------------ + +.. code-block:: php + + $groupConfig->update(array( + 'name' => 'New name!' + )); + + +Get launch configuration +------------------------ + +.. code-block:: php + + /** @var */ + $launchConfig = $group->getLaunchConfig(); + + +Edit group/launch configuration +------------------------------- + +.. code-block:: php + + $launchConfig = $group->getLaunchConfig(); + + $server = $launchConfig->args->server; + $server->name = "BRAND NEW SERVER NAME"; + + $launchConfig->update(array + 'args' => array( + 'server' => $server, + 'loadBalancers' => $launchConfig->args->loadBalancers + ) + )); diff --git a/doc/services/autoscale/groups.rst b/doc/services/autoscale/groups.rst new file mode 100644 index 000000000..8e781a2aa --- /dev/null +++ b/doc/services/autoscale/groups.rst @@ -0,0 +1,90 @@ +Groups +====== + +.. contents:: + + +Setup +----- + +.. include:: service.sample.rst + + +List all groups +--------------- + +.. code-block:: php + + $groups = $service->groupList(); + foreach ($group as $group) { + /** @var $group OpenCloud\Autoscale\Resources\Group */ + } + +Please consult the `iterator guide `__ for more information about +iterators. + + +Retrieve group by ID +-------------------- + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Create a new group +------------------ + +.. code-block:: php + + // Set the config object for this autoscale group; contains all of properties + // which determine its behaviour + $groupConfig = array( + 'name' => 'new_autoscale_group', + 'minEntities' => 5, + 'maxEntities' => 25, + 'cooldown' => 60, + ); + + // We need specify what is going to be launched. For now, we'll launch a new server + $launchConfig = array( + 'type' => 'launch_server', + 'args' => array( + 'server' => array( + 'flavorRef' => 3, + 'name' => 'webhead', + 'imageRef' => '0d589460-f177-4b0f-81c1-8ab8903ac7d8' + ), + 'loadBalancers' => array( + array('loadBalancerId' => 2200, 'port' => 8081), + ) + ) + ); + + // Do we want particular scaling policies? + $policy = array( + 'name' => 'scale up by 10', + 'change' => 10, + 'cooldown' => 5, + 'type' => 'webhook', + ); + + $group->create(array( + 'groupConfiguration' => $groupConfig, + 'launchConfiguration' => $launchConfig, + 'scalingPolicies' => array($policy), + )); + +Delete a group +-------------- + +.. code-block:: php + + $group->delete(); + +Get the current state of the scaling group +------------------------------------------ + +.. code-block:: php + + $group->getState(); diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst new file mode 100644 index 000000000..3077a0a2c --- /dev/null +++ b/doc/services/autoscale/index.rst @@ -0,0 +1,51 @@ +Auto Scale v2 +============= + +.. toctree:: + + groups + group-config + policies + webhooks + +Glossary +-------- + +.. glossary:: + + group + The scaling group is at the heart of an Auto Scale deployment. The scaling + group specifies the basic elements of the Auto Scale configuration. It + manages how many servers can participate in the scaling group. It also + specifies information related to load balancers if your configuration uses + a load balancer. + + group configuration + Outlines the basic elements of the Auto Scale configuration. The group + configuration manages how many servers can participate in the scaling group. + It sets a minimum and maximum limit for the number of entities that can be + used in the scaling process. It also specifies information related to load + balancers. + + launch configuration + Creates a blueprint for how new servers will be created. The launch + configuration specifies what type of server image will be started on + launch, what flavor the new server is, and which load balancer the new + server connects to. + + policy + Auto Scale uses policies to define the scaling activity that will take + place, as well as when and how that scaling activity will take place. + Scaling policies specify how to modify the scaling group and its behavior. + You can specify multiple policies to manage a scaling group. + + webhook + A webhook is a reachable endpoint that when visited will execute a scaling + policy for a particular scaling group. + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ + - `API release history `_ diff --git a/doc/services/autoscale/policies.rst b/doc/services/autoscale/policies.rst new file mode 100644 index 000000000..f5d2605f4 --- /dev/null +++ b/doc/services/autoscale/policies.rst @@ -0,0 +1,83 @@ +Scaling Policies +================ + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with the functionality of a group's scaling +policies, you must first retrieve the details of the group itself. To do this, +you must substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get all policies +---------------- + +.. code-block:: php + + $policies = $group->getScalingPolicies(); + + foreach ($policies as $policy) { + printf("Name: %s Type: %s\n", $policy->name, $policy->type); + } + + +Create new scaling policies +--------------------------- + +Creating policies is achieved through passing an array to the ``create`` +method. + +.. code-block:: php + + $policies = array( + array( + 'name' => 'NEW NAME', + 'change' => 1, + 'cooldown' => 150, + 'type' => 'webhook', + ) + ); + + $group->createScalingPolicies($policies); + + +Get an existing scaling policy +------------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + + +Update a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->update(array( + 'name' => 'More relevant name' + )); + + +Delete a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->delete(); + +Execute a scaling policy +------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->execute(); diff --git a/doc/services/autoscale/service.sample.rst b/doc/services/autoscale/service.sample.rst new file mode 100644 index 000000000..7e5d166ab --- /dev/null +++ b/doc/services/autoscale/service.sample.rst @@ -0,0 +1,7 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); diff --git a/doc/services/autoscale/webhooks.rst b/doc/services/autoscale/webhooks.rst new file mode 100644 index 000000000..68b16b0dc --- /dev/null +++ b/doc/services/autoscale/webhooks.rst @@ -0,0 +1,64 @@ +Webhooks +======== + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with webhooks, you must first retrieve the +details of the group and scaling policy you want to execute: + +.. code-block:: php + + $group = $service->group('{groupId}'); + $policy = $group->getScalingPolicy('{policyId}'); + +Get all webhooks +---------------- + +.. code-block:: php + + $webhooks = $policy->getWebookList(); + +Create a new webhook +-------------------- + +.. code-block:: php + + $policy->createWebhooks(array( + array( + 'name' => 'Alice', + 'metadata' => array( + 'firstKey' => 'foo', + 'secondKey' => 'bar' + ) + ) + )); + +Get webhook +----------- + +.. code-block:: php + + $webhook = $policy->getWebhook('{webhookId}'); + +Update webhook +-------------- + +.. code-block:: php + + // Update the metadata + $metadata = $webhook->metadata; + $metadata->thirdKey = 'blah'; + $webhook->update(array( + 'metadata' => $metadata + )); + + +Delete webhook +-------------- + +.. code-block: php + + $webhook->delete(); From 531df7cdb038aeb592d07b9bb6b90084dbf33ac5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:03:38 +0100 Subject: [PATCH 413/835] Add initial Common and Compute --- doc/services/common/os-client.sample.rst | 13 ++ doc/services/common/rs-client.sample.rst | 15 ++ doc/services/compute/flavors.rst | 66 ++++++++ doc/services/compute/images.rst | 87 ++++++++++ doc/services/compute/index.rst | 36 ++++ doc/services/compute/keypairs.rst | 71 ++++++++ doc/services/compute/servers.rst | 202 +++++++++++++++++++++++ doc/services/compute/service.sample.rst | 24 +++ 8 files changed, 514 insertions(+) create mode 100644 doc/services/common/os-client.sample.rst create mode 100644 doc/services/common/rs-client.sample.rst create mode 100644 doc/services/compute/flavors.rst create mode 100644 doc/services/compute/images.rst create mode 100644 doc/services/compute/index.rst create mode 100644 doc/services/compute/keypairs.rst create mode 100644 doc/services/compute/servers.rst create mode 100644 doc/services/compute/service.sample.rst diff --git a/doc/services/common/os-client.sample.rst b/doc/services/common/os-client.sample.rst new file mode 100644 index 000000000..b4614474d --- /dev/null +++ b/doc/services/common/os-client.sample.rst @@ -0,0 +1,13 @@ +.. code-block:: php + + '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/services/common/rs-client.sample.rst b/doc/services/common/rs-client.sample.rst new file mode 100644 index 000000000..e33983270 --- /dev/null +++ b/doc/services/common/rs-client.sample.rst @@ -0,0 +1,15 @@ +The first thing to do is pass in your credentials and instantiate a Rackspace +client: + +.. code-block:: php + + '{username}', + 'apiKey' => '{apiKey}', + )); diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst new file mode 100644 index 000000000..f79caa171 --- /dev/null +++ b/doc/services/compute/flavors.rst @@ -0,0 +1,66 @@ +Flavors +======= + +Setup +----- + +.. include:: service.sample.rst + + +Get a flavor +------------ + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + + +List flavors +------------ + +.. code-block:: php + + $flavors = $service->flavorList(); + + foreach ($flavors as $flavor) { + /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ + } + + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the ``flavorList`` method returns full details on all flavors. +However, because of the overhead involved in retrieving all the details, this +function can be slower than might be expected. To disable this feature and +keep bandwidth at a minimum, just pass ``false`` as the first argument: + +.. code-block:: php + + // Name and ID only + $compute->flavorList(false); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+================================================================================================================================================================================================+ +| minDisk | Filters the list of flavors to those with the specified minimum number of gigabytes of disk storage. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| minRam | Filters the list of flavors to those with the specified minimum amount of RAM in megabytes. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to return all flavors over 4GB in RAM: + +.. code-block:: php + + $flavors = $service->flavorList(true, array('minRam' => 4)); diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst new file mode 100644 index 000000000..b2aae4a65 --- /dev/null +++ b/doc/services/compute/images.rst @@ -0,0 +1,87 @@ +Images +====== + +.. note:: + + **Images on Rackspace servers:** with standard servers, the entire disk + (OS and data) is captured in the image. With Performance servers, only the s + ystem disk is captured in the image. The data disks should be backed up using + Cloud Backup or Cloud Block Storage to ensure availability in case you need + to rebuild or restore a server. + +Setup +----- + +.. include:: service.sample.rst + + +List images +----------- + +Below is the simplest usage for retrieving a list of images: + +.. code-block:: php + + $images = $service->imageList(); + + foreach ($images as $image) { + + } + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the only fields returned in a list call are `id` and `name`, but +you can enable more detailed information to be result by passing in `true` as +the first argument of the call, like so: + +.. code-block:: php + + $images = $service->imageList(true); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+====================================================================================================================================================================================================================================================================================================================================================+ +| server | Filters the list of images by server. Specify the server reference by ID or by full URL. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | Filters the list of images by image name. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| status | Filters the list of images by status. In-flight images have a status of ``SAVING`` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the ``OpenCloud\Compute\Constants\ImageState`` class. Images with an ``ACTIVE`` status are available for use. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| changes-since | Filters the list of images to those that have changed since the changes-since time. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| type | Filters base Rackspace images or any custom server images that you have created. Can either be ``BASE`` or ``SNAPSHOT``. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to filter images for a particular server: + +.. code-block:: php + + $images = $service->imageList(false, array('server' => '{serverId}')); + + +Retrieve details about an image +------------------------------- + +.. code-block:: php + + $image = $service->image('{imageId}'); + + +Delete an image +--------------- + +.. code-block:: php + + $image->delete(); diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst new file mode 100644 index 000000000..94c41979f --- /dev/null +++ b/doc/services/compute/index.rst @@ -0,0 +1,36 @@ +Compute v2 +========== + +.. note:: + + This is a joint service that supports both Rackspace Cloud Servers v2 API, and + OpenStack Nova v2 API. + +.. toctree:: + + images + flavors + servers + keypairs + +Glossary +-------- + +.. glossary:: + + image + An image is a collection of files for a specific operating system that you + use to create or rebuild a server. Rackspace provides prebuilt images. You + can also create custom images from servers that you have launched. + + flavor + A flavor is a named definition of certain server parameters such as + the amount of RAM and disk space available. (There are other parameters + set via the flavor, such as the amount of disk space and the number of + virtual CPUs, but a discussion of those is too in-depth for a simple + Getting Started Guide like this one.) + + server + A server is a virtual machine instance in the Cloud Servers environment. + + keypair diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst new file mode 100644 index 000000000..4040c0e65 --- /dev/null +++ b/doc/services/compute/keypairs.rst @@ -0,0 +1,71 @@ +Keypairs +======== + +Setup +----- + +.. include:: service.sample.rst + + +Generate a new keypair +---------------------- + +This operation creates a new keypair under a provided name; the public key +value is automatically generated for you. + +.. code-block:: php + + // Instantiate empty object + $keypair = $service->keypair(); + + // Send to API + $keypair->create(array( + 'name' => 'jamie_keypair_1' + )); + + // Save these! + $pubKey = $keypair->getPublicKey(); + $priKey = $keypair->getPrivateKey(); + + +Upload existing keypair +----------------------- + +This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem. + +.. code-block:: php + + $keypair = $service->keypair(); + + // $key needs to be the string content of the key file, not the filename + $content = file_get_contents('~/.ssh/id_rsa.pub'); + + $keypair->create(array( + 'name' => 'main_key', + 'publicKey' => $content + )); + +List keypairs +------------- + +To list all existing keypairs: + +.. code-block:: php + + $keys = $service->listKeypairs(); + + foreach ($keys as $key) { + + } + + +Delete keypairs +--------------- + +To delete a specific keypair: + +.. code-block:: php + + $keypair->delete(); diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst new file mode 100644 index 000000000..4d2f2363c --- /dev/null +++ b/doc/services/compute/servers.rst @@ -0,0 +1,202 @@ +Servers +======= + +Setup +----- + +.. include:: service.sample.rst + +Get server +---------- + +The easiest way to retrieve a specific server is by its unique ID: + +.. code-block:: php + + $server = $service->server('{serverId}'); + + +List servers +------------ + +You can list servers in two different ways: + +- return an *overview* of each server (ID, name and links) +- return *detailed information* for each server + +Knowing which option to use might help save unnecessary bandwidth and +reduce latency. + +.. code-block:: php + + // overview + $servers = $service->serverList(); + + // detailed + $servers = $service->serverList(true); + +URL parameters for filtering servers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| Name | Description | Type | ++==========================+====================================================================================================================================================================================================================================================================================================================+=================================================+ +| image | The image ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| flavor | The flavor ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| name | The server name | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| status | The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult ``OpenCloud\Compute\Constants\ServerState`` | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| changes-since | Value for checking for changes since a previous request | A valid ISO 8601 dateTime (2011-01-24T17:08Z) | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ + +Create server +------------- + +Using an image +~~~~~~~~~~~~~~ + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Using a bootable volume +~~~~~~~~~~~~~~~~~~~~~~~ + +Firstly we need to find our volume using their IDs. + +.. code-block:: php + + $bootableVolume = $client->volumeService()->volume('{volumeId}'); + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $response = $server->create(array( + 'name' => 'My lovely server', + 'volume' => $bootableVolume, + 'flavorId' => '{flavorId}' + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Create parameters +~~~~~~~~~~~~~~~~~ + ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| Name | Description | Type | Required | ++=============================+=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+===========+==============================+ +| name | The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync. | string | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| flavor | A populated ``OpenCloud\Compute\Resource\Flavor`` object representing your chosen flavor | object | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| image | A populated ``OpenCloud\Compute\Resource\Image`` object representing your chosen image | object | No, if volume is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volume | A populated ``OpenCloud\Volume\Resource\Volume`` object representing your chosen bootable volume | object | No, if image is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volumeDeleteOnTermination | ``true`` if the bootable volume should be deleted when the server is terminated; ``false``, otherwise | boolean | No; default = ``false`` | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| OS-DCF:diskConfig | The disk configuration value. You can use two options: ``AUTO`` or ``MANUAL``. \ ``AUTO`` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. ``AUTO`` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.\ ``MANUAL`` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration. | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| networks | An array of populated ``OpenCloud\Compute\Resource\Network`` objects that indicate which networks your instance resides in. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| metadata | An array of arbitrary data (key-value pairs) that adds additional meaning to your server. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| keypair | You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| personality | Files that you can upload to your newly created instance's filesystem. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ + +Creating a server with keypairs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'New server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'keypair' => 'main_key' + )); + +So, as you can see, you specify the **name** of an existing keypair that +you previously created on the API. + + +Creating a server with personality files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you execute the create operation, you can add "personality" files +to your ``OpenCloud\Compute\Resource\Server`` object. These files are +structured as a flat array. + +.. code-block:: php + + $server->addFile('/var/test_file', 'FILE CONTENT'); + +As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the `official +documentation `__. + +Update server +------------- + +You can update certain attributes of an existing server instance. These +attributes are detailed in the next section. + +.. code-block:: php + + $server->update(array( + 'name' => 'NEW SERVER NAME' + )); + +Updatable attributes +~~~~~~~~~~~~~~~~~~~~ + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | description | ++==============+==================================================================================================================================================+ +| name | The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv4 | The IP version 4 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv6 | The IP version 6 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Delete server +------------- + +.. code-block:: php + + $server->delete(); diff --git a/doc/services/compute/service.sample.rst b/doc/services/compute/service.sample.rst new file mode 100644 index 000000000..c684caf9a --- /dev/null +++ b/doc/services/compute/service.sample.rst @@ -0,0 +1,24 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + + +``{catalogName}`` is the **name** of the service, as it appears in the service +catalog. For Rackspace users, this will default to `cloudServersOpenStack`; for +OpenStack users, you must set your own value since it can depend on your +environment setup. + +``{region}`` is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the `supported regions page`. + +``{urlType}`` is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either `internalURL` or `publicURL`. +The former will execute HTTP transactions over the internal Rackspace network, +reducing latency and the overall bandwidth cost - the caveat is that all of your +resources must be in same region. `publicURL`, however, which is the default, +will operate over the public Internet and is to be used for multi-region +installations. From 2141f48bc43cb940b05c68f3e6307fe085c29d4b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:00 +0100 Subject: [PATCH 414/835] Add databases and DNS --- doc/services/database/README.md.rst | 125 ++++++++++++ doc/services/database/index.rst | 0 doc/services/dns/Domains.md.rst | 290 ++++++++++++++++++++++++++++ doc/services/dns/Limits.md.rst | 70 +++++++ doc/services/dns/Records.md.rst | 111 +++++++++++ doc/services/dns/Reverse-DNS.md.rst | 96 +++++++++ doc/services/dns/Service.md.rst | 13 ++ doc/services/dns/index.rst | 0 8 files changed, 705 insertions(+) create mode 100644 doc/services/database/README.md.rst create mode 100644 doc/services/database/index.rst create mode 100644 doc/services/dns/Domains.md.rst create mode 100644 doc/services/dns/Limits.md.rst create mode 100644 doc/services/dns/Records.md.rst create mode 100644 doc/services/dns/Reverse-DNS.md.rst create mode 100644 doc/services/dns/Service.md.rst create mode 100644 doc/services/dns/index.rst diff --git a/doc/services/database/README.md.rst b/doc/services/database/README.md.rst new file mode 100644 index 000000000..3f6bdd3c2 --- /dev/null +++ b/doc/services/database/README.md.rst @@ -0,0 +1,125 @@ +Databases +========= + +A **cloud database** is a MySQL relational database service that allows +customers to programatically provision database instances of varying +virtual resource sizes without the need to maintain and/or update MySQL. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\Rackspace; + use OpenCloud\Common\Constants\State; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Create a database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $databaseService = $client->databaseService('cloudDatabases', 'DFW'); + + $twoGbFlavor = $databaseService->flavor(3); + + $dbInstance = $databaseService->instance(); + $dbInstance->name = 'Demo database instance'; + $dbInstance->volume = new stdClass(); + $dbInstance->volume->size = 20; // GB + $dbInstance->flavor = $twoGbFlavor; + $dbInstance->create(); + + $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { + + printf("Database instance build status: %s\n", $dbInstance->status); + + }); + +The example above creates a database server instance with 20GB of disk +space and 2GB of memory, then waits for it to become ACTIVE. + +3. Create a database on the database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $db = $dbInstance->database(); + $db->name = 'demo_db'; + + $db->create(); + +The example above creates a database named ``demo_db`` on the database +server instance created in the previous step. + +4. Create database user and give it access to database. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $user = $dbInstance->user(); + $user->name = 'demo_user'; + $user->password = 'h@X0r!'; + $user->databases = array('demo_db'); + + $user->create(); + +The example above creates a database user named ``demo_user``, sets its +password and gives it access to the ``demo_db`` database created in the +previous step. + +5. Optional step: Create a load balancer to allow access to the database from the Internet. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The database created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $loadBalancer->name = 'Load balancer - DB'; + $loadBalancer->addNode($dbInstance->hostname, 3306); + $loadBalancer->port = 3306; + $loadBalancer->protocol = 'MYSQL'; + $loadBalancer->addVirtualIp('PUBLIC'); + + $loadBalancer->create(); + + $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { + printf("Load balancer build status: %s\n", $lb->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/dns/Domains.md.rst b/doc/services/dns/Domains.md.rst new file mode 100644 index 000000000..824c05099 --- /dev/null +++ b/doc/services/dns/Domains.md.rst @@ -0,0 +1,290 @@ +Domains +======= + +A domain is an entity/container of all DNS-related information +containing one or more records. + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name. + +.. code:: php + + $domain = $service->domain(12345); + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the aforementioned search by using the ``name`` parameter +in a key/value array supplied as a method argument. For example, +providing ``array('name' => 'hoola.com')`` will return hoola.com and +similar names such as main.hoola.com and sub.hoola.com. + +.. code:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'hoola.com' + )); + +Filter criteria may consist of: + +- Any letter (A-Za-z) +- Numbers (0-9) +- Hyphen ("-") +- 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +If you know a domain's name, but not its unique identifier, you can do +this: + +.. code:: php + + $domains = $service->domainList(array( + 'name' => 'foo.com' + )); + + foreach ($domains as $domain) { + $id = $domain->id; + } + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. The since parameter is optional and defaults to midnight of +the current day. + +.. code:: php + + $changes = $domain->changes(); + + # Changes since last week + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + +Export domain +------------- + +This call provides the BIND (Berkeley Internet Name Domain) 9 formatted +contents of the requested domain. This call is for a single domain only, +and as such, does not traverse up or down the domain hierarchy for +details (that is, no subdomain information is provided). + +.. code:: php + + $asyncResponse = $domain->export(); + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + +Create domain +------------- + +A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` +records) and an optional list of sub-domains. You will need to specify +these before creating the domain itself: + +.. code:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + $domain->addRecord($aRecord); + + // add optional C record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add optional MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add optional NS records + $nsRecord1 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord1); + + $nsRecord2 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns2.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord2); + + // add optional subdomains + $sub1 = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($sub1); + + // send to API + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + +Clone domain +------------ + +This call will duplicate a single existing domain configuration with a +new domain name for the specified Cloud account. By default, all records +and, optionally, subdomain(s) are duplicated as well. + +The method signature you will need to use is: + +.. code:: php + + cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) + ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Data type | Default | Description | ++======================+==============+============+====================================================================================================================================================================================+ +| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +For example: + +.. code:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true); + +Import domain +------------- + +This call provisions a new DNS domain under the account specified by the +BIND 9 formatted file configuration contents defined in the request +object. + +You will need to ensure that the BIND 9 formatted file configuration +contents are valid by adhering to the following rules: + +- Each record starts on a new line and on the first column. If a record + will not fit on one line, use the BIND\_9 line continuation + convention where you put a left parenthesis and continue the one + record on the next line and put a right parenthesis when the record + ends. For example, + + example2.net. 3600 IN SOA dns1.stabletransit.com. ( + sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +- The attribute values of a record must be separated by a single blank + or tab. No other white space characters. + +- If there are any NS records, the data field should not be + dns1.stabletransit.com or dns2.stabletransit.com. They will result in + "duplicate record" errors. + +For example: + +.. code:: php + + $bind9Data = <<import($bind9Data); + +Modify domain +------------- + +This call modifies DNS domain(s) attributes only. Only the TTL, email +address and comment attributes of a domain can be modified. Records +cannot be added, modified, or removed through this API operation - you +will need to use the `add +records `__, `modify +records `__ or `remove +records `__ operations +respectively. + +.. code:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + +Remove domain +------------- + +.. code:: php + + $domain->delete(); + diff --git a/doc/services/dns/Limits.md.rst b/doc/services/dns/Limits.md.rst new file mode 100644 index 000000000..72f8219a0 --- /dev/null +++ b/doc/services/dns/Limits.md.rst @@ -0,0 +1,70 @@ +Limits +====== + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +List all limits +--------------- + +This call provides a list of all applicable limits for the specified +account. + +.. code:: php + + $limits = $service->limits(); + +Absolute limits +~~~~~~~~~~~~~~~ + +There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain: + +.. code:: php + + $absoluteLimits = $limits->absolute; + + # Domain limit + echo $absoluteLimits->domains; + + # Record limit per domain + echo $absoluteLimits->{'records per domain'}; + +List limit types +---------------- + +To find out the different limit types you can query, run: + +.. code:: php + + $limitTypes = $service->limitTypes(); + +will return: + +:: + + array(3) { + [0] => + string(10) "RATE_LIMIT" + [1] => + string(12) "DOMAIN_LIMIT" + [2] => + string(19) "DOMAIN_RECORD_LIMIT" + } + +Query a specific limit +---------------------- + +.. code:: php + + $limit = $service->limits('DOMAIN_LIMIT'); + + echo $limit->absolute->limits->value; + + >>> 500 + diff --git a/doc/services/dns/Records.md.rst b/doc/services/dns/Records.md.rst new file mode 100644 index 000000000..4e492e8ef --- /dev/null +++ b/doc/services/dns/Records.md.rst @@ -0,0 +1,111 @@ +Records +======= + +A DNS record belongs to a particular domain and is used to specify +information about the domain. + +There are several types of DNS records. Examples include mail exchange +(MX) records, which specify the mail server for a particular domain, and +name server (NS) records, which specify the authoritative name servers +for a domain. + +It is represented by the ``OpenCloud\DNS\Resource\Record`` class. +Records belong to a `Domain `__. + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('NS-1234567'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+---------------+ +| Name | Data type | Default | Description | ++============+==============+========================+===============+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+---------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+---------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+---------------+ + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record(123456); + $record->ttl -= 100; + $record->update(); + +Delete record +------------- + +.. code:: php + + $record->delete(); + diff --git a/doc/services/dns/Reverse-DNS.md.rst b/doc/services/dns/Reverse-DNS.md.rst new file mode 100644 index 000000000..b8a5c0e76 --- /dev/null +++ b/doc/services/dns/Reverse-DNS.md.rst @@ -0,0 +1,96 @@ +Reverse DNS +=========== + +DNS usually determines an IP address associated with a domain name. +Reverse DNS is the opposite process: resolving a domain name from an IP +address. This is usually achieved with a domain name pointer. + +Get PTR record +-------------- + +PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one's PTR record: + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptr = $service->ptrRecord(array( + 'parent' => $parent + )); + +So, in the above example, a ``$parent`` could be an instance of +``OpenCloud\Compute\Resource\Server`` or +``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both +implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please +consult the `server documentation <../Compute/Server.md>`__ and `load +balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +detailed usage instructions. + +List PTR records +---------------- + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptrRecords = $service->ptrRecordList($parent); + + foreach ($ptrRecords as $ptrRecord) { + + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add PTR record +-------------- + +.. code:: php + + $parent = $computeService->server('foo-server-id'); + + $ptr = $dnsService->ptrRecord(array( + 'parent' => $parent, + 'ttl' => 3600, + 'name' => 'example.com', + 'type' => 'PTR', + 'data' => '192.0.2.7' + )); + + $ptr->create(); + +Here is a table that explains the above attributes: + ++-----------+------------------------------------------------------------------------------------+------------+ +| Name | Description | Required | ++===========+====================================================================================+============+ +| type | Specifies the record type as "PTR". | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| name | Specifies the name for the domain or subdomain. Must be a valid domain name. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| data | The data field for PTR records must be a valid IPv4 or IPv6 IP address. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| ttl | If specified, must be greater than 300. Defaults to 3600 if no TTL is specified. | No | ++-----------+------------------------------------------------------------------------------------+------------+ +| comment | If included, its length must be less than or equal to 160 characters. | No | ++-----------+------------------------------------------------------------------------------------+------------+ + +Modify PTR record +----------------- + +.. code:: php + + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + +Delete PTR record +----------------- + +.. code:: php + + $ptr->delete(); + diff --git a/doc/services/dns/Service.md.rst b/doc/services/dns/Service.md.rst new file mode 100644 index 000000000..29ea79193 --- /dev/null +++ b/doc/services/dns/Service.md.rst @@ -0,0 +1,13 @@ +DNS Service +=========== + +To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the `Clients documentation <../Clients.md>`__. + +You will then need to run: + +.. code:: php + + $service = $client->dnsService(); + diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst new file mode 100644 index 000000000..e69de29bb From 09c7b0e4e3b6f70115987ad367ab3c6c8ec6d2b9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:13 +0100 Subject: [PATCH 415/835] Add Identity and Images --- doc/services/identity/Roles.md.rst | 92 +++++++++++++++ doc/services/identity/Service.md.rst | 35 ++++++ doc/services/identity/Tenants.md.rst | 29 +++++ doc/services/identity/Tokens.md.rst | 105 +++++++++++++++++ doc/services/identity/Users.md.rst | 170 +++++++++++++++++++++++++++ doc/services/identity/index.rst | 0 doc/services/image/Images.md.rst | 107 +++++++++++++++++ doc/services/image/Schemas.md.rst | 170 +++++++++++++++++++++++++++ doc/services/image/Sharing.md.rst | 129 ++++++++++++++++++++ doc/services/image/Tags.md.rst | 28 +++++ doc/services/image/index.rst | 0 11 files changed, 865 insertions(+) create mode 100644 doc/services/identity/Roles.md.rst create mode 100644 doc/services/identity/Service.md.rst create mode 100644 doc/services/identity/Tenants.md.rst create mode 100644 doc/services/identity/Tokens.md.rst create mode 100644 doc/services/identity/Users.md.rst create mode 100644 doc/services/identity/index.rst create mode 100644 doc/services/image/Images.md.rst create mode 100644 doc/services/image/Schemas.md.rst create mode 100644 doc/services/image/Sharing.md.rst create mode 100644 doc/services/image/Tags.md.rst create mode 100644 doc/services/image/index.rst diff --git a/doc/services/identity/Roles.md.rst b/doc/services/identity/Roles.md.rst new file mode 100644 index 000000000..96a56488f --- /dev/null +++ b/doc/services/identity/Roles.md.rst @@ -0,0 +1,92 @@ +Roles +===== + +Intro +----- + +A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user's roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service. + +Setup +----- + +Role objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++---------------+------------------------+------------------------+ +| Property | Getter | Setter | ++===============+========================+========================+ +| id | ``getId()`` | ``setId()`` | ++---------------+------------------------+------------------------+ +| name | ``getName()`` | ``setName()`` | ++---------------+------------------------+------------------------+ +| description | ``getDescription()`` | ``setDescription()`` | ++---------------+------------------------+------------------------+ + +List roles +---------- + +This call lists the global roles available within a specified service. + +.. code:: php + + $roles = $service->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get role +-------- + +This call lists detailed information (id, name, description) for a +specified role. + +.. code:: php + + $roleId = '123abc'; + $role = $service->getRole($roleId); + +Add/delete user roles +--------------------- + +To add/remove user roles, you must first instantiate a +`user `__ object: + +.. code:: php + + $roleId = '123abc'; + + // add role to user + $user->addRole($roleId); + + // remove role from user + $user->removeRole($roleId); + +List user global roles +---------------------- + +This call returns a list of global roles associated with a user: + +.. code:: php + + $roles = $user->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Service.md.rst b/doc/services/identity/Service.md.rst new file mode 100644 index 000000000..f7e8e12ba --- /dev/null +++ b/doc/services/identity/Service.md.rst @@ -0,0 +1,35 @@ +Identity service +================ + +Intro +----- + +The Identity service is regionless, so you do not need to specify a +region when instantiating the service object. Although this was +primarily based on Rackspace's implementation of Cloud Identity, it +should also work for OpenStack Keystone. + +A note on object creation +------------------------- + +Normally, when services are created the client handles authenticates +automatically. But because Keystone/Identity is fundamental to the +authentication process itself, it proves difficult to do this procedure +as its normally done. For this reason, you have two options when +creating the service object: + +1: Use the client's factory method + +.. code:: php + + $identity = $client->identityService(); + +2: Authenticate manually + +.. code:: php + + use OpenCloud\Identity\Service as IdentityService; + + $identity = IdentityService::factory($client); + $identity->getClient()->authenticate(); + diff --git a/doc/services/identity/Tenants.md.rst b/doc/services/identity/Tenants.md.rst new file mode 100644 index 000000000..9b58efd1a --- /dev/null +++ b/doc/services/identity/Tenants.md.rst @@ -0,0 +1,29 @@ +Tenants +======= + +Intro +----- + +A tenant is a container used to group or isolate resources and/or +identity objects. Depending on the service operator, a tenant may map to +a customer, account, organization, or project. + +Setup +----- + +Tenant objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +List tenants +------------ + +.. code:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Tokens.md.rst b/doc/services/identity/Tokens.md.rst new file mode 100644 index 000000000..c42ce1573 --- /dev/null +++ b/doc/services/identity/Tokens.md.rst @@ -0,0 +1,105 @@ +Tokens +====== + +Intro +----- + +A token is an opaque string that represents an authorization to access +cloud resources. Tokens may be revoked at any time and are valid for a +finite duration. + +Setup +----- + +Token objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| Property | Description | Getter | Setter | ++============+===========================================+========================================+====================+ +| id | The unique ID of the token | ``getId()`` | ``setId()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +Request body for Rackspace's generate token operation: + +.. code:: json + + { + "auth": { + "RAX-KSKEY:apiKeyCredentials": { + "username": "foo", + "apiKey": "aaaaa-bbbbb-ccccc-12345678" + }, + "tenantId": "1100111" + } + } + +Request body for Keystone's generate token operation: + +.. code:: json + + { + "auth": { + "passwordCredentials":{ + "username":"demoauthor", + "password":"theUsersPassword" + }, + "tenantId": "12345678" + } + } + +The only real differences you'll notice is the name of the object key +(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret +(``apiKey``/``password``). The ``tenantId`` property in both templates +are optional. You can also add ``tenantName`` too. + +.. code:: php + + use OpenCloud\Common\Http\Message\Formatter; + + $template = sprintf( + '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', + 'my_username', + 'my_api_key' + ); + + $response = $service->generateToken($template); + + $body = Formatter::decode($response); + + // service catalog + $catalog = $body->access->serviceCatalog; + + // token + $token = $body->access->token; + + // user + $user = $body->access->user; + +As you will notice, these variables will be stdClass objects - for fully +fledged functionality, let the client authenticate by itself because it +ends up stocking the necessary models for you. + +To see the response body structure, consult the `official +docs `__. + +Revoke token (destroy session) +------------------------------ + +.. code:: php + + $tokenId = '1234567'; + $service->revokeToken($tokenId); + diff --git a/doc/services/identity/Users.md.rst b/doc/services/identity/Users.md.rst new file mode 100644 index 000000000..e8c2e6d63 --- /dev/null +++ b/doc/services/identity/Users.md.rst @@ -0,0 +1,170 @@ +Users +===== + +Intro +----- + +A user is a digital representation of a person, system, or service who +consumes cloud services. Users have credentials and may be assigned +tokens; based on these credentials and tokens, the authentication +service validates that incoming requests are being made by the user who +claims to be making the request, and that the user has the right to +access the requested resources. Users may be directly assigned to a +particular tenant and behave as if they are contained within that +tenant. + +Setup +----- + +User objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| Property | Description | Getter | Setter | ++=================+===============================================================================================================================================================================================================================================================================================================================+============================================+===============================================================================================================+ +| id | The unique ID for this user | ``getId()`` | ``setId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| username | Username for this user | ``getUsername()`` | ``setUsername()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| email | User's email address | ``getEmail()`` | ``setEmail()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| enabled | Whether or not this user can consume API functionality | ``getEnabled()`` or ``isEnabled()`` | ``setEnabled()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| password | Either a user-defined string, or an automatically generated one, that provides security when authenticating. | ``getPassword()`` only valid on creation | ``setPassword()`` to set local property only. To set password on API (retention), use ``updatePassword()``. | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| defaultRegion | Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region. | ``getDefaultRegion()`` | ``setDefaultRegion()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| domainId | Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account. | ``getDomainId()`` | ``setDomainId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ + +List users +---------- + +.. code:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get user +-------- + +There are various ways to get a specific user: by name, ID and email +address. + +.. code:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + // Get user by name + $user1 = $service->getUser('jamie'); + + // Get user by ID + $user2 = $service->getUser(123456, UserConst::MODE_ID); + + // Get user by email + $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); + +Create user +----------- + +There are a few things to remember when creating a user: + +- This operation is available only to users who hold the + ``identity:user-admin`` role. This admin can create a user who holds + the ``identity:default`` user role. + +- The created user **will** have access to APIs but **will not** have + access to the Cloud Control Panel. + +- Within an account, a maximum of 100 account users can be added. + +- If you attempt to add a user who already exists, an HTTP error 409 + results. + +The ``username`` and ``email`` properties are required for creating a +user. Providing a ``password`` is optional; if omitted, one will be +automatically generated and provided in the response. + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + // execute operation + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + } catch (ClientErrorResponseException $e) { + // catch 4xx HTTP errors + echo $e->getResponse()->toString(); + } + + // show generated password + echo $user->getPassword(); + +Update user +----------- + +When updating a user, specify which attribute/property you want to +update: + +.. code:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); + +Updating a user password +~~~~~~~~~~~~~~~~~~~~~~~~ + +Updating a user password requires calling a distinct method: + +.. code:: php + + $user->updatePassword('password123'); + +Delete user +----------- + +.. code:: php + + $user->delete(); + +List credentials +---------------- + +This operation allows you to see your non-password credential types for +all authentication methods available. + +.. code:: php + + $creds = $user->getOtherCredentials(); + +Get user API key +---------------- + +.. code:: php + + echo $user->getApiKey(); + +Reset user API key +------------------ + +When resetting an API key, a new one will be automatically generated for +you: + +.. code:: php + + $user->resetApiKey(); + echo $user->getApiKey(); + diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/image/Images.md.rst b/doc/services/image/Images.md.rst new file mode 100644 index 000000000..ef187f083 --- /dev/null +++ b/doc/services/image/Images.md.rst @@ -0,0 +1,107 @@ +Images +====== + +A virtual machine image is a single file which contains a virtual disk +that has an installed bootable operating system. In the Cloud Images +API, an image is represented by a JSON-encoded data structure (the image +schema) and its raw binary data (the image file). + +An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. + +Setup +----- + +You instantiate an Image object from its ``OpenCloud\Image\Service`` +class, which is available from the OpenStack/Rackspace client: + +.. code:: php + + $service = $client->imageService('cloudImages', 'IAD'); + +View the guides for more information about `clients <../Clients.md>`__ +or `services <../Services.md>`__. + +List images +----------- + +.. code:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Get image details +----------------- + +.. code:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage(''); + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and +``OpenCloud\Image\Resource\Member`` extend the +``AbstractSchemaResource`` abstract class, which offers some unique +functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a way +different than conventional getter/setter methods, and even class +properties. For this reason, they implement SPL's native +```ArrayAccess`` `__ +interface which allows you to access their state as a conventional +array: + +.. code:: php + + $image = $service->getImage(''); + + $id = $image['id']; + $tags = $image['tags']; + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +- If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +- If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code:: php + + $image->delete(); + diff --git a/doc/services/image/Schemas.md.rst b/doc/services/image/Schemas.md.rst new file mode 100644 index 000000000..8f792c4c0 --- /dev/null +++ b/doc/services/image/Schemas.md.rst @@ -0,0 +1,170 @@ +JSON schemas +============ + +The Cloud Images API supplies json documents describing the JSON-encoded +data structures that represent domain objects, so that a client knows +exactly what to expect in an API response. + +A JSON Schema is represented by the +``OpenCloud\Image\Resource\Schema\Schema`` class. + +Schema types +------------ + +There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema. + +Example response from the API +----------------------------- + +A sample response from the API, for an Images schema might be: + +.. code:: json + + { + "name": "images", + "properties": { + "images": { + "items": { + "type": "array", + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + }, + "schema": {"type": "string"}, + "next": {"type": "string"}, + "first": {"type": "string"} + }, + "links": [ + {"href": "{first}", "rel": "first"}, + {"href": "{next}", "rel": "next"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + +The top-level schema is called ``images``, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level ``images`` object. So we know that it will +take this form: + +.. code:: json + + { + "images": [something...] + } + +Within this object, we can see that it contains an array of anonymous +objects, each of which is called ``image`` and has its own set of nested +properties: + +.. code:: json + + { + "images": [ + { + [object 1...] + }, + { + [object 2...] + }, + { + [object 3...] + } + ] + } + +The structure of these nested objects are defined as another schema - +i.e. a *subschema*. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +``private`` or ``public``), etc. + +.. code:: json + + { + "images": [ + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + } + ] + } + +Each nested property of a schema is represented by the +``OpenCloud\Image\Resource\Schema\Property`` class. + +If you would like to find out more about schemas, Guzzle has good +documentation about `service +descriptions `__, +which is fairly analogous. + +JSON Patch +---------- + +The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in `RFC +6902 `__. + +Requests need to use the +``application/openstack-images-v2.1-json-patch`` content-type. + +In order for the operation to occur, the request entity body needs to +contain a very particular structure: + +:: + + [ + {"op": "replace", "path": "/name", "value": "Fedora 17"}, + {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} + ] + +The ``op`` key refers to the type of Operation (see +``OpenCloud\Image\Enum\OperationType`` for a full list). + +The ``path`` key is a JSON pointer to the document property you want to +modify or insert. JSON pointers are defined in `RFC +6901 `__. + +The ``value`` key is the value. + +Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the `official +documentation `__ +for additional information. diff --git a/doc/services/image/Sharing.md.rst b/doc/services/image/Sharing.md.rst new file mode 100644 index 000000000..b1a1ea382 --- /dev/null +++ b/doc/services/image/Sharing.md.rst @@ -0,0 +1,129 @@ +Sharing images +============== + +Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers: + ++-------------+-----------------+-----------------+ +| Operation | Producer can? | Consumer can? | ++=============+=================+=================+ +| Created | Yes | No | ++-------------+-----------------+-----------------+ +| Deleted | Yes | No | ++-------------+-----------------+-----------------+ +| Updated | No | Yes | ++-------------+-----------------+-----------------+ +| Listed | Yes | Yes | ++-------------+-----------------+-----------------+ + +The producer shares an image with the consumer by making the consumer a +*member* of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer's image list. + +Typical workflow +---------------- + +1. The producer posts the availability of specific images on a public + website. + +2. A potential consumer provides the producer with his/her tenant ID and + email address. + +3. The producer `creates a new Image Member <>`__ with the consumer's + details + +4. The producer notifies the consumer via email that the image has been + shared and provides the image's ID. + +5. If the consumer wishes the image to appear in his/her image list, the + consumer `updates their own Member status <>`__ to ``ACCEPTED``. + +Additional notes +~~~~~~~~~~~~~~~~ + +- If the consumer subsequently wishes to hide the image, the consumer + can change their Member status to ``REJECTED``. + +- If the consumer wishes to hide the image, but is open to the + possibility of being reminded by the producer that the image is + available, the consumer can change their Member status to + ``PENDING``. + +- Image producers add or remove image members, but may not modify the + member status of an image member. + +- Image consumers change their own member status, but may not add or + remove themselves as an image member. + +- Image consumers can boot from any image shared by the image producer, + regardless of the member status, as long as the image consumer knows + the image ID. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +List image members +------------------ + +This operation is available for both producers and consumers. + +.. code:: php + + $members = $image->listMembers(); + + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Create image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember($tenantId); + +Delete image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->delete(); + +Update image member status +-------------------------- + +This operation is only available for consumers. + +.. code:: php + + use OpenCloud\Images\Enum\MemberStatus; + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->updateStatus(MemberStatus::ACCEPTED); + +The acceptable states you may pass in are made available to you through +the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` +class. diff --git a/doc/services/image/Tags.md.rst b/doc/services/image/Tags.md.rst new file mode 100644 index 000000000..af5baf157 --- /dev/null +++ b/doc/services/image/Tags.md.rst @@ -0,0 +1,28 @@ +Image tags +========== + +An image tag is a string of characters used to identify a specific image +or images. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +Add image tag +------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); + diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst new file mode 100644 index 000000000..e69de29bb From f63a1b0d85de0b1aae86d5f81dbbf09434544439 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:26 +0100 Subject: [PATCH 416/835] Add remaining --- doc/services/load-balancer/README.md.rst | 92 ++ doc/services/load-balancer/USERGUIDE.md.rst | 840 ++++++++++++++++ doc/services/load-balancer/index.rst | 0 doc/services/monitoring/Agents.md.rst | 202 ++++ doc/services/monitoring/Alarms.md.rst | 81 ++ doc/services/monitoring/Changelogs.md.rst | 21 + doc/services/monitoring/Checks.md.rst | 211 ++++ doc/services/monitoring/Entities.md.rst | 77 ++ doc/services/monitoring/Metrics.md.rst | 131 +++ doc/services/monitoring/Notifications.md.rst | 281 ++++++ doc/services/monitoring/Service.md.rst | 23 + doc/services/monitoring/Views.md.rst | 27 + doc/services/monitoring/Zones.md.rst | 67 ++ doc/services/monitoring/index.rst | 0 doc/services/networking/README.md.rst | 108 +++ doc/services/networking/USERGUIDE.md.rst | 582 +++++++++++ doc/services/networking/index.rst | 0 doc/services/object-store/Access.md.rst | 75 ++ doc/services/object-store/Account.md.rst | 33 + .../object-store/Container.md.cdn.rst | 90 ++ .../object-store/Container.md.storage.rst | 218 +++++ .../object-store/Migrating.md.storage.rst | 101 ++ doc/services/object-store/Object.md.cdn.rst | 25 + .../object-store/Object.md.storage.rst | 330 +++++++ doc/services/object-store/README.md.rst | 86 ++ doc/services/object-store/USERGUIDE.md.rst | 900 ++++++++++++++++++ doc/services/object-store/index.rst | 0 doc/services/orchestration/README.md.rst | 98 ++ doc/services/orchestration/USERGUIDE.md.rst | 672 +++++++++++++ doc/services/orchestration/index.rst | 0 doc/services/queues/Claim.md.rst | 164 ++++ doc/services/queues/Message.md.rst | 257 +++++ doc/services/queues/Queue.md.rst | 197 ++++ doc/services/queues/index.rst | 0 doc/services/volume/index.rst | 0 35 files changed, 5989 insertions(+) create mode 100644 doc/services/load-balancer/README.md.rst create mode 100644 doc/services/load-balancer/USERGUIDE.md.rst create mode 100644 doc/services/load-balancer/index.rst create mode 100644 doc/services/monitoring/Agents.md.rst create mode 100644 doc/services/monitoring/Alarms.md.rst create mode 100644 doc/services/monitoring/Changelogs.md.rst create mode 100644 doc/services/monitoring/Checks.md.rst create mode 100644 doc/services/monitoring/Entities.md.rst create mode 100644 doc/services/monitoring/Metrics.md.rst create mode 100644 doc/services/monitoring/Notifications.md.rst create mode 100644 doc/services/monitoring/Service.md.rst create mode 100644 doc/services/monitoring/Views.md.rst create mode 100644 doc/services/monitoring/Zones.md.rst create mode 100644 doc/services/monitoring/index.rst create mode 100644 doc/services/networking/README.md.rst create mode 100644 doc/services/networking/USERGUIDE.md.rst create mode 100644 doc/services/networking/index.rst create mode 100644 doc/services/object-store/Access.md.rst create mode 100644 doc/services/object-store/Account.md.rst create mode 100644 doc/services/object-store/Container.md.cdn.rst create mode 100644 doc/services/object-store/Container.md.storage.rst create mode 100644 doc/services/object-store/Migrating.md.storage.rst create mode 100644 doc/services/object-store/Object.md.cdn.rst create mode 100644 doc/services/object-store/Object.md.storage.rst create mode 100644 doc/services/object-store/README.md.rst create mode 100644 doc/services/object-store/USERGUIDE.md.rst create mode 100644 doc/services/object-store/index.rst create mode 100644 doc/services/orchestration/README.md.rst create mode 100644 doc/services/orchestration/USERGUIDE.md.rst create mode 100644 doc/services/orchestration/index.rst create mode 100644 doc/services/queues/Claim.md.rst create mode 100644 doc/services/queues/Message.md.rst create mode 100644 doc/services/queues/Queue.md.rst create mode 100644 doc/services/queues/index.rst create mode 100644 doc/services/volume/index.rst diff --git a/doc/services/load-balancer/README.md.rst b/doc/services/load-balancer/README.md.rst new file mode 100644 index 000000000..862cf20d2 --- /dev/null +++ b/doc/services/load-balancer/README.md.rst @@ -0,0 +1,92 @@ +Load Balancers +============== + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Retrieve the server instances you want to add as nodes of the load balancer. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); + + $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); + $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); + +The example above uses two server instances that have already been +created. It retrieves the server instances using their IDs. See also: +`creating server instances <>`__. + +3. Obtain a Load Balancer service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This object will be used to first define the load balancer nodes and +later create the load balancer itself. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +4. Define a load balancer node for each server. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + +In the example above, each node runs a service that listens on port +8080. Further, each node will start out as ``ENABLED``, which means it +will be ready to receive network traffic from the load balancer as soon +as it is created. + +5. Create the load balancer with the two nodes. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +In the example above, the load balancer will have a virtual IP address +accessible from the public Internet. Also notice that the port the load +balancer listens on (80) does not need to match the ports of its nodes +(8080). + +Next steps +---------- + +Once you have created a load balancer, there is a lot you can do with +it. See the `complete user guide for load balancers `__. diff --git a/doc/services/load-balancer/USERGUIDE.md.rst b/doc/services/load-balancer/USERGUIDE.md.rst new file mode 100644 index 000000000..788651f2b --- /dev/null +++ b/doc/services/load-balancer/USERGUIDE.md.rst @@ -0,0 +1,840 @@ +The Complete User Guide to Load Balancers +========================================= + +Prerequisites +------------- + +Client +~~~~~~ + +To use the load balancers service, you must first instantiate a +``Rackspace`` client object. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Load Balancer Service +~~~~~~~~~~~~~~~~~~~~~ + +All operations on load balancers are done via a load balancer service +object. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +Cloud Servers +~~~~~~~~~~~~~ + +Many of the examples in this document use two cloud servers as nodes for +the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer +to these two cloud servers. + +Load Balancers +-------------- + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Create Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +List Load Balancer Details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single load balancer's details by using its ID. + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer('254889'); + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $loadBalancers = $loadBalancerService->loadBalancerList(); + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + +Update Load Balancer Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +Updating a single attribute of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name' + )); + +Updating multiple attributes of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it. + +.. code:: php + + $loadBalancer->delete(); + +Nodes +----- + +A **node** is a backend device that provides a service on specified IP +and port. An example of a load balancer node might be a web server +serving HTTP traffic on port 8080. + +A load balancer typically has multiple nodes attached to it so it can +distribute incoming network traffic amongst them. + +List Nodes +~~~~~~~~~~ + +You can list the nodes attached to a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodes = $loadBalancer->nodeList(); + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + +Add Nodes +~~~~~~~~~ + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| --------------\| -------------- \| +----------------- \| +|  1 \| IP address of node \| String \| Yes \| - \| +|  2 \| Port used by node's service \| Integer \| Yes \| - \| +|  3 \| Starting condition of node: +|  4 \| Type of node to add: +|  5 \| Weight, between 1 and 100, given to node when distributing +traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the +``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| +No \| 1 \| + +Modify Nodes +~~~~~~~~~~~~ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +Modifying a single attribute of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + + $node->update(array( + 'condition' => NodeCondition::DISABLED + )); + +Modifying multiple attributes of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + +Remove Nodes +~~~~~~~~~~~~ + +There are two ways to remove a node. + +Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $node->delete(); + +Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->removeNode(490639); + +The ``removeNode`` method, as shown above, accepts the following +arguments: + ++------------+---------------+-------------+-------------+-----------------+ +| Position | Description | Data type | Required? | Default value | ++============+===============+=============+=============+=================+ +| 1 | ID of node | Integer | Yes | - | ++------------+---------------+-------------+-------------+-----------------+ + +View Node Service Events +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can view events associated with the activity between a node and a +load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } + +Virtual IPs +----------- + +A **virtual IP (VIP)** makes a load balancer accessible by clients. The +load balancing service supports either a public VIP address +(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP +address (``SERVICENET``), routable only within the region in which the +load balancer resides. + +List Virtual IPs +~~~~~~~~~~~~~~~~ + +You can list the VIPs associated with a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $vips = $loadBalancer->virtualIpList(); + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + +Add Virtual IPv6 +~~~~~~~~~~~~~~~~ + +You can add additional IPv6 VIPs to a load balancer. + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +The ``addVirtualIp`` method, as shown above, accepts the following +arguments: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| -------------- \|-------------- \| +----------------- \| +|  1 \| Type of VIP: +|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| + +Remove Virtual IPs +~~~~~~~~~~~~~~~~~~ + +You can remove a VIP from a load balancer. + +.. code:: php + + $vip->remove(); + +Please note that a load balancer must have at least one VIP associated +with it. If you try to remove a load balancer's last VIP, a +``ClientErrorResponseException`` will be thrown. + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported load balancing algorithms using a load +balancer service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $algorithms = $loadBalancerService->algorithmList(); + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols using a load balancer +service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $protocols = $loadBalancerService->protocolList(); + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + +Session Persistence +------------------- + +**Session persistence** is a feature of the load balancing service that +forces multiple requests, of the same protocol, from clients to be +directed to the same node. This is common with many web applications +that do not inherently share application state between back-end servers. + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +List Session Persistence Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistenceType = $sessionPersistence->persistenceType; + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + +Enable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + +Disable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); + +Connection Logging +------------------ + +The **connection logging** feature allows logs to be delivered to a +Cloud Files account every hour. For HTTP-based protocol traffic, these +are Apache-style access logs. For all other traffic, this is connection +and transfer logging. + +Check Logging Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $connectionLogging bool **/ + + $connectionLogging = $loadBalancer->hasConnectionLogging(); + +In the example above: + +- If connection logging is enabled, the value of ``$connectionLogging`` + is ``true``. +- If connection logging is disabled, the value of + ``$connectionLogging`` is ``false``. + +Enable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(true); + +Disable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(false); + +Error Page +---------- + +An **error page** is the html file that is shown to the end user when an +error in the service has been thrown. By default every virtual server is +provided with the default error file. It is also possible to set a +custom error page for a load balancer. + +View Error Page Content +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + +Set Custom Error Page +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + +Delete Custom Error Page +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); + +Allowed Domains +--------------- + +**Allowed domains** are a restricted set of domain names that are +allowed to add load balancer nodes. + +List Allowed Domains +~~~~~~~~~~~~~~~~~~~~ + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code:: php + + $allowedDomains = $loadBalancerService->allowedDomainList(); + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + +Access Lists +------------ + +**Access Lists** allow fine-grained network access to a load balancer's +VIP. Using access lists, network traffic to a load balancer's VIP can be +allowed or denied from a single IP address, multiple IP addresses or +entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` +network items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +View Access List +~~~~~~~~~~~~~~~~ + +You can view a load balancer's access list. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $accessList = $loadBalancer->accessList(); + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + +Add Network Items To Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can add network items to a load balancer's access list very easily: + +.. code:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + +Remove Network Item From Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You an remove a network item from a load balancer's access list. + +.. code:: php + + $networkItem->delete(); + +Content Caching +--------------- + +When **content caching** is enabled on a load balancer, +recently-accessed files are stored on the load balancer for easy +retrieval by web clients. Requests to the load balancer for these files +are serviced by the load balancer itself, which reduces load off its +back-end nodes and improves response times as well. + +Check Content Caching Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $contentCaching bool **/ + + $contentCaching = $loadBalancer->hasContentCaching(); + +In the example above: + +- If content caching is enabled, the value of ``$contentCaching`` is + ``true``. +- If content caching is disabled, the value of ``$contentCaching`` is + ``false``. + +Enable Content Caching +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(true); + +Disable Content Caching +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(false); + +SSL Termination +--------------- + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +View current SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + + $sslConfig = $loadBalancer->SSLTermination(); + +Update SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Delete SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->delete(); + +Metadata +-------- + +Metadata can be associated with each load balancer and each node for the +client's personal use. It is defined using key-value pairs where the key +and value consist of alphanumeric characters. A key is unique per load +balancer. + +List metadata +~~~~~~~~~~~~~ + +.. code:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add metadata +~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + +Modify metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + +Remove metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem->delete(); + +Monitors +-------- + +The load balancing service includes a health monitoring operation which +periodically checks your back-end nodes to ensure they are responding +correctly. If a node is not responding, it is removed from rotation +until the health monitor determines that the node is functional. In +addition to being performed periodically, the health check also is +performed against every node that is added to ensure that the node is +operating properly before allowing it to service traffic. Only one +health monitor is allowed to be enabled on a load balancer at a time. + +.. code:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Update or delete +~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + // Delete + $healthMonitor->delete(); + +Statistics +---------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect\_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load + balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive\_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + + $stats = $loadBalancer->stats(); + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } + diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/monitoring/Agents.md.rst b/doc/services/monitoring/Agents.md.rst new file mode 100644 index 000000000..943983f7e --- /dev/null +++ b/doc/services/monitoring/Agents.md.rst @@ -0,0 +1,202 @@ +Agents +====== + +Intro +----- + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Setup +----- + +.. code:: php + + $agentId = '00-agent.example.com'; + $agent = $service->getAgent($agentId); + +You can view the `service page `__ for more information +about setting up the Cloud Monitoring service. + +List agents +----------- + +.. code:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +List connections +---------------- + +.. code:: php + + $connections = $agent->getConnections(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Get connection +-------------- + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('cntl4qsIbA'); + +Agent Connection properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------+---------------+--------+---------------------------+ +| Name | Description | Type | Method | ++====================+===============+========+===========================+ +| id | - | - | ``getId()`` | ++--------------------+---------------+--------+---------------------------+ +| guid | - | - | ``getGuid()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_id | - | - | ``getAgentId()`` | ++--------------------+---------------+--------+---------------------------+ +| endpoint | - | - | ``getEndpoint()`` | ++--------------------+---------------+--------+---------------------------+ +| process\_version | - | - | ``getProcessVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| bundle\_version | - | - | ``getBundleVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_ip | - | - | ``getAgentIp()`` | ++--------------------+---------------+--------+---------------------------+ + +Agent tokens +============ + +Intro +----- + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Setup +----- + +.. code:: php + + $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; + $agentToken = $service->getAgentToken($tokenId); + +Create agent token +------------------ + +.. code:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + +List agent tokens +----------------- + +.. code:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +Update and delete Agent Token +----------------------------- + +.. code:: php + + // Update + $token->update(array( + 'label' => 'New label' + )); + + // Delete + $token->delete(); + +Agent Host Information +====================== + +Info +---- + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code:: php + + $host = $monitoringService->getAgentHost(); + +Get some metrics +---------------- + +.. code:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Info +---- + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } + diff --git a/doc/services/monitoring/Alarms.md.rst b/doc/services/monitoring/Alarms.md.rst new file mode 100644 index 000000000..2918637e9 --- /dev/null +++ b/doc/services/monitoring/Alarms.md.rst @@ -0,0 +1,81 @@ +Alarms +====== + +Info +---- + +Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (``OK``, +``WARNING`` or ``CRITICAL``) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the `reference +documentation `__. + +Setup +----- + +Alarms are sub-resources of Entities: + +.. code:: php + + $alarmId = 'alAAAA'; + $alarm = $check->getAlarm(); + +For more information about working with Checks, please see the +`appropriate documentation `__. + +Attributes +---------- + ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| Name | Description | Required? | Method | ++==========================+=============================================================================+=============+=================================+ +| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| disabled | Disable processing and alerts on this alarm | Optional | ``isDisabled()`` <``bool``\ > | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| label | A friendly label for an alarm. | Optional | ``getLabel()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + +Create Alarm +------------ + +.. code:: php + + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); + +List Alarms +----------- + +.. code:: php + + $alarms = $entity->getAlarms(); + + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } + +Update and delete Alarm +----------------------- + +.. code:: php + + // Update + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + // Delete + $alarm->delete(); + diff --git a/doc/services/monitoring/Changelogs.md.rst b/doc/services/monitoring/Changelogs.md.rst new file mode 100644 index 000000000..2e9059e26 --- /dev/null +++ b/doc/services/monitoring/Changelogs.md.rst @@ -0,0 +1,21 @@ +Changelogs +========== + +Info +---- + +The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information. + +Working with Changelogs +----------------------- + +.. code:: php + + $changelog = $service->getChangelog(); + + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } + diff --git a/doc/services/monitoring/Checks.md.rst b/doc/services/monitoring/Checks.md.rst new file mode 100644 index 000000000..0e8e902a5 --- /dev/null +++ b/doc/services/monitoring/Checks.md.rst @@ -0,0 +1,211 @@ +Checks +====== + +Info +---- + +A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information: + +- A name for the check +- The check's parent entity +- The type of check you're creating +- Details of the check +- The monitoring zones that will launch the check + +The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate). + +Setup +----- + +Checks are sub-resources of Entities: + +.. code:: php + + $checkId = 'chAAAA'; + $check = $entity->getCheck($checkId); + +Attributes +---------- + ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| Name | Description | Required? | Data type | ++============+=============================================================================================================+=============+==========================================+ +| type | The type of check. | Required | Valid check type. String (1..25 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| details | Details specific to the check type. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| disabled | Disables the check. | Optional | Boolean | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| label | A friendly label for a check. | Optional | String (1..255 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| period | The period in seconds for a check. The value must be greater than the minimum period set on your account. | Optional | Integer (30..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ + +Attributes used for remote Checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| Name | Description | Required? | Data type | ++===========================+========================================================================================================================================================+=============+============================================================+ +| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ + +Test parameters (before create) +------------------------------- + +.. code:: php + + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); + + // You can do a test to see what would happen + // if a Check is launched with these params + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + +Create a Check +-------------- + +.. code:: php + + $entity->createCheck($params); + +Test existing Check +------------------- + +.. code:: php + + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; + +List Checks +----------- + +.. code:: php + + $checks = $entity->getChecks(); + + foreach ($checks as $check) { + echo $check->getId(); + } + +Update and delete Check +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $check->update(array('period' => 500)); + + // Delete + $check->delete(); + +Check types +=========== + +Info +---- + +Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. **Note:** Users cannot create, update or delete +check types. + +Check types for commonly encountered web protocols, such as HTTP +(``remote.http``), IMAP (``remote.imap-banner``) , SMTP +(``remote.stmp``), and DNS (``remote.dns``) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(``remote.mysql-banner``) and PostgreSQL (``remote.postgresql-banner``) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the 'extract' attribute to define the format. + +In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +`official API +documentation `__. + +Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check's parameters. For example, +using the 'extract' attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided. + +Setup +----- + +If you want to see the type for an existing Check: + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + +Alternatively, you can retrieve a specific type based on its ID: + +.. code:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); + +Attributes +---------- + ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| Name | Description | Data type | Method | ++========================+==========================================================================================================================================================================================+=============+===============================+ +| type | The name of the supported check type. | String | ``getType()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| fields | Check type fields. | Array | ``getFields()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ + +List all possible check types +----------------------------- + +.. code:: php + + $checkTypes = $service->getCheckTypes(); + + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } + diff --git a/doc/services/monitoring/Entities.md.rst b/doc/services/monitoring/Entities.md.rst new file mode 100644 index 000000000..068aec122 --- /dev/null +++ b/doc/services/monitoring/Entities.md.rst @@ -0,0 +1,77 @@ + Entities +========= + +Info +---- + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Setup +----- + +.. code:: php + + $entity = $service->getEntity(); + +For more information about setting up the ``$service`` object, please +see the userguide tutorial for `services `__. + +Attributes +---------- + ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| Name | Description | Required? | Data type | Method | ++=================+=========================================================================+=============+=====================================================+========================+ +| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ + +Create Entity +------------- + +.. code:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + +Update and delete Entity +------------------------ + +.. code:: php + + // Update + $entity->update(array( + 'label' => 'New label for my entity' + )); + + // Delete + $entity->delete(); + diff --git a/doc/services/monitoring/Metrics.md.rst b/doc/services/monitoring/Metrics.md.rst new file mode 100644 index 000000000..4231cf914 --- /dev/null +++ b/doc/services/monitoring/Metrics.md.rst @@ -0,0 +1,131 @@ + Metrics +======== + +Info +---- + +When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points. + +Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time. + +Data Granularity +---------------- + +Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. + +When you fetch metrics data points, you specify several parameters to +control the granularity of data returned: + +- A time range for the points +- Either the number of points you want returned OR the resolution of + the data you want returned + +When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution. + +**Note:** Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request. + +Consider that you want to query data for a 48-hour time range between +the timestamps ``from=1354647221000`` and ``to=1358794421000`` ( +**specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970** ). The following table shows the number +of points that the API returns for a given resolution. + +Specifying resolution to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-----------------------------+-------------------------+ +| You specify resolution... | API returns points... | ++=============================+=========================+ +| FULL | 5760 | ++-----------------------------+-------------------------+ +| MIN5 | 576 | ++-----------------------------+-------------------------+ +| MIN20 | 144 | ++-----------------------------+-------------------------+ +| MIN60 | 48 | ++-----------------------------+-------------------------+ +| MIN240 | 12 | ++-----------------------------+-------------------------+ +| MIN1440 | 2 | ++-----------------------------+-------------------------+ + +Specifying number of points to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++--------------------------------------+-----------------------------+ +| You specify points in the range... | API calculates resolution | ++======================================+=============================+ +| 3168-∞ | FULL | ++--------------------------------------+-----------------------------+ +| 360-3167 | MIN5 | ++--------------------------------------+-----------------------------+ +| 96-359 | MIN20 | ++--------------------------------------+-----------------------------+ +| 30-95 | MIN60 | ++--------------------------------------+-----------------------------+ +| 7-29 | MIN240 | ++--------------------------------------+-----------------------------+ +| 0-6 | MIN1440 | ++--------------------------------------+-----------------------------+ + +Data Point Expiration +^^^^^^^^^^^^^^^^^^^^^ + +Cloud Monitoring expires data points according to the following +schedule: + ++--------------+--------------+ +| Resolution | Expiration | ++==============+==============+ +| FULL | 2 days | ++--------------+--------------+ +| MIN5 | 7 days | ++--------------+--------------+ +| MIN20 | 15 days | ++--------------+--------------+ +| MIN60 | 30 days | ++--------------+--------------+ +| MIN240 | 60 days | ++--------------+--------------+ +| MIN1440 | 365 days | ++--------------+--------------+ + + Setup +------ + +Metrics are sub-resources of Checks. For more information about working +with Checks, please see the `relevant documentation `__. + +List all metrics +---------------- + +.. code:: php + + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } + +Fetch data points +----------------- + +.. code:: php + + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); + diff --git a/doc/services/monitoring/Notifications.md.rst b/doc/services/monitoring/Notifications.md.rst new file mode 100644 index 000000000..f361e3c9c --- /dev/null +++ b/doc/services/monitoring/Notifications.md.rst @@ -0,0 +1,281 @@ +Notifications +============= + +Info +---- + +A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time. + +For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from ``OK`` -> ``CRITICAL`` and so on). + +Setup +----- + +.. code:: php + + $id = 'ntAAAA'; + $notification = $service->getNotification($id); + +Attributes +---------- + ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| Name | Description | Data type | Method | ++===========+===========================================================================+===========================================================+====================+ +| details | A hash of notification specific details based on the notification type. | Array | ``getDetails()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| label | Friendly name for the notification. | String (1..255 chars) | ``getLabel()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ + +Test parameters +--------------- + +.. code:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + // Test it + $response = $notification->testParams($params); + + if ($response->status == 'Success') { + echo $response->message; + } + +Create Notification +------------------- + +.. code:: php + + $notification->create($params); + +Test existing notification +-------------------------- + +.. code:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; + +List Notifications +------------------ + +.. code:: php + + $notifications = $service->getNotifications(); + + foreach ($notifications as $notification) { + echo $notification->getId(); + } + +Update and delete Notifications +------------------------------- + +.. code:: php + + // Update + $notification->update(array( + 'label' => 'New notification label' + )); + + // Delete + $notification->delete(); + +Notification types +================== + +Info +---- + +Pretty self-explanatory. Rackspace Cloud Monitoring currently supports +the following notification types: + +Webhook +^^^^^^^ + +Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes: + ++-----------+------------------------------------------+---------------+ +| Name | Description | Data type | ++===========+==========================================+===============+ +| address | Email address to send notifications to | Valid email | ++-----------+------------------------------------------+---------------+ + +Email +^^^^^ + +Email alerts where the message is delivered to a specified address. It +has these attributes: + ++--------+-----------------------------------+-------------+ +| Name | Description | Data type | ++========+===================================+=============+ +| url | An HTTP or HTTPS URL to POST to | Valid URL | ++--------+-----------------------------------+-------------+ + +Setup +----- + +If you've already set up a main Notification object, and want to access +functionality for this Notification's particular Notification Type, you +can access its property: + +.. code:: php + + $type = $notification->getNotificationType(); + +Alternatively, you can retrieve an independent resource using the ID: + +.. code:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); + +List all possible notification types +------------------------------------ + +.. code:: php + + $types = $service->getNotificationTypes(); + + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } + +Notification plans +================== + +Info +---- + +A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications. + +Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states: + +- Critical +- Warning +- OK + +A notification plan, ``npTechnicalContactsEmail``, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change. + +Setup +----- + +.. code:: php + + $planId = 'npAAAA'; + $plan = $service->getNotificationPlan(); + +Attributes +---------- + ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| Name | Description | Required? | Data type | Method | ++===================+====================================================================+=============+=========================+==========================+ +| label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ + +Create Notification Plan +------------------------ + +.. code:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + +Update and delete Notification Plan +----------------------------------- + +.. code:: php + + // Update + $plan->update(array( + 'label' => 'New label for my plan' + )); + + // Delete + $plan->delete(); + +Alarm Notification History +========================== + +Info +---- + +The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm's statuses. + +Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information. + + Setup +------ + +Notification History is a sub-resource of an Alarm. For more information +about working with Alarms, please consult the relevant +`documentation `__. + +Discover which Checks have a Notification History +------------------------------------------------- + +This operation list checks for which alarm notification history is +available: + +.. code:: php + + $checks = $alarm->getRecordedChecks(); + +List Alarm Notification History for a particular Check +------------------------------------------------------ + +.. code:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); + +Get a particular Notification History item +------------------------------------------ + +.. code:: php + + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + diff --git a/doc/services/monitoring/Service.md.rst b/doc/services/monitoring/Service.md.rst new file mode 100644 index 000000000..09c25c21d --- /dev/null +++ b/doc/services/monitoring/Service.md.rst @@ -0,0 +1,23 @@ +Cloud Monitoring Service +======================== + +Initializing the Cloud Monitoring is easy - and can be done in a similar +way to all other Rackspace services: + +1. Create client and pass in auth details. For more information about + creating clients, please consult the `Client + documentation <../Clients.md>`__. +2. Use the factory method, specifying additional parameters where + necessary: + +.. code:: php + + $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); + +All three parameters are optional - if not specified, it will revert to +the service's default values which are: + +- Name = ``cloudMonitoring`` +- Region = ``DFW`` +- URL type = ``publicURL`` + diff --git a/doc/services/monitoring/Views.md.rst b/doc/services/monitoring/Views.md.rst new file mode 100644 index 000000000..a5b7245fa --- /dev/null +++ b/doc/services/monitoring/Views.md.rst @@ -0,0 +1,27 @@ +Views +===== + +Info +---- + +Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint. + +List all Views +-------------- + +\`\`\`php $views = $service->getViews(); + +foreach ($views as $view) { $entity = $view->getEntity(); + +:: + + echo $view->getTimestamp(); + +} \`\`\` + +Please consult the `iterator doc `__ for +more information about iterators. diff --git a/doc/services/monitoring/Zones.md.rst b/doc/services/monitoring/Zones.md.rst new file mode 100644 index 000000000..ffe4bba1e --- /dev/null +++ b/doc/services/monitoring/Zones.md.rst @@ -0,0 +1,67 @@ +Zones +===== + +Info +---- + +A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". +It is an abstraction for a general location from which data is +collected. + +An "endpoint," also known as a "collector," collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range. + +A check references a list of monitoring zones it should be run from. + + Setup +------ + +.. code:: php + + $zoneId = 'mzAAAAA'; + $zone = $monitoringService->getMonitoringZone($zoneId); + +Attributes +---------- + ++-----------------+------------------+-----------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+==================+===================================+========================+ +| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| label | Label | String | ``getLabel()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| source\_ips | Source IP list | Array | ``getSourceIps()`` | ++-----------------+------------------+-----------------------------------+------------------------+ + + List all zones +--------------- + +.. code:: php + + $zones = $service->getMonitoringZones(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Perform a traceroute +-------------------- + +.. code:: php + + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); + + // How many hops? + echo count($traceroute); + + // What was the first hop's IP? + echo $traceroute[0]->ip; + diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/networking/README.md.rst b/doc/services/networking/README.md.rst new file mode 100644 index 000000000..605805fe1 --- /dev/null +++ b/doc/services/networking/README.md.rst @@ -0,0 +1,108 @@ +Networking +========== + +**Networking** is a service that you can use to create virtual networks +and attach cloud devices such as servers to these networks. + +Concepts +-------- + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: A network is an isolated virtual layer-2 broadcast + domain that is typically reserved for the tenant who created it + unless you configure the network to be shared. The network is the + main entity in the Networking service. Ports and subnets are always + associated with a network. + +- **Subnet**: A subnet represents an IP address block that can be used + to assign IP addresses to virtual instances (such as servers created + using the Compute service). Each subnet must have a CIDR and must be + associated with a network. + +- **Port**: A port represents a virtual switch port on a logical + network switch. Virtual instances (such as servers created using the + Compute service) attach their interfaces into ports. The port also + defines the MAC address and the IP address(es) to be assigned to the + interfaces plugged into them. When IP addresses are associated to a + port, this also implies the port is associated with a subet, as the + IP address is taken from the allocation pool for a specific subnet. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Networking service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via an *networking service object*. +To instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance will be stored in the cloud region +specified by ``$region``. + +3. Create a network. +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a network, there is more you can do with it. See +`complete user guide for networking `__. diff --git a/doc/services/networking/USERGUIDE.md.rst b/doc/services/networking/USERGUIDE.md.rst new file mode 100644 index 000000000..bfeb27774 --- /dev/null +++ b/doc/services/networking/USERGUIDE.md.rst @@ -0,0 +1,582 @@ +Complete User Guide for the Networking Service +============================================== + +Networking is a service that you can use to create virtual networks and +attach cloud devices such as servers to these networks. + +This user guide introduces you the entities in the Networking service — +networks, subnets, and ports — and shows you how to create and manage +these entities. + +Table of contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ + + - `Client <#client>`__ + - `Networking service <#networking-service>`__ + +- `Networks <#networks>`__ + + - `Create a network <#create-a-network>`__ + - `Create multiple networks <#create-multiple-networks>`__ + - `List networks <#list-networks>`__ + - `Get a network <#get-a-network>`__ + - `Update a network <#update-a-network>`__ + - `Delete a network <#delete-a-network>`__ + +- `Subnets <#subnets>`__ + + - `Create a subnet <#create-a-subnet>`__ + - `Create multiple subnets <#create-multiple-subnets>`__ + - `List subnets <#list-subnets>`__ + - `Get a subnet <#get-a-subnet>`__ + - `Update a subnet <#update-a-subnet>`__ + - `Delete a subnet <#delete-a-subnet>`__ + +- `Ports <#ports>`__ + + - `Create a port <#create-a-port>`__ + - `Create multiple ports <#create-multiple-ports>`__ + - `List ports <#list-ports>`__ + - `Get a port <#get-a-port>`__ + - `Update a port <#update-a-port>`__ + - `Delete a port <#delete-a-port>`__ + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: An isolated virtual layer-2 broadcast domain that is + typically reserved for the tenant who created it unless it is + configured to be shared. The network is the main entity in the + Networking service. Ports and subnets are always associated with a + network. + +- **Subnet**: An IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the + Compute service). Each subnet must have a CIDR and must be associated + with a network. + +- **Port**: A virtual switch port on a logical network switch. Virtual + instances (such as servers created using the Compute service) attach + their interfaces into ports. The port also defines the MAC address + and the IP address or addresses to be assigned to the interfaces + plugged into them. When IP addresses are associated with a port, this + also implies the port is associated with a subnet because the IP + address is taken from the allocation pool for a specific subnet. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate an + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Networking service +~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via a *networking service object*. To +instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes the following arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance are stored in the cloud region specified +by ``$region``. + +Networks +-------- + +A network is an isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it unless it is configured +to be shared. The network is the main entity in the Networking service. +Ports and subnets are always associated with a network. + +Create a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple networks +~~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List networks +~~~~~~~~~~~~~ + +You can list all the networks to which you have access as shown in the +following example: + +.. code:: php + + $networks = $networkingService->listNetworks(); + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a network +~~~~~~~~~~~~~ + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code:: php + + $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code:: php + + $network->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Subnets +------- + +A subnet represents an IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the +Compute service). Each subnet must have a CIDR and must be associated +with a network. + +Create a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code:: php + + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple subnets +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List subnets +~~~~~~~~~~~~ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a subnet +~~~~~~~~~~~~ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code:: php + + $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a subnet +~~~~~~~~~~~~~~~ + +You can delete a subnet as shown in the following example: + +.. code:: php + + $subnet->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Ports +----- + +A port represents a virtual switch port on a logical network switch. +Virtual instances (such as servers created using the Compute service) +attach their interfaces into ports. The port also defines the MAC +address and the IP address or addresses to be assigned to the interfaces +plugged into them. When IP addresses are associated with a port, this +also implies the port is associated with a subnet because the IP address +is taken from the allocation pool for a specific subnet. + +Create a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code:: php + + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple ports +~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List ports +~~~~~~~~~~ + +You can list all the ports to which you have access as shown in the +following example: + +.. code:: php + + $ports = $networkingService->listPorts(); + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a port +~~~~~~~~~~ + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code:: php + + $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a port +~~~~~~~~~~~~~ + +You can delete a port as shown in the following example: + +.. code:: php + + $port->delete(); + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/object-store/Access.md.rst b/doc/services/object-store/Access.md.rst new file mode 100644 index 000000000..ee85a938e --- /dev/null +++ b/doc/services/object-store/Access.md.rst @@ -0,0 +1,75 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want + +Temporary URLs +-------------- + +Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they're CDN-enabled. + +Set "temporary URL" metadata key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You must set this "secret" value on your account, where it can be used +in a global state: + +.. code:: php + + $account = $service->getAccount(); + $account->setTempUrlSecret('my_secret'); + + echo $account->getTempUrlSecret(); + +The string argument of ``setTempUrlSecret()`` is optional - if left out, +the SDK will generate a random hashed secret for you. + +Create a temporary URL +~~~~~~~~~~~~~~~~~~~~~~ + +Once you've set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute: + +.. code:: php + + $object->getTemporaryUrl(60, 'GET'); + +To allow PUT access for 1 hour: + +.. code:: php + + $object->getTemporaryUrl(360, 'PUT'); + +Hosting websites on CloudFiles +------------------------------ + +To host a static (i.e. HTML) website on CloudFiles, you must follow +these steps: + +1. CDN-enable a container +2. Upload all HTML content. You can use nested directory structures. +3. Tell CloudFiles what to use for your default index page like this: + +.. code:: php + + $container->setStaticIndexPage('index.html'); + +4. (Optional) Tell CloudFiles which error page to use by default: + +.. code:: php + + $container->setStaticErrorPage('error.html'); + +Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object. diff --git a/doc/services/object-store/Account.md.rst b/doc/services/object-store/Account.md.rst new file mode 100644 index 000000000..a2d380b2a --- /dev/null +++ b/doc/services/object-store/Account.md.rst @@ -0,0 +1,33 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +View Account Details +-------------------- + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +.. code:: php + + $account = $service->getAccount(); + + // Either return the full Metadata object + $details = $account->getDetails(); + + // or individual values + $account->getContainerCount(); + $account->getObjectCount(); + $account->getBytesUsed(); + diff --git a/doc/services/object-store/Container.md.cdn.rst b/doc/services/object-store/Container.md.cdn.rst new file mode 100644 index 000000000..2f1c8ea68 --- /dev/null +++ b/doc/services/object-store/Container.md.cdn.rst @@ -0,0 +1,90 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +To access the CDN functionality of a particular container: + +.. code:: php + + $container = $service->getContainer('foo_bar'); + + $cdn = $container->getCdn(); + +List CDN-enabled container +-------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code:: php + + $cdnService = $service->getCdnService(); + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + + } + +CDN-enable and -disable a container +----------------------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code:: php + + $container->enableCdn(); + $container->disableCdn(); + +Serving containers through SSL +------------------------------ + +.. code:: php + + $cdn->getCdnSslUri(); + +Streaming CDN-enabled containers +-------------------------------- + +.. code:: php + + $cdn->getCdnStreamingUri(); + +iOS streaming +------------- + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code:: php + + $cdn->getIosStreamingUri(); + +CDN logging +----------- + +To enable and disable logging for your CDN: + +.. code:: php + + $cdn->enableCdnLogging(); + $cdn->disableCdnLogging(); + diff --git a/doc/services/object-store/Container.md.storage.rst b/doc/services/object-store/Container.md.storage.rst new file mode 100644 index 000000000..89798fdd0 --- /dev/null +++ b/doc/services/object-store/Container.md.storage.rst @@ -0,0 +1,218 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + // Create a client object to communicate with various Rackspace Cloud services. + $client = new Rackspace(RACKSPACE_US, array( + 'username' => 'Replace this with your Rackspace Cloud user name', + 'apiKey' => 'Replace this with your Rackspace Cloud API key' + )); + + // Create a service object to use the object store service. The sample code + // creates the object store in the 'DFW' region. + $service = $client->objectStoreService('cloudFiles', 'DFW'); + +Create container +---------------- + +To create a new container, you just need to define its name: + +.. code:: php + + $container = $service->createContainer('my_amazing_container'); + +If the response returned is ``FALSE``, there was an API error - most +likely due to the fact you have a naming collision. + +Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +List containers +--------------- + +Return a list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $containerList = $service->listContainers(); + + while ($container = $containerList->next()) { + // Do stuff; some examples below + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } + +Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite's +memcmp() function, regardless of text encoding. + +The list is limited to 10,000 containers at a time. See 1.3 for ways to +limit and navigate this list. + +Return a formatted list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, the SDK only supports JSON-formatted responses. + +Controlling a large list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may limit and control this list of results by using the ``marker`` +and ``end_marker`` parameters. The former parameter (``marker``) tells +the API where to begin the list, and the latter (``end_marker``) tells +it where to end the list. You may use either of them independently or +together. You may also use the ``limit`` parameter to fix the number of +containers returned. + +To list a set of containers between two fixed points: + +.. code:: php + + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); + +Or to return a limited set: + +.. code:: php + + $someContainers = $service->listContainers(array('limit' => 560)); + +Get container +------------- + +To retrieve a certain container, either to access its object or +metadata: + +.. code:: php + + $container = $service->getContainer('container_name'); + + echo $container->getObjectCount(); + echo $container->getBytesUsed(); + +Delete container +---------------- + +Deleting a container is easy: + +.. code:: php + + $container->delete(); + +Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +``$deleteObjects`` parameter to ``TRUE`` like so: + +.. code:: php + + $container->delete(TRUE); + +You can also do it manually: + +.. code:: php + + $container->deleteAllObjects(); + $container->delete(); + +Create or update container metadata +----------------------------------- + +.. code:: php + + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); + +Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To *append* values +to the current metadata: + +.. code:: php + + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); + +If you only want to set the metadata to the local object, and not +immediately retain these values on the API, you can use a standard +setter method - which can contribute to eventual actions like an update: + +.. code:: php + + $container->setMetadata(array('Foo' => 'Bar')); + +Container quotas +---------------- + +The container\_quotas middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check. + +To set quotas for a container: + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); + +And to retrieve them: + +.. code:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); + +Access log delivery +------------------- + +To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage). + +.. code:: php + + $container->enableLogging(); + $container->disableLogging(); + +Syncing containers +------------------ + +You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory: + +.. code:: php + + $container->uploadDirectory('/home/Jamie/blog'); + +There are four scenarios you should be aware of: + ++------------------------+-----------------------+----------------------+--------------------------------+ +| Local | Remote | Comparison | Action | ++========================+=======================+======================+================================+ +| File exists | File exists | Identical checksum | No action | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File exists | Different checksum | Local file overwrites remote | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File does not exist | - | Local file created in Swift | ++------------------------+-----------------------+----------------------+--------------------------------+ +| Files does not exist | File exists | - | Remote file deleted | ++------------------------+-----------------------+----------------------+--------------------------------+ + diff --git a/doc/services/object-store/Migrating.md.storage.rst b/doc/services/object-store/Migrating.md.storage.rst new file mode 100644 index 000000000..99738554b --- /dev/null +++ b/doc/services/object-store/Migrating.md.storage.rst @@ -0,0 +1,101 @@ +Migrating containers (across regions) +===================================== + +Introduction +------------ + +Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a ``COPY`` +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality. + +You **will** be charged for bandwidth between regions, so it's advisable +to use ServiceNet where possible (which is free). + +Requirements +------------ + +- You must install the full Guzzle package, so that the process can + take advantage of Guzzle's batching functionality (it allows parallel + requests to be batched for greater efficiency). You can do this by + running: + +.. code:: bash + + php composer.phar install --dev + +- Depending on the size and number of transfer items, you will need to + raise PHP's memory limit: + +.. code:: php + + ini_set('memory_limit', '512M'); + +- You will need to enact some kind of backoff/retry strategy for rate + limits. Guzzle comes with a convenient feature that just needs to be + added as a normal subscriber: + +.. code:: php + + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + +This tells the client to retry up to ``10`` times for failed requests +have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + +Setup +----- + +You can access all this functionality by executing: + +.. code:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); + + $iadService->migrateContainer($oldContainer, $newContainer); + +It's advisable to do this process in a Cloud Server in one of the two +regions you're migrating to/from. This allows you to use ``privateURL`` +as the third argument in the ``objectStoreService`` methods like this: + +.. code:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + +This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free. + +Options +------- + +You can pass in an array of arguments to the method: + +.. code:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); + + $iadService->migrateContainer($oldContainer, $newContainer, $options); + +Options explained +~~~~~~~~~~~~~~~~~ + ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| Name | Description | Default | ++========================+===============================================================================================================================================================================================================================================================================================================================================+===========+ +| ``read.pageLimit`` | When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional ``objectList`` method, which calls the ``PaginatedIterator``. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API) | 10,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``read.batchLimit`` | After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together. | 1,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ + diff --git a/doc/services/object-store/Object.md.cdn.rst b/doc/services/object-store/Object.md.cdn.rst new file mode 100644 index 000000000..64ac143b3 --- /dev/null +++ b/doc/services/object-store/Object.md.cdn.rst @@ -0,0 +1,25 @@ +Setup +----- + +You will need to instantiate the container object and access its CDN +functionality as `documented +here `__. + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); + diff --git a/doc/services/object-store/Object.md.storage.rst b/doc/services/object-store/Object.md.storage.rst new file mode 100644 index 000000000..16dab18f4 --- /dev/null +++ b/doc/services/object-store/Object.md.storage.rst @@ -0,0 +1,330 @@ +Setup +----- + +Conceptually, a container contains objects (also known as files). In +order to work with objects, you will need to instantiate a container +object first as `documented +here `__. + +Note on object properties +------------------------- + +Please be aware that you cannot directly access the properties of +DataObject anymore, you **must** use appropriate getter/ setter methods: + ++----------------------+------------------------+ +| Property | Method | ++======================+========================+ +| Parent container | ``getContainer`` | ++----------------------+------------------------+ +| Name | ``getName`` | ++----------------------+------------------------+ +| Body of file | ``getContent`` | ++----------------------+------------------------+ +| Size of file | ``getContentLength`` | ++----------------------+------------------------+ +| Type of file | ``getContentType`` | ++----------------------+------------------------+ +| ETag checksum | ``getEtag`` | ++----------------------+------------------------+ +| Last modified date | ``getLastModified`` | ++----------------------+------------------------+ + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + + **Note:** Unlike previous versions, you do not need to manually + specify your object's content type. The API will do this for you. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +To upload a single/basic file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $data = fopen('/path/to/sample.mp3', 'r+'); + + // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) + + $meta = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow' + ); + + $metaHeaders = DataObject::stockHeaders($meta); + $customHeaders = array(); + $allHeaders = $metaHeaders + $customHeaders; + + $container->uploadObject('sample.mp3', $data, $allHeaders); + +To upload multiple small-to-mid sized files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + +To upload large files +~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your +transfer, upon which you can execute your upload functionality. For your +convenience, the Container resource object contains a simple method to +do this heavy lifting for you: + +.. code:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/jamie/video.mov', + 'metadata' => array( + 'Author' => 'Jamie' + ), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifest is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + // ... do something + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code:: php + + $container->objectList(array('prefix' => 'logFile_')); + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code:: php + + $file = $container->getObject('summer_vacation.mp4'); + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +:: + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + +Update an existing object +------------------------- + +Updating content is easy: + +.. code:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + +Copy object +----------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code:: php + + $object->copy('/container_2/new_object_name'); + +Delete object +------------- + +.. code:: php + + $object->delete(); + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code:: php + + $container->getPartialObject('summer_vacation.mp4'); + +In order to access the metadata on a partial or complete object, use: + +.. code:: php + + $object->getMetadata(); + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code:: php + + $object->refresh(); + +You can also update to get the latest metadata: + +.. code:: php + + $object->retrieveMetadata(); + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + +Bulk delete +----------- + +Bulk delete a set of paths: + +.. code:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); + diff --git a/doc/services/object-store/README.md.rst b/doc/services/object-store/README.md.rst new file mode 100644 index 000000000..01cf26733 --- /dev/null +++ b/doc/services/object-store/README.md.rst @@ -0,0 +1,86 @@ +Object Store +============ + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Specifically, a cloud is made up of one or more regions. Each region can +have several **containers**, created by a user. Each container can +container several **objects** (sometimes referred to as files), uploaded +by the user. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choose one of the following two options: + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Object Store service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +3. Create a container for your objects (also referred to as files). +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +4. Upload an object to the container. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +There is a lot more you can do with containers and objects. See the +`complete user guide to the Object Store service `__. diff --git a/doc/services/object-store/USERGUIDE.md.rst b/doc/services/object-store/USERGUIDE.md.rst new file mode 100644 index 000000000..d9732ea36 --- /dev/null +++ b/doc/services/object-store/USERGUIDE.md.rst @@ -0,0 +1,900 @@ +The Complete User Guide to the Object Store Service +=================================================== + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the object store service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'apiKey' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Object Store Service +~~~~~~~~~~~~~~~~~~~~ + +All operations on the object store are done via an object store service +object. + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +Containers +---------- + +A **container** defines a namespace for **objects**. An object with the +same name in two different containers represents two different objects. + +For example, you may create a container called ``logos`` to hold all the +image files for your blog. + +A container may contain zero or more objects in it. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Create Container +~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Details +~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single container's details by using its name. An +instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. + +.. code:: php + + $container = $objectStoreService->getContainer('logos'); + + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + +[ `Get the executable PHP script for this +example `__ ] + +List Containers +~~~~~~~~~~~~~~~ + +You can retrieve a list of all your containers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $containers = $objectStoreService->listContainers(); + foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Set or Update Container Metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can set metadata on a container. + +.. code:: php + + $container->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a container. + +.. code:: php + + $containerMetadata = $container->getMetadata(); + +[ `Get the executable PHP script for this +example `__ ] + +Delete Container +~~~~~~~~~~~~~~~~ + +When you no longer have a need for the container, you can remove it. + +If the container is empty (that is, it has no objects in it), you can +remove it as shown below: + +.. code:: php + + $container->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +If the container is not empty (that is, it has objects in it), you have +two choices in how to remove it: + +- `Individually remove each object <#delete-object>`__ in the + container, then remove the container itself as shown above, or + +- Remove the container and all the objects within it as shown below: + + .. code:: php + + $container->delete(true); + + [ `Get the executable PHP script for this + example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in a container. + +.. code:: php + + $containerObjectCount = $container->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerObjectCount`` will contain the number +of objects in the container represented by ``$container``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by a container, in bytes. + +.. code:: php + + $containerSizeInBytes = $container->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerSizeInBytes`` will contain the space +used, in bytes, by the container represented by ``$container``. + +Container Quotas +~~~~~~~~~~~~~~~~ + +Set Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum number of objects that may be stored +in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = 25; + $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Set Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum total space (in bytes) used by +objects in a container. + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; + $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum number of objects that may be +stored in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum total space (in bytes) used +by objects in a container. + +.. code:: php + + $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Objects +------- + +An **object** (sometimes referred to as a file) is the unit of storage +in an Object Store. An object is a combination of content (data) and +metadata. + +For example, you may upload an object named ``php-elephant.jpg``, a JPEG +image file, to the ``logos`` container. Further, you may assign metadata +to this object to indicate that the author of this object was someone +named Jane Doe. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Upload Object +~~~~~~~~~~~~~ + +Once you have created a container, you can upload objects to it. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +It is also possible to upload an object and associate metadata with it. + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData, $allHeaders); + +[ `Get the executable PHP script for this +example `__ ] + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Pseudo-hierarchical Folders +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Although you cannot nest directories in an Object Store, you can +simulate a hierarchical structure within a single container by adding +forward slash characters (``/``) in the object name. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'languages/php/elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. Within that container, the filename is +``languages/php/elephant.jpg``, where ``languages/php/`` is a +pseudo-hierarchical folder hierarchy. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Upload Multiple Objects +^^^^^^^^^^^^^^^^^^^^^^^ + +You can upload more than one object at a time to a container. + +.. code:: php + + $objects = array( + array( + 'name' => 'php-elephant.jpg', + 'path' => '/path/to/local/php-elephant.jpg' + ), + array( + 'name' => 'python-snake.jpg', + 'path' => '/path/to/local/python-snake.jpg' + ), + a + ); + + $container->uploadObjects($objects); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, the contents of two files present on the local +filesystem are uploaded as objects to the container referenced by +``$container``. + +Instead of specifying the ``path`` key in an element of the ``$objects`` +array, you can specify a ``body`` key whose value is a string or a +stream representation. + +Finally, you can pass headers as the second parameter to the +``uploadObjects`` method. These headers will be applied to every object +that is uploaded. + +:: + + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $container->uploadObjects($objects, $allHeaders); + +[ `Get the executable PHP script for this +example `__ +] + +In the example above, every object referenced within the ``$objects`` +array will be uploaded with the same metadata. + +Large Objects +~~~~~~~~~~~~~ + +If you want to upload objects larger than 5GB in size, you must use a +different upload process. + +.. code:: php + + $options = array( + 'name' => 'san_diego_vacation_video.mp4', + 'path' => '/path/to/local/videos/san_diego_vacation.mp4' + ); + $objectTransfer = $container->setupObjectTransfer($options); + $objectTransfer->upload(); + +[ `Get the executable PHP script for this +example `__ ] + +The process shown above will automatically partition your large object +into small chunks and upload them concurrently to the container +represented by ``$container``. + +You can tune the parameters of this process by specifying additional +options in the ``$options`` array. Here is a complete listing of keys +that can be specified in the ``$options`` array: + ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ +| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ + +Auto-extract Archive Files +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can upload a tar archive file and have the Object Store service +automatically extract it into a container. + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $localArchiveFileName = '/path/to/local/image_files.tar.gz'; + $remotePath = 'images/'; + + $fileData = fopen($localArchiveFileName, 'r'); + $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, a local archive file named ``image_files.tar.gz`` +is uploaded to an Object Store container named ``images`` (defined by +the ``$remotePath`` variable). + +Note that while we call ``fopen`` to open a file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``bulkExtract`` call. + +The third parameter to ``bulkExtract`` is the type of the archive file +being uploaded. The acceptable values for this are: + +- ``UrlType::TAR`` for tar archive files, *or*, +- ``UrlType:TAR_GZ`` for tar archive files that are compressed with + gzip, *or* +- ``UrlType::TAR_BZ`` for tar archive file that are compressed with + bzip + +Note that the value of ``$remotePath`` could have been a +(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as +``images/blog`` as well. + +List Objects in a Container +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all the objects stored in a container. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $objects = $container->objectList(); + foreach ($objects as $object) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +You can list only those objects in the container whose names start with +a certain prefix. + +.. code:: php + + $options = array( + 'prefix' => 'php' + ); + + $objects = $container->objectList($options); + +[ `Get the executable PHP script for this +example `__ ] + +In general, the ``objectList()`` method described above takes an +optional parameter (``$options`` in the example above). This parameter +is an associative array of various options. Here is a complete listing +of keys that can be specified in the ``$options`` array: + ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++==================+=============================================================================+=============+=============+=================+=========================+ +| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ + +Retrieve Object +~~~~~~~~~~~~~~~ + +You can retrieve an object and its metadata, given the object's +container and name. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getObject($objectName); + + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + + $objectContent = $object->getContent(); + + /** @var $objectContent Guzzle\Http\EntityBody **/ + + // Write object content to file on local filesystem. + $objectContent->rewind(); + $stream = $objectContent->getStream(); + $localFilename = tempnam("/tmp", 'php-opencloud-'); + file_put_contents($localFilename, $stream); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$object`` is the object named +``php-elephant.jpg`` in the container represented by ``$container``. +Further, ``$objectContent`` represents the contents of the object. It is +of type +```Guzzle\Http\EntityBody`` `__. + +Retrieve Object Metadata +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve just an object's metadata without retrieving its +contents. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getPartialObject($objectName); + $objectMetadata = $object->getMetadata(); + + /** @var $objectMetadata \OpenCloud\Common\Metadata **/ + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, while ``$object`` is an instance of +``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only +partially populated. Specifically, only properties of the instance +relating to object metadata are populated. + +Temporary URLs +~~~~~~~~~~~~~~ + +The Temporary URL feature allows you to create limited-time Internet +addresses that allow you to grant limited access to your Object Store +account. Using this feature, you can allow others to retrieve or place +objects in your Object Store account for a specified amount of time. +Access to the temporary URL is independent of whether or not your +account is `CDN-enabled <#cdn-containers>`__. Even if you do not +CDN-enable a container, you can still grant temporary public access +through a temporary URL. + +First, you must set the temporary URL secret on your account. This is a +one-time operation; you only need to perform it the very first time you +wish to use the temporary URLs feature. + +.. code:: php + + $account->setTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation is carried out on ``$account``, which is an +instance of ``OpenCloud\ObjectStore\Resource\Account``, a class +representing `your object store account <#accounts>`__. + +The above operation will generate a random secret and set it on your +account. Instead of a random secret, if you wish to provide a secret, +you can supply it as a parameter to the ``setTempUrlSecret`` method. + +.. code:: php + + $account->setTempUrlSecret(''); + +[ `Get the executable PHP script for this +example `__ +] + +Once a temporary URL secret has been set on your account, you can +generate a temporary URL for any object in your Object Store. + +.. code:: php + + $expirationTimeInSeconds = 3600; // one hour from now + $httpMethodAllowed = 'GET'; + $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, a temporary URL for the object is generated. This +temporary URL will provide public access to the object for an hour (3600 +seconds), as specified by the ``$expirationTimeInSeconds`` variable. +Further, only GET HTTP methods will be allowed on this URL, as specified +by the ``$httpMethodAllowed`` variable. The other value allowed for the +``$httpMethodAllowed`` variable would be ``PUT``. + +You can also retrieve the temporary URL secret that has been set on your +account. + +.. code:: php + + $tempUrlSecret = $account->getTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Update Object +~~~~~~~~~~~~~ + +You can update an object's contents (as opposed to `updating its +metadata <#update-object-metadata>`__) by simply re-\ `uploading the +object <#upload-object>`__ to its container using the same object name +as before. + +Update Object Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can update an object's metadata after it has been uploaded to a +container. + +.. code:: php + + $object->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Copy Object +~~~~~~~~~~~ + +You can copy an object from one container to another, provided the +destination container already exists. + +.. code:: php + + $object->copy('logos_copy/php.jpg'); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, both the name of the destination container +(``logos_copy``)and the name of the destination object (``php.jpg``) +have to be specified, separated by a ``/``. + +Delete Object +~~~~~~~~~~~~~ + +When you no longer need an object, you can delete it. + +.. code:: php + + $object->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Bulk Delete +~~~~~~~~~~~ + +While you can delete individual objects as shown above, you can also +delete objects and empty containers in bulk. + +.. code:: php + + $objectStoreService->bulkDelete(array( + 'logos/php-elephant.png', + 'logos/python-snakes.png', + 'some_empty_container' + )); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, two objects (``some_container/object_a.png``, +``some_other_container/object_z.png``) and one empty container +(``some_empty_container``) are all being deleted in bulk via a single +command. + +CDN Containers +-------------- + +Note: The functionality described in this section is available only on +the Rackspace cloud. It will not work as described when working with a +vanilla OpenStack cloud. + +Any container can be converted to a CDN-enabled container. When this is +done, the objects within the container can be accessed from anywhere on +the Internet via a URL. + +Enable CDN Container +~~~~~~~~~~~~~~~~~~~~ + +To take advantage of CDN capabilities for a container and its objects, +you must CDN-enable that container. + +.. code:: php + + $container->enableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Public URLs +~~~~~~~~~~~ + +Once you have CDN-enabled a container, you can retrieve a +publicly-accessible URL for any of its objects. There are four types of +publicly-accessible URLs for each object. Each type of URL is meant for +a different purpose. The sections below describe each of these URL types +and how to retrieve them. + +HTTP URL +^^^^^^^^ + +You can use this type of URL to access the object over HTTP. + +:: + + $httpUrl = $object->getPublicUrl(); + +[ `Get the executable PHP script for this +example `__ ] + +Secure HTTP URL +^^^^^^^^^^^^^^^ + +You can use this type of URL to access the object over HTTP + TLS/SSL. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $httpsUrl = $object->getPublicUrl(UrlType::SSL); + +[ `Get the executable PHP script for this +example `__ ] + +Streaming URL +^^^^^^^^^^^^^ + +You can use this type of URL to stream a video or audio object using +Adobe's HTTP Dynamic Streaming. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +IOS Streaming URL +^^^^^^^^^^^^^^^^^ + +You can use this type of URL to stream an audio or video object to an +iOS device. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +Update CDN Container TTL +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update the TTL of a CDN-enabled container. + +.. code:: php + + $cdnContainer = $container->getCdn(); + $cdnContainer->setTtl(); + +[ `Get the executable PHP script for this +example `__ ] + +Disable CDN Container +~~~~~~~~~~~~~~~~~~~~~ + +If you no longer need CDN capabilities for a container, you can disable +them. + +.. code:: php + + $container->disableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Accounts +-------- + +An **account** defines a namespace for **containers**. An account can +have zero or more containers in it. + +Retrieve Account +~~~~~~~~~~~~~~~~ + +You must retrieve the account before performing any operations on it. + +.. code:: php + + $account = $objectStoreService->getAccount(); + +Get Container Count +~~~~~~~~~~~~~~~~~~~ + +You can quickly find out how many containers are in your account. + +.. code:: php + + $accountContainerCount = $account->getContainerCount(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in your account. + +.. code:: php + + $accountObjectCount = $account->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountObjectCount`` will contain the number of +objects in the account represented by ``$account``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by your account, in bytes. + +.. code:: php + + $accountSizeInBytes = $account->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountSizeInBytes`` will contain the space +used, in bytes, by the account represented by ``$account``. diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/orchestration/README.md.rst b/doc/services/orchestration/README.md.rst new file mode 100644 index 000000000..1a983d187 --- /dev/null +++ b/doc/services/orchestration/README.md.rst @@ -0,0 +1,98 @@ +Orchestration +============= + +**Orchestration** is a service that can be used to create and manage +cloud resources. Examples of such resources are databases, load +balancers, servers and software installed on them. + +Concepts +-------- + +To use the Orchestration service effectively, you should understand +several key concepts: + +- **Template**: An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + +- **Resource**: A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + +- **Stack**: A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Orchestration service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object as shown in the following example: + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +3. Create a stack from a template. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a stack, there is more you can do with it. See +`complete user guide for orchestration `__. diff --git a/doc/services/orchestration/USERGUIDE.md.rst b/doc/services/orchestration/USERGUIDE.md.rst new file mode 100644 index 000000000..98bd51db7 --- /dev/null +++ b/doc/services/orchestration/USERGUIDE.md.rst @@ -0,0 +1,672 @@ +Complete User Guide for the Orchestration Service +================================================= + +Orchestration is a service that you can use to create and manage cloud +resources such as databases, load balancers, and servers, and the +software installed on servers. + +Table of Contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ +- `Client <#client>`__ +- `Orchestration service <#orchestration-service>`__ +- `Templates <#templates>`__ +- `Validate template <#validate-template>`__ + + - `Validate a template from a + file <#validate-a-template-from-a-file>`__ + - `Validate Template from URL <#validate-template-from-url>`__ + +- `Stacks <#stacks>`__ +- `Preview stack <#preview-stack>`__ + + - `Preview a stack from a template + file <#preview-a-stack-from-a-template-file>`__ + - `Preview a stack from a template + URL <#preview-a-stack-from-a-template-url>`__ + +- `Create stack <#create-stack>`__ + + - `Create a stack from a template + file <#create-a-stack-from-a-template-file>`__ + - `Create a stack from a template + URL <#create-a-stack-from-a-template-url>`__ + +- `List stacks <#list-stacks>`__ +- `Get stack <#get-stack>`__ +- `Get stack template <#get-stack-template>`__ +- `Update stack <#update-stack>`__ + + - `Update a stack from a template + file <#update-a-stack-from-a-template-file>`__ + - `Update Stack from Template + URL <#update-stack-from-template-url>`__ + +- `Delete stack <#delete-stack>`__ +- `Abandon Stack <#abandon-stack>`__ +- `Adopt stack <#adopt-stack>`__ +- `Stack resources <#stack-resources>`__ +- `List stack resources <#list-stack-resources>`__ +- `Get stack resource <#get-stack-resource>`__ +- `Get stack resource metadata <#get-stack-resource-metadata>`__ +- `Stack resource events <#stack-resource-events>`__ +- `List stack events <#list-stack-events>`__ +- `List stack resource events <#list-stack-resource-events>`__ +- `Get stack resource event <#get-stack-resource-event>`__ +- `Resource types <#resource-types>`__ +- `List resource types <#list-resource-types>`__ +- `Get resource type <#get-resource-type>`__ +- `Get resource type template <#get-resource-type-template>`__ +- `Build info <#build-info>`__ +- `Get build info <#get-build-info>`__ + +Concepts +-------- + +To use the Orchestration service effectively, you should understand the +following key concepts: + +- **Template**: A JSON or YAML document that describes how a set of + resources should be assembled to produce a working deployment. The + template specifies the resources to use, the attributes of these + resources that are parameterized and the information that is sent to + the user when a template is instantiated. + +- **Resource**: Some component of your architecture (a cloud server, a + group of scaled cloud servers, a load balancer, some configuration + management system, and so on) that is defined in a template. + +- **Stack**: A running instance of a template. When a stack is created, + the resources specified in the template are created. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+==========================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +Templates +--------- + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validate template +~~~~~~~~~~~~~~~~~ + +Before you use a template to create a stack, you might want to validate +it. + +Validate a template from a file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Validate Template from URL +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Stacks +------ + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + +Preview stack +~~~~~~~~~~~~~ + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Preview a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create stack +~~~~~~~~~~~~ + +You can create a stack from a template. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +List stacks +~~~~~~~~~~~ + +You can list all the stacks that you have created as shown in the +following example: + +.. code:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack +~~~~~~~~~ + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code:: php + + $stack = $orchestrationService->getStack('simple-lamp-setup'); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack template +~~~~~~~~~~~~~~~~~~ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code:: php + + $stackTemplate = $stack->getTemplate(); + /** @var $stackTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update stack +~~~~~~~~~~~~ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + +Update a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code:: php + + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Update Stack from Template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code:: php + + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Delete stack +~~~~~~~~~~~~ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code:: php + + $stack->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Abandon Stack +~~~~~~~~~~~~~ + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code:: php + + $abandonStackData = $stack->abandon(); + /** @var $abandonStackData string **/ + + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation returns data about the abandoned stack as a +string. You can use this data to recreate the stack by using the `adopt +stack <#adopt-stack>`__ operation. + +Adopt stack +~~~~~~~~~~~ + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code:: php + + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resources +--------------- + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + +List stack resources +~~~~~~~~~~~~~~~~~~~~ + +You can list all the resources for a stack as shown in the following +example: + +.. code:: php + + $resources = $stack->listResources(); + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource +~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code:: php + + $resource = $stack->getResource('load-balancer'); + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceMetadata = $resource->getMetadata(); + /** @var $resourceMetadata \stdClass **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resource events +--------------------- + +Operations on resources within a stack (such as the creation of a +resource) produce events. + +List stack events +~~~~~~~~~~~~~~~~~ + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code:: php + + $stackEvents = $stack->listEvents(); + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List stack resource events +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceEvents = $resource->listEvents(); + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource event +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code:: php + + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + +[ `Get the executable PHP script for this +example `__ ] + +Resource types +-------------- + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +~~~~~~~~~~~~~~~~~~~ + +You can list all supported resource types as shown in the following +example: + +.. code:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type +~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code:: php + + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type template +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code:: php + + $resourceTypeTemplate = $resourceType->getTemplate(); + /** @var $resourceTypeTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Build info +---------- + +Get build info +~~~~~~~~~~~~~~ + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code:: php + + $buildInfo = $orchestrationService->getBuildInfo(); + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/queues/Claim.md.rst b/doc/services/queues/Claim.md.rst new file mode 100644 index 000000000..42161536d --- /dev/null +++ b/doc/services/queues/Claim.md.rst @@ -0,0 +1,164 @@ +1. Introduction +--------------- + +A **Claim** is the process of a worker checking out a message to perform +a task. Claiming a message prevents other workers from attempting to +process the same messages. + +2. Setup +-------- + +A Claim is initialized on its parent object, a Queue: + +.. code:: php + + // To initialize an empty object: + $claim = $queue->getClaim(); + + // or retrieve a specific claim: + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +3. Claim messages +----------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +``204 No Content`` message. + +When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it. + +The age given for a claim is relative to the server's clock. The claim's +age is useful for determining how quickly messages are getting processed +and whether a given message's claim is about to expire. + +When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message. + +3.2 Attributes +~~~~~~~~~~~~~~ + +The ``ttl`` attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request. + +The ``grace`` attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted. + +The ``limit`` attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional. + +3.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); + +4. Query claim +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation queries the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +4.2 Attributes +~~~~~~~~~~~~~~ + +Claim ID. + +4.3 Code +~~~~~~~~ + +.. code:: php + + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +5. Update claim +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific **Claim** and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL. + +5.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +5.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); + +6. Release claim +---------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored. + +This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers. + +6.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +6.3 Code +~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/services/queues/Message.md.rst b/doc/services/queues/Message.md.rst new file mode 100644 index 000000000..ca67e22bb --- /dev/null +++ b/doc/services/queues/Message.md.rst @@ -0,0 +1,257 @@ +1. Introduction +--------------- + +A **Message** is a task, a notification, or any meaningful data that a +producer or publisher sends to the queue. A message exists until it is +deleted by a recipient or automatically by the system based on a TTL +(time-to-live) value. + +2. Setup +-------- + +A message is initialized from its parent object, a Queue: + +.. code:: php + + // Setup an empty object + $message = $queue->getMessage(); + + // or retrieve an existing one + $message = $queue->getMessage(''); + +3. Post message +--------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation posts the specified message or messages. You can submit +up to 10 messages in a single request. + +When posting new messages, you specify only the ``body`` and ``ttl`` for +the message. The API will insert metadata, such as ID and age. + +3.2 Parameters +~~~~~~~~~~~~~~ + +How you pass through the array structure depends on whether you are +executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the +same. + +The ``body`` attribute specifies an arbitrary document that constitutes +the body of the message being sent. The size of this body is limited to +256 KB, excluding whitespace. + +The ``ttl`` attribute specifies how long the server waits before marking +the message as expired and removing it from the queue. The value of ttl +must be between 60 and 1209600 seconds (14 days). Note that the server +might not actually delete the message until its age has reached up to +(ttl + 60) seconds, to allow for flexibility in storage implementations. + +3.3 Code samples +~~~~~~~~~~~~~~~~ + +3.3.1 Posting a single message +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + use OpenCloud\Common\Constants\Datetime; + + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013 + ), + 'ttl' => 2 * Datetime::DAY + )); + +3.3.2 Post a batch of messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Please note that the list of messages will be truncated at 10. For more, +please execute another method call. + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); + + $queue->createMessages($messages); + +4. Get messages +--------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the message or messages in the specified queue. + +Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations. + +Results are ordered by age, oldest message first. + +4.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++====================+=========+============+============================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ +| marker | Query | String | Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +5. Get a set of messages by ID +------------------------------ + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format +multiple message ID values by separating them with commas +(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies +the claim ID with which the message is associated. Optional.\| +\|----\|-----\|----\|-----------\| + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $messages = $queue->listMessages(array('ids' => $ids)); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +6. Delete a set of messages by ID +--------------------------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted. + +6.2 Parameters +~~~~~~~~~~~~~~ + +An array of IDs. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $response = $queue->deleteMessages($ids); + +7. Get a specific message +------------------------- + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the specified message from the specified queue. + +7.2 Parameters +~~~~~~~~~~~~~~ + +Message ID. + +7.3 Object properties +~~~~~~~~~~~~~~~~~~~~~ + +``href`` is an opaque relative URI that the client can use to uniquely +identify a message resource and interact with it. + +``ttl`` is the TTL that was set on the message when it was posted. The +message expires after (ttl - age) seconds. + +``age`` is the number of seconds relative to the server's clock. + +``body`` is the arbitrary document that was submitted with the original +request to post the message. + +7.4 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message = $queue->getMessage('51db6f78c508f17ddc924357'); + +8. Delete message +----------------- + +8.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified message. + +8.2 Parameters +~~~~~~~~~~~~~~ + +None. + +8.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/services/queues/Queue.md.rst b/doc/services/queues/Queue.md.rst new file mode 100644 index 000000000..1fe9f7036 --- /dev/null +++ b/doc/services/queues/Queue.md.rst @@ -0,0 +1,197 @@ +1. Introduction +--------------- + +A Queue is an entity that holds messages. Ideally, a queue is created +per work type. For example, if you want to compress files, you would +create a queue dedicated to this job. Any application that reads from +this queue would only compress files. + +2. Setup +-------- + +.. code:: php + + $service = $client->queuesService('cloudQueues', 'ORD'); + +3. Client IDs +------------- + +With most of Marconi's operation, you must specify a **Client ID** which +will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + +4. List queues +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation lists queues for the project. The queues are sorted +alphabetically by name. + +4.2 Parameters +~~~~~~~~~~~~~~ + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|marker\|Query\|​String\|Specifies the name of the last queue received +in a previous request, or none to get the first page of results. +Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to +return. The default value for the number of queues returned is 10. If +you do not specify this parameter, the default number of queues is +returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether +queue metadata is included in the response. The default value for this +parameter is false, which excludes the metadata. Optional.\| +\|----\|-----\|----\|-----------\| + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queues = $service->listQueues(); + + while ($queue = $queues->next()) { + echo $queue->getName() . PHP_EOL; + } + +5. Create queue +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation creates a new queue. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A string representation of the name for your new Queue. The name must +not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->createQueue('new_queue'); + +6. Retrieve queue +----------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +Returns a ``Queue`` object for use. + +6.2 Parameters +~~~~~~~~~~~~~~ + +Queue name. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->getQueue('new_queue'); + +7. Check queue existence +------------------------ + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +7.2 Parameters +~~~~~~~~~~~~~~ + +7.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + +8. Update queue metadata (permanently to the API) +------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +4.2 Parameters +~~~~~~~~~~~~~~ + +Hash of key pairs. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + +9. Retrieve the queue metadata (fresh from the API) +--------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns metadata, such as message TTL, for the queue. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadata = $queue->retrieveMetadata(); + + print_r($metadata->toArray()); + +10. Get queue stats +------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->getStats(); + diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst new file mode 100644 index 000000000..e69de29bb From fcdd78a2f8caadc8a152b31dc9d5072eb06395f4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:47 +0100 Subject: [PATCH 417/835] Add remaining top-level things --- doc/caching-creds.rst | 44 +++++++++++ doc/debugging.rst | 99 +++++++++++++++++++++++ doc/index.rst | 23 ++++++ doc/iterators.rst | 178 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+) create mode 100644 doc/caching-creds.rst create mode 100644 doc/debugging.rst create mode 100644 doc/index.rst create mode 100644 doc/iterators.rst diff --git a/doc/caching-creds.rst b/doc/caching-creds.rst new file mode 100644 index 000000000..3c3e8df23 --- /dev/null +++ b/doc/caching-creds.rst @@ -0,0 +1,44 @@ +Caching credentials +=================== + +You can speed up your API operations by caching your credentials in a +(semi-)permanent location, such as your DB or local filesystem. This +enable subsequent requests to access a shared resource, instead of +repetitively having to re-authenticate on every thread of execution. + +Tokens are valid for 24 hours, so you can effectively re-use the same +cached value for that period. If you try to use a cached version that +has expired, an authentication request will be made. + +Filesystem example +------------------ + +In this example, credentials will be saved to a file in the local +filesystem. Be sure to exclude it from your VCS. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); + + $cacheFile = __DIR__ . '/.opencloud_token'; + + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } + + $token = $client->getTokenObject(); + + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } + +In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/debugging.rst b/doc/debugging.rst new file mode 100644 index 000000000..445915284 --- /dev/null +++ b/doc/debugging.rst @@ -0,0 +1,99 @@ +Debugging +========= + +There are two important debugging strategies to use when encountering +problems with HTTP transactions. + +Strategy 1: Meaningful exception handling +----------------------------------------- + +If the API returns a ``4xx`` or ``5xx`` status code, it indicates that +there was an error with the sent request, meaning that the transaction +cannot be adequately completed. + +The Guzzle HTTP component, which forms the basis of our SDK's transport +layer, utilizes `numerous exception +classes `__ +to handle this error logic. + +The two most common exception classes are: + +- ``Guzzle\Http\Exception\ClientErrorResponseException``, which is + thrown when a ``4xx`` response occurs + +- ``Guzzle\Http\Exception\ServerErrorResponseException``, which is + thrown when a ``5xx`` response occurs + +Both of these classes extend the base ``BadResponseException`` class. + +This provides you with the granularity you need to debug and handle +exceptions. + +An example with Swift +~~~~~~~~~~~~~~~~~~~~~ + +If you're trying to retrieve a Swift resource, such as a Data Object, +and you're not completely certain that it exists, it makes sense to wrap +your call in a try/catch block: + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + // Okay, the resource probably does not exist + return false; + } catch (\Exception $e) { + // Some other exception was thrown, probably critical + $this->logException($e); + $this->alertDevs(); + } + +Both ``ClientErrorResponseException`` and +``ServerErrorResponseException`` have two methods that allow you to +access the HTTP transaction: + +.. code:: php + + // Find out the faulty request + $request = $e->getRequest(); + + // Display everything by casting as string + echo (string) $request; + + // Find out the HTTP response + $response = $e->getResponse(); + + // Output that too + echo (string) $response; + +Strategy 2: Wire logging +------------------------ + +Guzzle provides a `Log +plugin `__ +that allows you to log everything over the wire, which is useful if you +don't know what's going on. + +Here's how you enable it: + +Install the plugin +^^^^^^^^^^^^^^^^^^ + +.. code:: bash + + php composer.phar require guzzle/plugin-log:~3.8 + +Add to your client +^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use Guzzle\Plugin\Log\LogPlugin; + + $client->addSubscriber(LogPlugin::getDebugPlugin()); + +The above will add a generic logging subscriber to your client, which +will be notified every time a relevant HTTP event is fired off. diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 000000000..45c453a40 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,23 @@ +.. php-opencloud documentation master file, created by + sphinx-quickstart on Tue Mar 3 12:28:19 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to php-opencloud's documentation! +========================================= + +Contents: + +.. toctree:: + :glob: + :maxdepth: 2 + + services/**/index + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/iterators.rst b/doc/iterators.rst new file mode 100644 index 000000000..e5d3c198d --- /dev/null +++ b/doc/iterators.rst @@ -0,0 +1,178 @@ +Iterators +========= + +Intro +----- + +Iterators allow you to traverse over collections of your resources in an +efficient and easy way. Currently there are two Iterators provided by +the SDK: + +- **ResourceIterator**. The standard iterator class that implements + SPL's standard + `Iterator `__, + `ArrayAccess `__ + and `Countable `__ + interfaces. In short, this allows you to traverse this object (using + ``foreach``), count its internal elements like an array (using + ``count`` or ``sizeof``), and access its internal elements like an + array (using ``$iterator[1]``). + +- **PaginatedIterator**. This is a child of ResourceIterator, and as + such inherits all of its functionality. The difference however is + that when it reaches the end of the current collection, it attempts + to construct a URL to access the API based on predictive paginated + collection templates. + +Common behaviour +---------------- + +.. code:: php + + $iterator = $computeService->flavorList(); + +There are two ways to traverse an iterator. The first is the longer, +more traditional way: + +.. code:: php + + while ($iterator->valid()) { + $flavor = $iterator->current(); + + // do stuff.. + echo $flavor->id; + + $iterator->next(); + } + +There is also a shorter and more intuitive version: + +.. code:: php + + foreach ($iterator as $flavor) { + // do stuff... + echo $flavor->id; + } + +Because the iterator implements PHP's native ``Iterator`` interface, it +can inherit all the native functionality of traversible data structures +with ``foreach``. + +Very important note +------------------- + +Until now, users have been expected to do this: + +.. code:: php + + while ($flavor = $iterator->next()) { + // ... + } + +which is **incorrect**. The single responsibility of ``next`` is to move +the internal pointer forward. It is the job of ``current`` to retrieve +the current element. + +For your convenience, these two Iterator classes are fully backward +compatible: they exhibit all the functionality you'd expect from a +correctly implemented iterator, but they also allow previous behaviour. + +Using paginated collections +--------------------------- + +For large collections, such as retrieving DataObjects from +CloudFiles/Swift, you need to use pagination. Each resource will have a +different limit per page; so once that page is traversed, there needs to +be another API call to retrieve to *next* page's resources. + +There are two key concepts: + +- **limit** is the amount of resources returned per page +- **marker** is the way you define a starting point. It is some form of + identifier that allows the collection to begin from a specific + resource + +Resource classes +~~~~~~~~~~~~~~~~ + +When the iterator returns a current element in the internal list, it +populates the relevant resource class with all the data returned to the +API. In most cases, a ``stdClass`` object will become an instance of +``OpenCloud\Common\PersistentObject``. + +In order for this instantiation to happen, the ``resourceClass`` option +must correspond to some method in the parent class that creates the +resource. For example, if we specify 'ScalingPolicy' as the +``resourceClass``, the parent object (in this case +``OpenCloud\Autoscale\Group``, needs to have some method will allows the +iterator to instantiate the child resource class. These are all valid: + +1. ``Group::scalingGroup($data);`` + +2. ``Group::getScalingGroup($data);`` + +3. ``Group::resource('ScalingGroup', $data);`` + +where ``$data`` is the standard object. This list runs in order of +precedence. + +Setting up a PaginatedIterator +------------------------------ + +.. code:: php + + use OpenCloud\Common\Collection\PaginatedIterator; + + $service = $client->computeService(); + + $flavors = PaginatedIterator::factory($service, array( + 'resourceClass' => 'Flavor', + 'baseUrl' => $service->getUrl('flavors') + 'limit.total' => 350, + 'limit.page' => 100, + 'key.collection' => 'flavors' + )); + + foreach ($flavors as $flavor) { + echo $flavor->getId(); + } + +As you can see, there are a lot of configuration parameters to pass in - +and getting it right can be quite fiddly, involving a lot of API +research. For this reason, using the convenience methods like +``flavorList`` is recommended because it hides the complexity. + +PaginatedIterator options +~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are certain configuration options that the paginated iterator +needs to work. These are: + ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| Name | Description | Type | Required | Default | ++=========================+===================================================================================================================================================================================================================================================+==============================+============+===============+ +| resourceClass | The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator. | string | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| baseUrl | The base URL that is used for making new calls to the API for new pages | ``Guzzle\Http\Url`` | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.total | The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list | int | No | 10000 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.page | The amount of resources each page contains | int | No | 100 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.links | Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is ``images_links``. | string | No | links | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collection | The top-level key for the array of resources. For example, servers are returned with this data structure: ``{"servers": [...]}``. The **key.collection** value in this case would be ``servers``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collectionElement | Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: ``{"keypairs": [ {"keypair": {...}} ] }``. So in this case the collectionElement key would be ``keypair``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.marker | The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is ``id`` or ``name``. | string | No | name | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.method | The HTTP method used when making API calls for new pages | string | No | GET | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.headers | The HTTP headers to send when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.body | The HTTP entity body to send when making API calls for new pages | ``Guzzle\Http\EntityBody`` | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ + From b4869c81a1ace3f0af9cce8a5d280692e638d6d5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:49:42 +0100 Subject: [PATCH 418/835] Shuffle structure --- doc/services/autoscale/group-config.rst | 6 +----- doc/services/autoscale/groups.rst | 9 --------- doc/services/autoscale/index.rst | 18 +++++++++++++++++ doc/services/autoscale/policies.rst | 4 +--- doc/services/autoscale/service.sample.rst | 7 ------- doc/services/autoscale/webhooks.rst | 4 +--- doc/services/common/service-args.rst | 16 +++++++++++++++ doc/services/compute/flavors.rst | 6 ------ doc/services/compute/images.rst | 5 ----- doc/services/compute/index.rst | 18 +++++++++++++++++ doc/services/compute/keypairs.rst | 6 ------ doc/services/compute/servers.rst | 5 ----- doc/services/compute/service.sample.rst | 24 ----------------------- 13 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 doc/services/autoscale/service.sample.rst create mode 100644 doc/services/common/service-args.rst delete mode 100644 doc/services/compute/service.sample.rst diff --git a/doc/services/autoscale/group-config.rst b/doc/services/autoscale/group-config.rst index a0d7c98f7..5dcfb6ce4 100644 --- a/doc/services/autoscale/group-config.rst +++ b/doc/services/autoscale/group-config.rst @@ -1,14 +1,10 @@ Group configurations ==================== -.. contents:: - Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with the functionality of a group's configuration, +In order to interact with the functionality of a group's configuration, you must first retrieve the details of the group itself. To do this, you must substitute `{groupId}` for your group's ID: diff --git a/doc/services/autoscale/groups.rst b/doc/services/autoscale/groups.rst index 8e781a2aa..a6caf3f9d 100644 --- a/doc/services/autoscale/groups.rst +++ b/doc/services/autoscale/groups.rst @@ -1,15 +1,6 @@ Groups ====== -.. contents:: - - -Setup ------ - -.. include:: service.sample.rst - - List all groups --------------- diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst index 3077a0a2c..d6ec512ac 100644 --- a/doc/services/autoscale/index.rst +++ b/doc/services/autoscale/index.rst @@ -1,6 +1,23 @@ Auto Scale v2 ============= +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); + +.. include:: ../common/service-args.rst + + +Operations +---------- + .. toctree:: groups @@ -8,6 +25,7 @@ Auto Scale v2 policies webhooks + Glossary -------- diff --git a/doc/services/autoscale/policies.rst b/doc/services/autoscale/policies.rst index f5d2605f4..e103d328e 100644 --- a/doc/services/autoscale/policies.rst +++ b/doc/services/autoscale/policies.rst @@ -4,9 +4,7 @@ Scaling Policies Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with the functionality of a group's scaling +In order to interact with the functionality of a group's scaling policies, you must first retrieve the details of the group itself. To do this, you must substitute `{groupId}` for your group's ID: diff --git a/doc/services/autoscale/service.sample.rst b/doc/services/autoscale/service.sample.rst deleted file mode 100644 index 7e5d166ab..000000000 --- a/doc/services/autoscale/service.sample.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. include:: ../common/rs-client.sample.rst - -Now, set up the Auto Scale service: - -.. code-block:: php - - $service = $client->autoscaleService(); diff --git a/doc/services/autoscale/webhooks.rst b/doc/services/autoscale/webhooks.rst index 68b16b0dc..9133aa9ed 100644 --- a/doc/services/autoscale/webhooks.rst +++ b/doc/services/autoscale/webhooks.rst @@ -4,9 +4,7 @@ Webhooks Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with webhooks, you must first retrieve the +In order to interact with webhooks, you must first retrieve the details of the group and scaling policy you want to execute: .. code-block:: php diff --git a/doc/services/common/service-args.rst b/doc/services/common/service-args.rst new file mode 100644 index 000000000..696f32f2a --- /dev/null +++ b/doc/services/common/service-args.rst @@ -0,0 +1,16 @@ +``{catalogName}`` is the **name** of the service, as it appears in the service +catalog. For Rackspace users, a default will be provided if you pass ``null`` +in for this argument. For OpenStack users, you cannot do this: you must instead +set your own value since it can depend on your environment setup. + +``{region}`` is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the `supported regions page +`_. + +``{urlType}`` is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either ``internalURL`` or +``publicURL``. The former will execute HTTP transactions over the internal +network configured for your service, possibly reducing latency and the overall +bandwidth cost - the caveat is that all of your resources must be in same region. +``publicURL``, however, which is the default, will operate over the public +Internet and is to be used for multi-region installations. diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst index f79caa171..eeb447d3c 100644 --- a/doc/services/compute/flavors.rst +++ b/doc/services/compute/flavors.rst @@ -1,12 +1,6 @@ Flavors ======= -Setup ------ - -.. include:: service.sample.rst - - Get a flavor ------------ diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst index b2aae4a65..fb68af7f7 100644 --- a/doc/services/compute/images.rst +++ b/doc/services/compute/images.rst @@ -9,11 +9,6 @@ Images Cloud Backup or Cloud Block Storage to ensure availability in case you need to rebuild or restore a server. -Setup ------ - -.. include:: service.sample.rst - List images ----------- diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 94c41979f..207151d50 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -6,6 +6,23 @@ Compute v2 This is a joint service that supports both Rackspace Cloud Servers v2 API, and OpenStack Nova v2 API. +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Compute service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + .. toctree:: images @@ -13,6 +30,7 @@ Compute v2 servers keypairs + Glossary -------- diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst index 4040c0e65..f7e67b113 100644 --- a/doc/services/compute/keypairs.rst +++ b/doc/services/compute/keypairs.rst @@ -1,12 +1,6 @@ Keypairs ======== -Setup ------ - -.. include:: service.sample.rst - - Generate a new keypair ---------------------- diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 4d2f2363c..229e8e4ff 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -1,11 +1,6 @@ Servers ======= -Setup ------ - -.. include:: service.sample.rst - Get server ---------- diff --git a/doc/services/compute/service.sample.rst b/doc/services/compute/service.sample.rst deleted file mode 100644 index c684caf9a..000000000 --- a/doc/services/compute/service.sample.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. include:: ../common/rs-client.sample.rst - -Now, set up the Auto Scale service: - -.. code-block:: php - - $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); - - -``{catalogName}`` is the **name** of the service, as it appears in the service -catalog. For Rackspace users, this will default to `cloudServersOpenStack`; for -OpenStack users, you must set your own value since it can depend on your -environment setup. - -``{region}`` is the Compute region the service will operate in. For Rackspace -users, you can select one of the following from the `supported regions page`. - -``{urlType}`` is the type of URL to use, depending on what endpoints your -catalog provides. For Rackspace, you may use either `internalURL` or `publicURL`. -The former will execute HTTP transactions over the internal Rackspace network, -reducing latency and the overall bandwidth cost - the caveat is that all of your -resources must be in same region. `publicURL`, however, which is the default, -will operate over the public Internet and is to be used for multi-region -installations. From a536c1f3404c05745a7f11a2cf2d3eaa524cd133 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:03 +0100 Subject: [PATCH 419/835] Turn on extension --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 7f9e29414..f49381d42 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -23,7 +23,7 @@ lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) primary_domain = 'php' -extensions = [] +extensions = ['sphinxcontrib.phpdomain'] templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' From 4dd573a3a03c977fb2198733e71cda7561ca867e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:07 +0100 Subject: [PATCH 420/835] Add regions --- doc/regions.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 doc/regions.rst diff --git a/doc/regions.rst b/doc/regions.rst new file mode 100644 index 000000000..82edf28ec --- /dev/null +++ b/doc/regions.rst @@ -0,0 +1,15 @@ +Rackspace regions +================= + +Below are the supported regions on the Rackspace network: + ++======+===========+ +| code | location | ++======+===========+ +| IAD | Virginia | +| ORD | Chicago | +| DFW | Dallas | +| LON | London | +| SYD | Sydney | +| HKG | Hong Kong | ++------+-----------+ From f95ed717042c226e85b78e04846124cf4ab88519 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:13 +0100 Subject: [PATCH 421/835] Add DNS docs --- doc/services/dns/Domains.md.rst | 290 ------------------ doc/services/dns/Records.md.rst | 111 ------- doc/services/dns/Service.md.rst | 13 - doc/services/dns/domains.rst | 288 +++++++++++++++++ doc/services/dns/index.rst | 51 +++ .../dns/{Limits.md.rst => limits.rst} | 37 +-- doc/services/dns/records.rst | 113 +++++++ .../{Reverse-DNS.md.rst => reverse-dns.rst} | 24 +- 8 files changed, 474 insertions(+), 453 deletions(-) delete mode 100644 doc/services/dns/Domains.md.rst delete mode 100644 doc/services/dns/Records.md.rst delete mode 100644 doc/services/dns/Service.md.rst create mode 100644 doc/services/dns/domains.rst rename doc/services/dns/{Limits.md.rst => limits.rst} (59%) create mode 100644 doc/services/dns/records.rst rename doc/services/dns/{Reverse-DNS.md.rst => reverse-dns.rst} (82%) diff --git a/doc/services/dns/Domains.md.rst b/doc/services/dns/Domains.md.rst deleted file mode 100644 index 824c05099..000000000 --- a/doc/services/dns/Domains.md.rst +++ /dev/null @@ -1,290 +0,0 @@ -Domains -======= - -A domain is an entity/container of all DNS-related information -containing one or more records. - -Setup ------ - -Limit methods will be called on the DNS service, an instance of -``OpenCloud\DNS\Service``. Please see the `DNS service `__ -documentation for setup instructions. - -Get domain ----------- - -To retrieve a specific domain, you will need the domain's **id**, not -its domain name. - -.. code:: php - - $domain = $service->domain(12345); - -If you are having trouble remembering or accessing the domain ID, you -can do a domain list search for your domain and then access its ID. - -List domains ------------- - -These calls provide a list of all DNS domains manageable by a given -account. The resulting list is flat, and does not break the domains down -hierarchically by subdomain. All representative domains are included in -the list, even if a domain is conceptually a subdomain of another domain -in the list. - -.. code:: php - - $domains = $service->domainList(); - - # Return detailed information for each domain - $domains = $service->domainList(true); - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Filter parameters -~~~~~~~~~~~~~~~~~ - -You can filter the aforementioned search by using the ``name`` parameter -in a key/value array supplied as a method argument. For example, -providing ``array('name' => 'hoola.com')`` will return hoola.com and -similar names such as main.hoola.com and sub.hoola.com. - -.. code:: php - - $hoolaDomains = $service->domainList(array( - 'name' => 'hoola.com' - )); - -Filter criteria may consist of: - -- Any letter (A-Za-z) -- Numbers (0-9) -- Hyphen ("-") -- 1 to 63 characters - -Filter criteria should not include any of the following characters: - - ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò - -Finding a domain ID -~~~~~~~~~~~~~~~~~~~ - -If you know a domain's name, but not its unique identifier, you can do -this: - -.. code:: php - - $domains = $service->domainList(array( - 'name' => 'foo.com' - )); - - foreach ($domains as $domain) { - $id = $domain->id; - } - -List domain changes -------------------- - -This call shows all changes to the specified domain since the specified -date/time. The since parameter is optional and defaults to midnight of -the current day. - -.. code:: php - - $changes = $domain->changes(); - - # Changes since last week - $since = date('c', strtotime('last week')); - $changes = $domain->changes($since); - - foreach ($changes->changes as $change) { - printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); - - foreach ($change->changeDetails as $detail) { - printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); - } - } - -Export domain -------------- - -This call provides the BIND (Berkeley Internet Name Domain) 9 formatted -contents of the requested domain. This call is for a single domain only, -and as such, does not traverse up or down the domain hierarchy for -details (that is, no subdomain information is provided). - -.. code:: php - - $asyncResponse = $domain->export(); - $body = $asyncResponse->waitFor('COMPLETED'); - echo $body['contents']; - -Create domain -------------- - -A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` -records) and an optional list of sub-domains. You will need to specify -these before creating the domain itself: - -.. code:: php - - // get empty object - $domain = $service->domain(); - - // add A record - $aRecord = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 - )); - $domain->addRecord($aRecord); - - // add optional C record - $cRecord = $domain->record(array( - 'type' => 'CNAME', - 'name' => 'www.example.com', - 'data' => 'example.com', - 'ttl' => 3600 - )); - $domain->addRecord($cRecord); - - // add optional MX record - $mxRecord = $domain->record(array( - 'type' => 'MX', - 'data' => 'mail.example.com', - 'name' => 'example.com', - 'ttl' => 3600, - 'priority' => 5 - )); - $domain->addRecord($mxRecord); - - // add optional NS records - $nsRecord1 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns1.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 - )); - $domain->addRecord($nsRecord1); - - $nsRecord2 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns2.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 - )); - $domain->addRecord($nsRecord2); - - // add optional subdomains - $sub1 = $domain->subdomain(array( - 'emailAddress' => 'foo@example.com', - 'name' => 'dev.example.com', - 'comment' => 'Dev portal' - )); - $domain->addSubdomain($sub1); - - // send to API - $domain->create(array( - 'emailAddress' => 'webmaster@example.com', - 'ttl' => 3600, - 'name' => 'example.com', - 'comment' => 'Optional comment' - )); - -Clone domain ------------- - -This call will duplicate a single existing domain configuration with a -new domain name for the specified Cloud account. By default, all records -and, optionally, subdomain(s) are duplicated as well. - -The method signature you will need to use is: - -.. code:: php - - cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) - -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Name | Data type | Default | Description | -+======================+==============+============+====================================================================================================================================================================================+ -| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -For example: - -.. code:: php - - $asyncResponse = $domain->cloneDomain('new-name.com', true); - -Import domain -------------- - -This call provisions a new DNS domain under the account specified by the -BIND 9 formatted file configuration contents defined in the request -object. - -You will need to ensure that the BIND 9 formatted file configuration -contents are valid by adhering to the following rules: - -- Each record starts on a new line and on the first column. If a record - will not fit on one line, use the BIND\_9 line continuation - convention where you put a left parenthesis and continue the one - record on the next line and put a right parenthesis when the record - ends. For example, - - example2.net. 3600 IN SOA dns1.stabletransit.com. ( - sample@rackspace.com. 1308874739 3600 3600 3600 3600) - -- The attribute values of a record must be separated by a single blank - or tab. No other white space characters. - -- If there are any NS records, the data field should not be - dns1.stabletransit.com or dns2.stabletransit.com. They will result in - "duplicate record" errors. - -For example: - -.. code:: php - - $bind9Data = <<import($bind9Data); - -Modify domain -------------- - -This call modifies DNS domain(s) attributes only. Only the TTL, email -address and comment attributes of a domain can be modified. Records -cannot be added, modified, or removed through this API operation - you -will need to use the `add -records `__, `modify -records `__ or `remove -records `__ operations -respectively. - -.. code:: php - - $domain->update(array( - 'ttl' => ($domain->ttl + 100), - 'emailAddress' => 'new_dev@foo.com' - )); - -Remove domain -------------- - -.. code:: php - - $domain->delete(); - diff --git a/doc/services/dns/Records.md.rst b/doc/services/dns/Records.md.rst deleted file mode 100644 index 4e492e8ef..000000000 --- a/doc/services/dns/Records.md.rst +++ /dev/null @@ -1,111 +0,0 @@ -Records -======= - -A DNS record belongs to a particular domain and is used to specify -information about the domain. - -There are several types of DNS records. Examples include mail exchange -(MX) records, which specify the mail server for a particular domain, and -name server (NS) records, which specify the authoritative name servers -for a domain. - -It is represented by the ``OpenCloud\DNS\Resource\Record`` class. -Records belong to a `Domain `__. - -Get record ----------- - -In order to retrieve details for a specific DNS record, you will need -its **id**: - -.. code:: php - - $record = $domain->record('NS-1234567'); - -If you do not have this ID at your disposal, you can traverse the record -collection and do a string comparison (detailed below). - -List records ------------- - -This call lists all records configured for the specified domain. - -.. code:: php - - $records = $domain->recordList(); - - foreach ($records as $record) { - printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); - } - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Query parameters -~~~~~~~~~~~~~~~~ - -You can pass in an array of query parameters for greater control over -your search: - -+------------+--------------+------------------------+---------------+ -| Name | Data type | Default | Description | -+============+==============+========================+===============+ -| ``type`` | ``string`` | The record type | -+------------+--------------+------------------------+---------------+ -| ``name`` | ``string`` | The record name | -+------------+--------------+------------------------+---------------+ -| ``data`` | ``string`` | Data for this record | -+------------+--------------+------------------------+---------------+ - -Find a record ID from its name -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For example: - -.. code:: php - - $records = $domain->recordList(array( - 'name' => 'imap.example.com', - 'type' => 'MX' - )); - - foreach ($records as $record) { - $recordId = $record->id; - } - -Add record ----------- - -This call adds a new record to the specified domain: - -.. code:: php - - $record = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 - )); - - $record->create(); - -Please be aware that records that are added with a different hostname -than the parent domain might fail silently. - -Modify record -------------- - -.. code:: php - - $record = $domain->record(123456); - $record->ttl -= 100; - $record->update(); - -Delete record -------------- - -.. code:: php - - $record->delete(); - diff --git a/doc/services/dns/Service.md.rst b/doc/services/dns/Service.md.rst deleted file mode 100644 index 29ea79193..000000000 --- a/doc/services/dns/Service.md.rst +++ /dev/null @@ -1,13 +0,0 @@ -DNS Service -=========== - -To instantiate a Compute service object, you first need to setup a -Rackspace/OpenStack client. To do this, or for more information, please -consult the `Clients documentation <../Clients.md>`__. - -You will then need to run: - -.. code:: php - - $service = $client->dnsService(); - diff --git a/doc/services/dns/domains.rst b/doc/services/dns/domains.rst new file mode 100644 index 000000000..0a7b83b96 --- /dev/null +++ b/doc/services/dns/domains.rst @@ -0,0 +1,288 @@ +Domains +======= + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code-block:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the search by using the ``name`` parameter in a key/value array +supplied as a method argument. For example, to retrieve domains named ``foo.com``, +along with any subdomains like ``bar.foo.com``: + +.. code-block:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'foo.com' + )); + +Filter criteria may consist of: + +* Any letter (A-Za-z) +* Numbers (0-9) +* Hyphen ("-") +* 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +Once you have a list of domains, to retrieve a domain's ID: + +.. code-block:: php + + foreach ($domains as $domain) { + $id = $domain->id; + } + + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. To list all available changes for a domain for the current day: + +.. code-block:: php + + $changes = $domain->changes(); + + +For more granular control, you can manually define the ``since`` parameter like +so: + +.. code-block:: php + + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + +Once you have a set of changes, you can iterate over them like so: + +.. code-block:: php + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + + +Create domain +------------- + +The first thing you will need to do is instantiate a new object and set the +primary ``A`` record for the DNS domain, like so: + +.. code-block:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $domain->addRecord($aRecord); + +You also have the option of adding more types of DNS records such as ``CNAME``, +``MX`` and ``NS`` records. This step is completely optional and depends on +your requirements: + +.. code-block:: php + + // add CNAME record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add NS record + $nsRecord = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord); + +You can also add sub-domains to your new DNS domain. Again, this is completely +optional: + +.. code-block:: php + + $subdomain = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($subdomain); + +Once you've finished configuring how your DNS domain will work, you're ready +to specify the essential details and send it to the API for creation: + +.. code-block:: php + + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + + +Clone domain +------------ + +This call will duplicate an existing domain under a new name. By default, all +records and, optionally, subdomains are duplicated as well. + +The method signature you will need to use is: + +.. function:: cloneDomain( $newDomainName[, $subdomains[, $comments[, $email[, $records]]]] ) + + Clone a domain + + :param string $newDomainName: The name of the new domain entry + :param bool $subdomains: Set to ``true`` to clone all the subdomains for this domain + :param bool $comments: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. + :param bool $email: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + :param bool $records: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + + +For example: + +.. code-block:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true, false, true, false); + + +Export domain +------------- + +This call provides access to the `BIND `_ +(Berkeley Internet Name Domain) 9 for the requested domain. This call is for a +single domain only, and as such, does not traverse up or down the domain +hierarchy for details: + +.. code-block:: php + + $asyncResponse = $domain->export(); + + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + + +Import domain +------------- + +This operation will create a new DNS domain according to a `BIND `_ +(Berkeley Internet Name Domain) 9 formatted value. + +In order for the BIND value to be considered valid, it needs to adhere to the +following rules: + +* Each record starts on a new line and on the first column. If a record will + not fit on one line, use the BIND\_9 line continuation convention where you put + a left parenthesis and continue the one record on the next line and put a right + parenthesis when the record ends. For example: + + example2.net. 3600 IN SOA dns1.stabletransit.com. (sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +* The attribute values of a record must be separated by a single blank or tab. + No other white space characters. + +* If there are any NS records, the data field should not be + ``dns1.stabletransit.com`` or ``dns2.stabletransit.com``. They will result in + "duplicate record" errors. + +For example: + +.. code-block:: php + + $bind9Data = <<import($bind9Data); + + +Modify domain +------------- + +Only the TTL, email address and comment attributes of a domain can be modified. +Records cannot be added, modified, or removed through this API operation - you +will need to use the `add records `__, `modify records +`__ or `remove records `__ +operations respectively. + +.. code-block:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + + +Delete domain +------------- + +.. code-block:: php + + $domain->delete(); diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index e69de29bb..4caca9fe5 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -0,0 +1,51 @@ +DNS v1 +====== + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the DNS service: + +.. code-block:: php + + $service = $client->dnsService(); + + +Operations +---------- + +.. toctree:: + + records + domains + limits + reverse-dns + + +Glossary +-------- + + domain + A domain is an entity/container of all DNS-related information containing + one or more records. + + record + A DNS record belongs to a particular domain and is used to specify + information about the domain. There are several types of DNS records. Each + record type contains particular information used to describe that record's + purpose. Examples include mail exchange (MX) records, which specify the + mail server for a particular domain, and name server (NS) records, which + specify the authoritative name servers for a domain. + + subdomain + Subdomains are domains within a parent domain, and subdomains cannot be + registered. Subdomains allow you to delegate domains. Subdomains can + themselves have subdomains, so third-level, fourth-level, fifth-level, and + deeper levels of nesting are possible. + + pointer records + DNS usually determines an IP address associated with a domain name. + Reverse DNS is the opposite process: resolving a domain name from an IP + address. This is usually achieved with a domain name pointer. diff --git a/doc/services/dns/Limits.md.rst b/doc/services/dns/limits.rst similarity index 59% rename from doc/services/dns/Limits.md.rst rename to doc/services/dns/limits.rst index 72f8219a0..289c3888d 100644 --- a/doc/services/dns/Limits.md.rst +++ b/doc/services/dns/limits.rst @@ -1,22 +1,15 @@ Limits ====== -Setup ------ - -Limit methods will be called on the DNS service, an instance of -``OpenCloud\DNS\Service``. Please see the `DNS service `__ -documentation for setup instructions. - List all limits --------------- -This call provides a list of all applicable limits for the specified -account. +This call provides a list of all applicable limits for the specified account. .. code:: php - $limits = $service->limits(); + $limits = $service->limits(); + Absolute limits ~~~~~~~~~~~~~~~ @@ -29,12 +22,13 @@ domain: $absoluteLimits = $limits->absolute; - # Domain limit + // Domain limit echo $absoluteLimits->domains; - # Record limit per domain + // Record limit per domain echo $absoluteLimits->{'records per domain'}; + List limit types ---------------- @@ -42,20 +36,17 @@ To find out the different limit types you can query, run: .. code:: php - $limitTypes = $service->limitTypes(); + $limitTypes = $service->limitTypes(); will return: :: - array(3) { - [0] => - string(10) "RATE_LIMIT" - [1] => - string(12) "DOMAIN_LIMIT" - [2] => - string(19) "DOMAIN_RECORD_LIMIT" - } + array(3) { + [0] => string(10) "RATE_LIMIT" + [1] => string(12) "DOMAIN_LIMIT" + [2] => string(19) "DOMAIN_RECORD_LIMIT" + } Query a specific limit ---------------------- @@ -63,8 +54,4 @@ Query a specific limit .. code:: php $limit = $service->limits('DOMAIN_LIMIT'); - echo $limit->absolute->limits->value; - - >>> 500 - diff --git a/doc/services/dns/records.rst b/doc/services/dns/records.rst new file mode 100644 index 000000000..26e17ee5e --- /dev/null +++ b/doc/services/dns/records.rst @@ -0,0 +1,113 @@ +Records +======= + +Setup +----- + +In order to interact with the functionality of records, you must first +retrieve the details of the domain itself. To do this, you must substitute +`{domainId}` for your domain's ID: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('{recordId}'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+ +| Name | Data type | Description | ++============+==============+========================+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+ + + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record('{recordId}'); + $record->ttl -= 100; + $record->update(); + + +Delete record +------------- + +.. code:: php + + $record->delete(); diff --git a/doc/services/dns/Reverse-DNS.md.rst b/doc/services/dns/reverse-dns.rst similarity index 82% rename from doc/services/dns/Reverse-DNS.md.rst rename to doc/services/dns/reverse-dns.rst index b8a5c0e76..4d0eb671c 100644 --- a/doc/services/dns/Reverse-DNS.md.rst +++ b/doc/services/dns/reverse-dns.rst @@ -1,9 +1,6 @@ Reverse DNS =========== -DNS usually determines an IP address associated with a domain name. -Reverse DNS is the opposite process: resolving a domain name from an IP -address. This is usually achieved with a domain name pointer. Get PTR record -------------- @@ -20,14 +17,15 @@ formed resource object in order to retrieve either one's PTR record: 'parent' => $parent )); -So, in the above example, a ``$parent`` could be an instance of +So, in the above example, the ``$parent`` object could be an instance of ``OpenCloud\Compute\Resource\Server`` or ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please -consult the `server documentation <../Compute/Server.md>`__ and `load -balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +consult the `server documentation <../compute>`__ and `load +balancer documentation <../load-balancer>`__ for more detailed usage instructions. + List PTR records ---------------- @@ -41,9 +39,6 @@ List PTR records } -Please consult the `iterator -documentation `__ for more information -about iterators. Add PTR record -------------- @@ -78,19 +73,20 @@ Here is a table that explains the above attributes: | comment | If included, its length must be less than or equal to 160 characters. | No | +-----------+------------------------------------------------------------------------------------+------------+ + Modify PTR record ----------------- .. code:: php - $ptr->update(array( - 'ttl' => $ptr->ttl * 2 - )); + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + Delete PTR record ----------------- .. code:: php - $ptr->delete(); - + $ptr->delete(); From 912c572fb614ea55a94bcfa92ebec5ba3a420b5d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:23 +0100 Subject: [PATCH 422/835] Add DB docs --- doc/services/database/README.md.rst | 125 ------------------ doc/services/database/configurations.rst | 127 +++++++++++++++++++ doc/services/database/databases.rst | 60 +++++++++ doc/services/database/datastores.rst | 59 +++++++++ doc/services/database/index.rst | 69 ++++++++++ doc/services/database/instances.rst | 155 +++++++++++++++++++++++ doc/services/database/users.rst | 67 ++++++++++ 7 files changed, 537 insertions(+), 125 deletions(-) delete mode 100644 doc/services/database/README.md.rst create mode 100644 doc/services/database/configurations.rst create mode 100644 doc/services/database/databases.rst create mode 100644 doc/services/database/datastores.rst create mode 100644 doc/services/database/instances.rst create mode 100644 doc/services/database/users.rst diff --git a/doc/services/database/README.md.rst b/doc/services/database/README.md.rst deleted file mode 100644 index 3f6bdd3c2..000000000 --- a/doc/services/database/README.md.rst +++ /dev/null @@ -1,125 +0,0 @@ -Databases -========= - -A **cloud database** is a MySQL relational database service that allows -customers to programatically provision database instances of varying -virtual resource sizes without the need to maintain and/or update MySQL. - -Getting started ---------------- - -1. Instantiate a Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - use OpenCloud\Rackspace; - use OpenCloud\Common\Constants\State; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Create a database server instance. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $databaseService = $client->databaseService('cloudDatabases', 'DFW'); - - $twoGbFlavor = $databaseService->flavor(3); - - $dbInstance = $databaseService->instance(); - $dbInstance->name = 'Demo database instance'; - $dbInstance->volume = new stdClass(); - $dbInstance->volume->size = 20; // GB - $dbInstance->flavor = $twoGbFlavor; - $dbInstance->create(); - - $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { - - printf("Database instance build status: %s\n", $dbInstance->status); - - }); - -The example above creates a database server instance with 20GB of disk -space and 2GB of memory, then waits for it to become ACTIVE. - -3. Create a database on the database server instance. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $db = $dbInstance->database(); - $db->name = 'demo_db'; - - $db->create(); - -The example above creates a database named ``demo_db`` on the database -server instance created in the previous step. - -4. Create database user and give it access to database. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $user = $dbInstance->user(); - $user->name = 'demo_user'; - $user->password = 'h@X0r!'; - $user->databases = array('demo_db'); - - $user->create(); - -The example above creates a database user named ``demo_user``, sets its -password and gives it access to the ``demo_db`` database created in the -previous step. - -5. Optional step: Create a load balancer to allow access to the database from the Internet. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The database created in the previous step can only be accessed from the -Rackspace private network (aka ``SERVICENET``). If you have a cloud -server instance in the same region as the database server instance, you -will be able to connect to the database from that cloud server instance. - -If, however, you would like to access the database from the Internet, -you will need to create a load balancer with an IP address that is -routable from the Internet and attach the database server instance as a -back-end node of this load balancer. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $loadBalancer->name = 'Load balancer - DB'; - $loadBalancer->addNode($dbInstance->hostname, 3306); - $loadBalancer->port = 3306; - $loadBalancer->protocol = 'MYSQL'; - $loadBalancer->addVirtualIp('PUBLIC'); - - $loadBalancer->create(); - - $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { - printf("Load balancer build status: %s\n", $lb->status); - }); - - foreach ($loadBalancer->virtualIps as $vip) { - if ($vip->type == 'PUBLIC') { - printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); - } - } - -In the example above, a load balancer is created with the database -server instance as its only back-end node. Further, this load balancer -is configured to listen for MySQL connections on port 3306. Finally a -virtual IP address (VIP) is configured in the ``PUBLIC`` network address -space so that this load balancer may receive connections from the -Internet. - -Once the load balancer is created and becomes ``ACTIVE``, it's -Internet-accessible IP addresses are printed out. If you connect to any -of these IP addresses on port 3306 using the MySQL protocol, you will be -connected to the database created in step 3. diff --git a/doc/services/database/configurations.rst b/doc/services/database/configurations.rst new file mode 100644 index 000000000..edfb9dc89 --- /dev/null +++ b/doc/services/database/configurations.rst @@ -0,0 +1,127 @@ +Configurations +============== + +Creating a configuration +------------------------ + +.. code-block:: php + + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + $configuration = $service->configuration(); + + $configuration->create(array( + 'name' => 'example-configuration-name', + 'description' => 'An example configuration', + 'values' => array( + 'collation_server' => 'latin1_swedish_ci', + 'connect_timeout' => 120 + ), + 'datastore' => array( + 'type' => '10000000-0000-0000-0000-000000000001', + 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' + ) + )); + +`Get the executable PHP script for this example `__ + + +Listing configurations +---------------------- + +You can list out all the configurations you have created as shown below: + +.. code-block:: php + + $configurations = $service->configurationList(); + foreach ($configurations as $configuration) { + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a configuration +-------------------------- + +You can retrieve a specific configuration, using its ID, as shown below: + +.. code-block:: php + + $configuration = $service->configuration('{configId}'); + /** @var OpenCloud\Database\Resource\Configuration **/ + +`Get the executable PHP script for this example `__ + + +Updating a configuration +------------------------ + +You have two choices when updating a configuration: + +* you can `patch a configuration <#patching-a-configuration>`__ to change only +some configuration parameters +* you can `entirely replace a configuration <#replacing-a-configuration>`__ to +replace all configuration parameters with new ones + + +Patching a configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can patch a configuration as shown below: + +.. code-block:: php + + $configuration->patch(array( + 'values' => array( + 'connect_timeout' => 30 + ) + )); + +`Get the executable PHP script for this example `__ + + +Replacing a configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can replace a configuration as shown below: + +.. code-block:: php + + $configuration->update(array( + 'values' => array( + 'collation_server' => 'utf8_general_ci', + 'connect_timeout' => 60 + ) + )); + +`Get the executable PHP script for this example `__ + + +Deleting a configuration +------------------------ + +.. code-block:: php + + $configuration->delete(); + +`Get the executable PHP script for this example `__ + +.. note:: + + You cannot delete a configuration if it is in use by a running instance. + + +Listing instances using a configuration +--------------------------------------- + +You can list all instances using a specific configuration, using its ID, +as shown below: + +.. code-block:: php + + $instances = $configuration->instanceList(); + foreach ($instances as $instance) { + /** @var $instance OpenCloud\Database\Resource\Instance **/ + } + +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/databases.rst b/doc/services/database/databases.rst new file mode 100644 index 000000000..83d07c5e2 --- /dev/null +++ b/doc/services/database/databases.rst @@ -0,0 +1,60 @@ +Databases +========= + +Setup +----- + +In order to interact with the functionality of databases, you must first +retrieve the details of the instance itself. To do this, you must substitute +`{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating a new database +----------------------- + +To create a new database, you must supply it with a name; you can +optionally specify its character set and collating sequence: + +.. code-block:: php + + // Create an empty object + $database = $instance->database(); + + // Send to API + $database->create(array( + 'name' => 'production', + 'character_set' => 'utf8', + 'collate' => 'utf8_general_ci' + )); + +You can find values for ``character_set`` and ``collate`` at `the MySQL +website `__. + + +Deleting a database +------------------- + +.. code-block:: php + + $database->delete(); + +.. note:: + + This is a destructive operation: all your data will be wiped away and will + not be retrievable. + + +Listing databases +----------------- + +.. code-block:: php + + $databases = $service->databaseList(); + + foreach ($databases as $database) { + /** @param $database OpenCloud\Database\Resource\Database */ + } diff --git a/doc/services/database/datastores.rst b/doc/services/database/datastores.rst new file mode 100644 index 000000000..6ca45daed --- /dev/null +++ b/doc/services/database/datastores.rst @@ -0,0 +1,59 @@ +Datastores +========== + +Listing datastores +------------------ + +You can list out all the datastores available as shown below: + +.. code-block:: php + + $datastores = $service->datastoreList(); + foreach ($datastores as $datastore) { + /** @var $datastore OpenCloud\Database\Resource\Datastore **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore +---------------------- + +You can retrieve a specific datastore's information, using its ID, as +shown below: + +.. code-block:: php + + /** @var OpenCloud\Database\Resource\Datastore **/ + $datastore = $service->datastore('{datastoreId}'); + +`Get the executable PHP script for this example `__ + + +Listing datastore versions +-------------------------- + +You can list out all the versions available for a specific datastore, as +shown below: + +.. code-block:: php + + $versions = $datastore->versionList(); + foreach ($versions as $version) { + /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore version +------------------------------ + +You a retrieve a specific datastore version, using its ID, as shown +below: + +.. code-block:: php + + $datastoreVersion = $datastore->version('{versionId}'); + +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index e69de29bb..850e56c8a 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -0,0 +1,69 @@ +Databases v1 +============ + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Database service: + +.. code-block:: php + + $service = $client->databaseService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + instances + databases + users + datastores + + +Glossary +-------- + +.. glossary:: + + configuration group + A configuration group is a collection of key/value pairs which configure a + database instance. Some directives are capable of being applied dynamically, + while other directives require a server restart to take effect. The + configuration group can be applied to an instance at creation or applied to + an existing instance to modify the behavior of the running datastore on the + instance. + + flavor + A flavor is an available hardware configuration for a database instance. + Each flavor has a unique combination of memory capacity and priority for + CPU time. + + instance + A database instance is an isolated MySQL instance in a single tenant + environment on a shared physical host machine. Also referred to as + instance. + + database + A database is a local MySQL database running on an instance. + + user + A user is a local MySQL user that can access a database running on an + instance. + + datastore + The database engine running on your instance. Currently, there is support + for MySQL 5.6, MySQL 5.1, Percona 5.6 and MariaDB 10. + + volume + A volume is user-specified storage that contains the database engine data + directory. Volumes are automatically provisioned on shared Internet Small + Computer System Interface (iSCSI) storage area networks (SAN) that provide + for increased performance, scalability, availability and manageability. + Applications with high I/O demands are performance optimized and data is + protected through both local and network RAID-10. diff --git a/doc/services/database/instances.rst b/doc/services/database/instances.rst new file mode 100644 index 000000000..082e56c05 --- /dev/null +++ b/doc/services/database/instances.rst @@ -0,0 +1,155 @@ +Instances +========= + +Create a new instance +--------------------- + +.. code-block:: php + + // Create an empty object + $instance = $service->instance(); + + // Send to the API + $instance->create(array( + 'name' => '{name}', + 'flavor' => $service->flavor('{flavorId}'), + 'volume' => array('size' => 4) // 4GB of volume disk + )); + +`Get the executable PHP script for this sample `__ + + +Waiting for the instance to build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The SDK provides a blocking operation that will wait until your instance resource +has transitioned into an ``ACTIVE`` state. During this period, it will +continuously poll the API and break the loop when the state has been achieved: + +.. code-block:: php + + $instance->waitFor('ACTIVE', null, function ($instance) { + // This will be executed continuously + printf("Database instance build status: %s\n", $instance->status); + }); + + +Connecting an instance to a load balancer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The instance created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code-block:: php + + $lbService = $client->loadBalancerService(null, '{region}'); + + // Create empty object + $loadBalancer = $lbService->loadBalancer(); + + // Associate this LB with the instance as a "node" + $loadBalancer->addNode($instance->hostname, 3306); + $loadBalancer->addVirtualIp('PUBLIC'); + + // Configure other parameters and send to the API + $loadBalancer->create(array( + 'name' => 'DB Load Balancer', + 'port' => 3306, + 'protocol' => 'MYSQL', + )); + + // Wait for the resource to create + $loadBalancer->waitFor('ACTIVE', null, function ($loadBalancer) { + printf("Load balancer build status: %s\n", $loadBalancer->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. + + +Retrieving an instance +---------------------- + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + +`Get the executable PHP script for this example `__ + + +Updating an instance +-------------------- + +An instance can be updated to use a specific `configuration `__ as shown below. + +.. code-block:: php + + $instance->update(array( + 'configuration' => '{configurationId}' + )); + +.. note:: + + If any parameters in the associated configuration require a restart, then you + will need to `restart the instance <#restarting-an-instance>`__ after the update. + + +Deleting an instance +-------------------- + +.. code-block:: php + + $instance->delete(); + + +Restarting an instance +---------------------- + +.. code-block:: php + + $instance->restart(); + + +Resizing an instance's RAM +-------------------------- + +To change the amount of RAM allocated to the instance: + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + $instance->resize($flavor); + + +Resizing an instance's volume +----------------------------- + +You can also independently change the volume size to increase the disk +space: + +.. code-block:: php + + // Increase to 8GB disk + $instance->resizeVolume(8); diff --git a/doc/services/database/users.rst b/doc/services/database/users.rst new file mode 100644 index 000000000..0343c4904 --- /dev/null +++ b/doc/services/database/users.rst @@ -0,0 +1,67 @@ +Users +===== + +Setup +----- + +Finally, in order to interact with the functionality of databases, you must +first retrieve the details of the instance itself. To do this, you must +substitute `{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating users +-------------- + +Database users exist at the ``Instance`` level, but can be associated +with a specific ``Database``. They are represented by the +``OpenCloud\Database\Resource\User`` class. + +.. code-block:: php + + // New instance of OpenCloud\Database\Resource\User + $user = $instance->user(); + + // Send to API + $user->create(array( + 'name' => 'Alice', + 'password' => 'fooBar' + 'databases' => array('production') + )); + + +Deleting a user +--------------- + +.. code-block:: php + + $user->delete(); + + +The root user +------------- + +By default, Cloud Databases does not enable the root user. In most +cases, the root user is not needed, and having one can leave you open to +security violations. However, if you do want to enable access to the root user: + +.. code-block:: php + + $rootUser = $instance->enableRootUser(); + + +This returns a regular ``User`` object with the ``name`` attribute set +to ``root`` and the ``password`` attribute set to an auto-generated +password. + + +Check if root user is enabled +----------------------------- + +.. code-block:: php + + // true for yes, false for no + $instance->isRootEnabled(); From 737965c46ff453222bd53b97a67002fd8eb23f23 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 15:16:07 +0100 Subject: [PATCH 423/835] Identity docs --- doc/services/identity/Service.md.rst | 35 ----- doc/services/identity/Tenants.md.rst | 29 ---- doc/services/identity/Tokens.md.rst | 105 ------------- doc/services/identity/index.rst | 46 ++++++ .../identity/{Roles.md.rst => roles.rst} | 0 doc/services/identity/tenants.rst | 26 ++++ doc/services/identity/tokens.rst | 101 +++++++++++++ .../identity/{Users.md.rst => users.rst} | 139 ++++++++---------- 8 files changed, 238 insertions(+), 243 deletions(-) delete mode 100644 doc/services/identity/Service.md.rst delete mode 100644 doc/services/identity/Tenants.md.rst delete mode 100644 doc/services/identity/Tokens.md.rst rename doc/services/identity/{Roles.md.rst => roles.rst} (100%) create mode 100644 doc/services/identity/tenants.rst create mode 100644 doc/services/identity/tokens.rst rename doc/services/identity/{Users.md.rst => users.rst} (79%) diff --git a/doc/services/identity/Service.md.rst b/doc/services/identity/Service.md.rst deleted file mode 100644 index f7e8e12ba..000000000 --- a/doc/services/identity/Service.md.rst +++ /dev/null @@ -1,35 +0,0 @@ -Identity service -================ - -Intro ------ - -The Identity service is regionless, so you do not need to specify a -region when instantiating the service object. Although this was -primarily based on Rackspace's implementation of Cloud Identity, it -should also work for OpenStack Keystone. - -A note on object creation -------------------------- - -Normally, when services are created the client handles authenticates -automatically. But because Keystone/Identity is fundamental to the -authentication process itself, it proves difficult to do this procedure -as its normally done. For this reason, you have two options when -creating the service object: - -1: Use the client's factory method - -.. code:: php - - $identity = $client->identityService(); - -2: Authenticate manually - -.. code:: php - - use OpenCloud\Identity\Service as IdentityService; - - $identity = IdentityService::factory($client); - $identity->getClient()->authenticate(); - diff --git a/doc/services/identity/Tenants.md.rst b/doc/services/identity/Tenants.md.rst deleted file mode 100644 index 9b58efd1a..000000000 --- a/doc/services/identity/Tenants.md.rst +++ /dev/null @@ -1,29 +0,0 @@ -Tenants -======= - -Intro ------ - -A tenant is a container used to group or isolate resources and/or -identity objects. Depending on the service operator, a tenant may map to -a customer, account, organization, or project. - -Setup ------ - -Tenant objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -List tenants ------------- - -.. code:: php - - $tenants = $service->getTenants(); - - foreach ($tenants as $tenant) { - // ... - } - -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Tokens.md.rst b/doc/services/identity/Tokens.md.rst deleted file mode 100644 index c42ce1573..000000000 --- a/doc/services/identity/Tokens.md.rst +++ /dev/null @@ -1,105 +0,0 @@ -Tokens -====== - -Intro ------ - -A token is an opaque string that represents an authorization to access -cloud resources. Tokens may be revoked at any time and are valid for a -finite duration. - -Setup ------ - -Token objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -Useful object properties/methods --------------------------------- - -+------------+-------------------------------------------+----------------------------------------+--------------------+ -| Property | Description | Getter | Setter | -+============+===========================================+========================================+====================+ -| id | The unique ID of the token | ``getId()`` | ``setId()`` | -+------------+-------------------------------------------+----------------------------------------+--------------------+ -| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | -+------------+-------------------------------------------+----------------------------------------+--------------------+ - -Create token (authenticate) ---------------------------- - -In order to generate a token, you must pass in the JSON template that is -sent to the API. This is because Rackspace's operation expects a -slightly different entity body than OpenStack Keystone. - -Request body for Rackspace's generate token operation: - -.. code:: json - - { - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "foo", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1100111" - } - } - -Request body for Keystone's generate token operation: - -.. code:: json - - { - "auth": { - "passwordCredentials":{ - "username":"demoauthor", - "password":"theUsersPassword" - }, - "tenantId": "12345678" - } - } - -The only real differences you'll notice is the name of the object key -(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret -(``apiKey``/``password``). The ``tenantId`` property in both templates -are optional. You can also add ``tenantName`` too. - -.. code:: php - - use OpenCloud\Common\Http\Message\Formatter; - - $template = sprintf( - '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', - 'my_username', - 'my_api_key' - ); - - $response = $service->generateToken($template); - - $body = Formatter::decode($response); - - // service catalog - $catalog = $body->access->serviceCatalog; - - // token - $token = $body->access->token; - - // user - $user = $body->access->user; - -As you will notice, these variables will be stdClass objects - for fully -fledged functionality, let the client authenticate by itself because it -ends up stocking the necessary models for you. - -To see the response body structure, consult the `official -docs `__. - -Revoke token (destroy session) ------------------------------- - -.. code:: php - - $tokenId = '1234567'; - $service->revokeToken($tokenId); - diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index e69de29bb..35c27131b 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -0,0 +1,46 @@ +Identity v2 +=========== + +Setup +----- + +.. include:: ../common/rs-client.sample + +.. code-block:: php + + $service = $client->identityService(); + + +Operations +---------- + +.. toctree:: + + tokens + users + tenants + +Glossary +-------- + +.. glossary:: + + token + A token is an opaque string that represents an authorization to access + cloud resources. Tokens may be revoked at any time and are valid for a + finite duration. + + tenant + A tenant is a container used to group or isolate resources and/or + identity objects. Depending on the service operator, a tenant may map to + a customer, account, organization, or project. + + user + A user is a digital representation of a person, system, or service who + consumes cloud services. Users have credentials and may be assigned + tokens; based on these credentials and tokens, the authentication + service validates that incoming requests are being made by the user who + claims to be making the request, and that the user has the right to + access the requested resources. Users may be directly assigned to a + particular tenant and behave as if they are contained within that + tenant. diff --git a/doc/services/identity/Roles.md.rst b/doc/services/identity/roles.rst similarity index 100% rename from doc/services/identity/Roles.md.rst rename to doc/services/identity/roles.rst diff --git a/doc/services/identity/tenants.rst b/doc/services/identity/tenants.rst new file mode 100644 index 000000000..a27417cca --- /dev/null +++ b/doc/services/identity/tenants.rst @@ -0,0 +1,26 @@ +Tenants +======= + +List tenants +------------ + +.. code-block:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +Tenant object properties and methods +------------------------------------ + +Once you have a ``OpenCloud\Identity\Resource\Tenant`` object, you can retrieve +information like so: + +.. code-block:: php + + $tenant->getId(); + $tenant->getName(); + $tenant->getDescription(); + $tenant->isEnabled(); diff --git a/doc/services/identity/tokens.rst b/doc/services/identity/tokens.rst new file mode 100644 index 000000000..f49484105 --- /dev/null +++ b/doc/services/identity/tokens.rst @@ -0,0 +1,101 @@ +Tokens +====== + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +To do this, and then generate a token: + +.. code-block:: php + + $json = $client->getCredentials(); + + /** @var $response Guzzle\Http\Message\Response */ + $response = $service->generateToken($json); + $jsonBody = $response->json(); + +When a token is generated by the API, there are a few things returned: + +* a `service catalog `_ + outlining all of the services you can interact with, + including their names, service types, and endpoint URLs. Which services + make up your catalog, and how your catalog is structured, will depend on + your service provider. + +* details about your token, such as its ID, created and expiration date + +* details about your user account + +* details about your tenant + +Interacting with the service catalog +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have the ``$jsonBody``, you can construct a ``Catalog`` object for +easier interaction: + +.. code-block:: php + + $data = $jsonBody->access->serviceCatalog; + $catalog = OpenCloud\Common\Service\Catalog::factory($data); + + foreach ($catalog->getItems() as $service) { + /** @param $service OpenCloud\Common\Service\CatalogItem */ + printf("Catalog item: Name [%s] Type [%s]\n", $service->getName(), $service->getType()); + + foreach ($service->getEndpoints() as $endpoint) { + printf(" Endpoint provided: Region [%s] PublicURL [%s] PrivateURL [%s]\n", + $endpoint->getRegion(), $endpoint->getPublicUrl(), $endpoint->getPrivateUrl()); + } + } + +Interacting with tokens +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->token; + $token = $service->resource('Token', $data); + + printf("Token ID: %s - Token expiry %s", $token->getId(), $token->getExpires()); + + if ($token->hasExpired()) { + // ... + } + +Interacting with users +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->user; + $user = $service->resource('User', $data); + +To see which methods you can call on ``$user`` (which implements +``OpenCloud\Identity\Resource\User``), see our `user documentation `_ +which accompanies this guide. + + +Interacting with tenants +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->tenant; + $tenant = $service->resource('Tenant', $data); + +To see which methods you can call on ``$tenant`` (which implements +``OpenCloud\Identity\Resource\Tenant``), see our `user documentation `_ +which accompanies this guide. + + +Revoke token (destroy session) +------------------------------ + +.. code-block:: php + + $service->revokeToken('{tokenId}'); diff --git a/doc/services/identity/Users.md.rst b/doc/services/identity/users.rst similarity index 79% rename from doc/services/identity/Users.md.rst rename to doc/services/identity/users.rst index e8c2e6d63..b55236a74 100644 --- a/doc/services/identity/Users.md.rst +++ b/doc/services/identity/users.rst @@ -1,26 +1,9 @@ Users ===== -Intro ------ -A user is a digital representation of a person, system, or service who -consumes cloud services. Users have credentials and may be assigned -tokens; based on these credentials and tokens, the authentication -service validates that incoming requests are being made by the user who -claims to be making the request, and that the user has the right to -access the requested resources. Users may be directly assigned to a -particular tenant and behave as if they are contained within that -tenant. - -Setup ------ - -User objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -Useful object properties/methods --------------------------------- +Object properties/methods +------------------------- +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ | Property | Description | Getter | Setter | @@ -43,74 +26,78 @@ Useful object properties/methods List users ---------- -.. code:: php +.. code-block:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } - $users = $service->getUsers(); - foreach ($users as $user) { - // ... - } +Retrieve a user by username +--------------------------- -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. +.. code-block:: php -Get user --------- + $user = $service->getUser('jamie'); -There are various ways to get a specific user: by name, ID and email -address. -.. code:: php +Retrieve a user by user ID +-------------------------- - use OpenCloud\Identity\Constants\User as UserConst; +.. code-block:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{userId}', UserConst::MODE_ID); + + +Retrieve a user by email address +-------------------------------- - // Get user by name - $user1 = $service->getUser('jamie'); +.. code-block:: php - // Get user by ID - $user2 = $service->getUser(123456, UserConst::MODE_ID); + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); - // Get user by email - $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); Create user ----------- -There are a few things to remember when creating a user: +There are a few things to bear in mind when creating a user: -- This operation is available only to users who hold the +* This operation is available only to users who hold the ``identity:user-admin`` role. This admin can create a user who holds the ``identity:default`` user role. -- The created user **will** have access to APIs but **will not** have +* The created user **will** have access to APIs but **will not** have access to the Cloud Control Panel. -- Within an account, a maximum of 100 account users can be added. +* A maximum of 100 account users can be added per account. -- If you attempt to add a user who already exists, an HTTP error 409 +* If you attempt to add a user who already exists, an HTTP error 409 results. + The ``username`` and ``email`` properties are required for creating a user. Providing a ``password`` is optional; if omitted, one will be automatically generated and provided in the response. -.. code:: php - use Guzzle\Http\Exception\ClientErrorResponseException; +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; - try { - // execute operation - $user = $service->createUser(array( - 'username' => 'newUser', - 'email' => 'foo@bar.com' - )); - } catch (ClientErrorResponseException $e) { - // catch 4xx HTTP errors - echo $e->getResponse()->toString(); - } + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + + // show generated password + echo $user->getPassword(); - // show generated password - echo $user->getPassword(); Update user ----------- @@ -118,27 +105,30 @@ Update user When updating a user, specify which attribute/property you want to update: -.. code:: php +.. code-block:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); - $user->update(array( - 'email' => 'new_email@bar.com' - )); Updating a user password -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ Updating a user password requires calling a distinct method: -.. code:: php +.. code-block:: php + + $user->updatePassword('password123'); - $user->updatePassword('password123'); Delete user ----------- -.. code:: php +.. code-block:: php + + $user->delete(); - $user->delete(); List credentials ---------------- @@ -146,16 +136,18 @@ List credentials This operation allows you to see your non-password credential types for all authentication methods available. -.. code:: php +.. code-block:: php + + $creds = $user->getOtherCredentials(); - $creds = $user->getOtherCredentials(); Get user API key ---------------- -.. code:: php +.. code-block:: php + + echo $user->getApiKey(); - echo $user->getApiKey(); Reset user API key ------------------ @@ -163,8 +155,7 @@ Reset user API key When resetting an API key, a new one will be automatically generated for you: -.. code:: php - - $user->resetApiKey(); - echo $user->getApiKey(); +.. code-block:: php + $user->resetApiKey(); + echo $user->getApiKey(); From 76c72d283a2716ff4f391139e7c4b5234f1612b4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 05:04:24 -0800 Subject: [PATCH 424/835] Adding missing inline documentation in OpenCloud\ObjectStore classes. --- lib/OpenCloud/ObjectStore/AbstractService.php | 4 +- lib/OpenCloud/ObjectStore/CDNService.php | 14 ++++- .../Resource/AbstractContainer.php | 31 +++++++++- .../ObjectStore/Resource/AbstractResource.php | 62 +++++++++++++------ lib/OpenCloud/ObjectStore/Service.php | 55 +++++++++------- 5 files changed, 121 insertions(+), 45 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/AbstractService.php b/lib/OpenCloud/ObjectStore/AbstractService.php index 8f92874f8..6094d7780 100644 --- a/lib/OpenCloud/ObjectStore/AbstractService.php +++ b/lib/OpenCloud/ObjectStore/AbstractService.php @@ -30,7 +30,9 @@ abstract class AbstractService extends CatalogService const MAX_OBJECT_SIZE = 5102410241025; /** - * @return Resource\Account + * Returns the Object Store account associated with the service. + * + * @return Resource\Account Object Store account */ public function getAccount() { diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index d45fc494d..0092941e0 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -31,8 +31,12 @@ class CDNService extends AbstractService /** * List CDN-enabled containers. * - * @param array $filter - * @return \OpenCloud\Common\Collection\PaginatedIterator + * @param array $filter Array of filter options such as: + * + * * `limit`: number of results to limit the list to. Optional. + * * `marker`: name of container after which to start the list. Optional. + * * `end_marker`: name of container before which to end the list. Optional. + * @return \OpenCloud\Common\Collection\PaginatedIterator Iterator to list of CDN-enabled containers */ public function listContainers(array $filter = array()) { @@ -40,6 +44,12 @@ public function listContainers(array $filter = array()) return $this->resourceList('CDNContainer', $this->getUrl(null, $filter), $this); } + /** + * Return an existing CDN-enabled container. + * + * @param \stdClass $data Data to initialize container. + * @return CDNContainer CDN-enabled Container + */ public function cdnContainer($data) { $container = new CDNContainer($this, $data); diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php b/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php index a36320652..56cd1c2fa 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php @@ -50,13 +50,28 @@ public function __construct(ServiceInterface $service, $data = null) $this->populate($data); } + /** + * Return the transaction ID for an HTTP API operation. Useful for debugging. + * + * @return string Transaction ID + */ public function getTransId() { return $this->metadata->getProperty(HeaderConst::TRANS_ID); } + /** + * Returns whether this container is CDN-enabled or not. + * + * @return boolean true if this container is CDN-enabled; false, otherwise. + */ abstract public function isCdnEnabled(); + /** + * Returns whether this container has log retention enabled or not. + * + * @return boolean true if this container has log retention enabled; false, otherwise. + */ public function hasLogRetention() { if ($this instanceof CDNContainer) { @@ -66,11 +81,23 @@ public function hasLogRetention() } } + /** + * For internal use only + * + * @return string Name of the primary key field for this resource + */ public function primaryKeyField() { return 'name'; } + /** + * For internal use only + * + * @param string $path Path to add to URL. Optional. + * @param array $params Query parameters to add to URL. Optional. + * @return Url URL of this container + path + query parameters. + */ public function getUrl($path = null, array $params = array()) { if (strlen($this->getName()) == 0) { @@ -91,7 +118,7 @@ protected function createRefreshRequest() * This method will enable your CDN-enabled container to serve out HTML content like a website. * * @param $indexPage The data object name (i.e. a .html file) that will serve as the main index page. - * @return \Guzzle\Http\Message\Response + * @return \Guzzle\Http\Message\Response The HTTP response for this API operation. */ public function setStaticIndexPage($page) { @@ -110,7 +137,7 @@ public function setStaticIndexPage($page) * Set the default error page for your static site. * * @param $name The data object name (i.e. a .html file) that will serve as the main error page. - * @return \Guzzle\Http\Message\Response + * @return \Guzzle\Http\Message\Response The HTTP response for this operation. */ public function setStaticErrorPage($page) { diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php index e2d494ede..8e16b30b3 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php @@ -45,16 +45,31 @@ public function __construct(ServiceInterface $service) $this->metadata = new $this->metadataClass; } + /** + * For internal use only. + * + * @return Service The ObjectStore service associated with this ObjectStore resource. + */ public function getService() { return $this->service; } + /** + * For internal use only. + * + * @return Service The CDN version of the ObjectStore service associated with this ObjectStore resource. + */ public function getCdnService() { return $this->service->getCDNService(); } + /** + * For internal use only. + * + * @return Client The HTTP client associated with the associated ObjectStore service. + */ public function getClient() { return $this->service->getClient(); @@ -62,10 +77,12 @@ public function getClient() /** * Factory method that allows for easy instantiation from a Response object. + * + * For internal use only. * - * @param Response $response - * @param ServiceInterface $service - * @return static + * @param Response $response HTTP response from an API operation. + * @param ServiceInterface $service The ObjectStore service to associate with this ObjectStore resource object. + * @return AbstractResource A concrete sub-class of `AbstractResource`. */ public static function fromResponse(Response $response, ServiceInterface $service) { @@ -81,8 +98,10 @@ public static function fromResponse(Response $response, ServiceInterface $servic /** * Trim headers of their resource-specific prefixes. * - * @param $headers - * @return array + * For internal use only. + * + * @param array $headers Headers as returned from an HTTP response + * @return array Trimmed headers */ public static function trimHeaders($headers) { @@ -121,8 +140,8 @@ protected static function stripPrefix($header) /** * Prepend/stock the header names with a resource-specific prefix. * - * @param array $headers - * @return array + * @param array $headers Headers to use on ObjectStore resource. + * @return array Headers returned with appropriate prefix as expected by ObjectStore service. */ public static function stockHeaders(array $headers) { @@ -147,11 +166,12 @@ public static function stockHeaders(array $headers) } /** - * Set the metadata (local-only) for this object. + * Set the metadata (local-only) for this object. You must call saveMetadata + * to actually persist the metadata using the ObjectStore service. * - * @param $data - * @param bool $constructFromResponse - * @return $this + * @param array $data Object/container metadata key/value pair array. + * @param bool $constructFromResponse Whether the metadata key/value pairs were obtiained from an HTTP response of an ObjectStore API operation. + * @return AbstractResource This object, with metadata set. */ public function setMetadata($data, $constructFromResponse = false) { @@ -167,7 +187,9 @@ public function setMetadata($data, $constructFromResponse = false) } /** - * @return \OpenCloud\Common\Metadata + * Returns metadata for this object. + * + * @return \OpenCloud\Common\Metadata Metadata set on this object. */ public function getMetadata() { @@ -180,7 +202,7 @@ public function getMetadata() * @param array $metadata The array of values you want to set as metadata * @param bool $stockPrefix Whether to prepend each array key with the metadata-specific prefix. For objects, this * would be X-Object-Meta-Foo => Bar - * @return mixed + * @return Response HTTP response from API operation. */ public function saveMetadata(array $metadata, $stockPrefix = true) { @@ -192,7 +214,7 @@ public function saveMetadata(array $metadata, $stockPrefix = true) /** * Retrieve metadata from the API. This method will then set and return this value. * - * @return \OpenCloud\Common\Metadata + * @return \OpenCloud\Common\Metadata Metadata returned from the ObjectStore service for this object/container. */ public function retrieveMetadata() { @@ -208,8 +230,8 @@ public function retrieveMetadata() /** * To delete or unset a particular metadata item. * - * @param $key - * @return mixed + * @param $key Metadata key to unset + * @return Response HTTP response returned from API operation to unset metadata item. */ public function unsetMetadataItem($key) { @@ -224,10 +246,12 @@ public function unsetMetadataItem($key) } /** - * Append a particular array of values to the existing metadata. Analogous to a merge. + * Append a particular array of values to the existing metadata. Analogous + * to a merge. You must call to actually persist the metadata using the + * ObjectStore service. * - * @param array $values - * @return array + * @param array $values The array of values you want to append to metadata. + * @return array Metadata, after `$values` are appended. */ public function appendToMetadata(array $values) { diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index f0e1c3dc9..babd764dd 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -59,7 +59,9 @@ public function __construct(Client $client, $type = null, $name = null, $region } /** - * @return CDNService + * Return the CDN version of the ObjectStore service. + * + * @return CDNService CDN version of the ObjectStore service */ public function getCdnService() { @@ -69,8 +71,12 @@ public function getCdnService() /** * List all available containers. * - * @param array $filter - * @return \OpenCloud\Common\Collection\PaginatedIterator + * @param array $filter Array of filter options such as: + * + * * `limit`: number of results to limit the list to. Optional. + * * `marker`: name of container after which to start the list. Optional. + * * `end_marker`: name of container before which to end the list. Optional. + * @return \OpenCloud\Common\Collection\PaginatedIterator Iterator to list of containers */ public function listContainers(array $filter = array()) { @@ -79,8 +85,10 @@ public function listContainers(array $filter = array()) } /** - * @param $data - * @return Container + * Return a new or existing (if name is specified) container. + * + * @param \stdClass $data Data to initialize container. Optional. + * @return Container Container */ public function getContainer($data = null) { @@ -90,9 +98,9 @@ public function getContainer($data = null) /** * Create a container for this service. * - * @param $name The name of the container + * @param string $name The name of the container * @param array $metadata Additional (optional) metadata to associate with the container - * @return bool|static + * @return bool|Container Newly-created Container upon success; false, otherwise */ public function createContainer($name, array $metadata = array()) { @@ -114,9 +122,9 @@ public function createContainer($name, array $metadata = array()) /** * Check the validity of a potential container name. * - * @param $name - * @return bool - * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError + * @param string $name Name of container + * @return bool True if container name is valid + * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError if container name is invalid */ public function checkContainerName($name) { @@ -145,12 +153,12 @@ public function checkContainerName($name) * will be ignored. You can create up to 1,000 new containers per extraction request. Also note that only regular * files will be uploaded. Empty directories, symlinks, and so on, will not be uploaded. * - * @param $path The path to the archive being extracted - * @param $archive The contents of the archive (either string or stream) + * @param string $path The path to the archive being extracted + * @param string|stream $archive The contents of the archive (either string or stream) * @param string $archiveType The type of archive you're using {@see \OpenCloud\ObjectStore\Constants\UrlType} - * @return \Guzzle\Http\Message\Response - * @throws Exception\BulkOperationException - * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError + * @return \Guzzle\Http\Message\Response HTTP response from API + * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError if specifed `$archiveType` is invalid + * @throws Exception\BulkOperationException if there are errors with the bulk extract */ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ) { @@ -199,17 +207,17 @@ public function bulkDelete(array $paths) * and sent as individual requests. * * @param array $paths The objects you want to delete. Each path needs - * be formatted as /{containerName}/{objectName}. If - * you are deleting object_1 and object_2 from the - * photos_container, the array will be: + * be formatted as `/{containerName}/{objectName}`. If + * you are deleting `object_1` and `object_2` from the + * `photos_container`, the array will be: * * array( * '/photos_container/object_1', * '/photos_container/object_2' * ) * - * @return array The array of responses from the API - * @throws Exception\BulkOperationException + * @return array[Guzzle\Http\Message\Response] HTTP responses from the API + * @throws Exception\BulkOperationException if the bulk delete operation fails */ public function batchDelete(array $paths) { @@ -257,7 +265,12 @@ private function executeBatchDeleteRequest(array $paths) * * @param Container $old Where you're moving files from * @param Container $new Where you're moving files to - * @return array Of PUT responses + * @param array $options Options to configure the migration. Optional. Available options are: + * + * * `read.batchLimit`: Number of files to read at a time from `$old` container. Optional; default = 1000. + * * `write.batchLimit`: Number of files to write at a time to `$new` container. Optional; default = 1000. + * * `read.pageLimit`: Number of filenames to read at a time from `$old` container. Optional; default = 10000. + * @return array[Guzzle\Http\Message\Response] HTTP responses from the API */ public function migrateContainer(Container $old, Container $new, array $options = array()) { From ca3a9b668a223cf65a5ca597904ab403a991c981 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 22:43:10 -0800 Subject: [PATCH 425/835] Adding unit tests + implementation for Neutron Security Groups and Security Group Rules. --- lib/OpenCloud/Networking/Service.php | 100 ++++++++++++++++++ .../Tests/Networking/NetworkingTestCase.php | 10 ++ .../Tests/Networking/ServiceTest.php | 46 ++++++++ 3 files changed, 156 insertions(+) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 3c1f879e2..12bb7477f 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -22,6 +22,8 @@ use OpenCloud\Networking\Resource\Network; use OpenCloud\Networking\Resource\Subnet; use OpenCloud\Networking\Resource\Port; +use OpenCloud\Networking\Resource\SecurityGroup; +use OpenCloud\Networking\Resource\SecurityGroupRule; /** * The Networking class represents the OpenNetwork Neutron service. @@ -290,6 +292,104 @@ public function listPorts(array $params = array()) return $this->resourceList('Port', $url); } + /** + * Returns a SecurityGroup object associated with this Networking service + * + * @param string $id ID of security group to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroup object + */ + public function securityGroup($id = null) + { + return $this->resource('SecurityGroup', $id); + } + + /** + * Creates a new SecurityGroup and returns it. + * + * @param array $params SecurityGroup creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group + * @return \OpenCloud\Networking\Resource\SecurityGroup Object representing created security group + */ + public function createSecurityGroup(array $params = array()) + { + $securityGroup = $this->securityGroup(); + $securityGroup->create($params); + return $securityGroup; + } + + /** + * Returns a SecurityGroup object associated with this Networking service + * + * @param string $id ID of security group to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroup object + */ + public function getSecurityGroup($id) + { + return $this->securityGroup($id); + } + + /** + * Returns a list of security groups you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSecurityGroups(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(SecurityGroup::resourceName())->setQuery($params); + + return $this->resourceList('SecurityGroup', $url); + } + + /** + * Returns a SecurityGroupRule object associated with this Networking service + * + * @param string $id ID of security group rule to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroupRule object + */ + public function securityGroupRule($id = null) + { + return $this->resource('SecurityGroupRule', $id); + } + + /** + * Creates a new SecurityGroupRule and returns it. + * + * @param array $params SecurityGroupRule creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule + * @return \OpenCloud\Networking\Resource\SecurityGroupRule Object representing created security group rule + */ + public function createSecurityGroupRule(array $params = array()) + { + $securityGroupRule = $this->securityGroupRule(); + $securityGroupRule->create($params); + return $securityGroupRule; + } + + /** + * Returns a SecurityGroupRule object associated with this Networking service + * + * @param string $id ID of security group rule to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroupRule object + */ + public function getSecurityGroupRule($id) + { + return $this->securityGroupRule($id); + } + + /** + * Returns a list of security group rules you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSecurityGroupRules(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(SecurityGroupRule::resourceName())->setQuery($params); + + return $this->resourceList('SecurityGroupRule', $url); + } + /** * Return namespaces. * diff --git a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php index be6b304c4..399094b6e 100644 --- a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php +++ b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php @@ -48,4 +48,14 @@ protected function assertIsPort($object) { $this->assertInstanceOf('OpenCloud\Networking\Resource\Port', $object); } + + protected function assertIsSecurityGroup($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\SecurityGroup', $object); + } + + protected function assertIsSecurityGroupRule($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\SecurityGroupRule', $object); + } } diff --git a/tests/OpenCloud/Tests/Networking/ServiceTest.php b/tests/OpenCloud/Tests/Networking/ServiceTest.php index 92b793e19..f2ab2cbfe 100644 --- a/tests/OpenCloud/Tests/Networking/ServiceTest.php +++ b/tests/OpenCloud/Tests/Networking/ServiceTest.php @@ -146,4 +146,50 @@ public function testGetPort() $this->assertIsPort($port); $this->assertEquals('port1', $port->getName()); } + + public function testCreateSecurityGroup() + { + $this->assertIsSecurityGroup($this->service->createSecurityGroup()); + } + + public function testListSecurityGroups() + { + $this->addMockSubscriber($this->makeResponse('{"security_groups":[{"description":"default","id":"85cc3048-abc3-43cc-89b3-377341426ac5","name":"default","security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}],"tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}]}')); + + $securityGroups = $this->service->listSecurityGroups(); + $this->isCollection($securityGroups); + $this->assertIsSecurityGroup($securityGroups->getElement(0)); + } + + public function testGetSecurityGroup() + { + $this->addMockSubscriber($this->makeResponse('{"security_group":{"description":"default","id":"85cc3048-abc3-43cc-89b3-377341426ac5","name":"default","security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}],"tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}}')); + + $securityGroup = $this->service->getSecurityGroup('85cc3048-abc3-43cc-89b3-377341426ac5'); + $this->assertIsSecurityGroup($securityGroup); + $this->assertEquals('default', $securityGroup->getName()); + } + + public function testCreateSecurityGroupRule() + { + $this->assertIsSecurityGroupRule($this->service->createSecurityGroupRule()); + } + + public function testListSecurityGroupRules() + { + $this->addMockSubscriber($this->makeResponse('{"security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}]}')); + + $securityGroupRules = $this->service->listSecurityGroupRules(); + $this->isCollection($securityGroupRules); + $this->assertIsSecurityGroupRule($securityGroupRules->getElement(0)); + } + + public function testGetSecurityGroupRule() + { + $this->addMockSubscriber($this->makeResponse('{"security_group_rule":{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}}')); + + $securityGroupRule = $this->service->getSecurityGroupRule('3c0e45ff-adaf-4124-b083-bf390e5482ff'); + $this->assertIsSecurityGroupRule($securityGroupRule); + $this->assertEquals('egress', $securityGroupRule->getDirection()); + } } From 21ea4b484b473710249e3b566c99bc033b14b369 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:09:09 -0800 Subject: [PATCH 426/835] Adding userguide documentation for security groups and security group rules. --- docs/userguide/Networking/USERGUIDE.md | 147 +++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 74e713b8a..bfa1a217c 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -33,6 +33,16 @@ these entities. * [Get a port](#get-a-port) * [Update a port](#update-a-port) * [Delete a port](#delete-a-port) + * [Security Groups](#security-groups) + * [Create a security group](#create-a-security-group) + * [List security groups](#list-security-groups) + * [Get a security group](#get-a-security-group) + * [Delete a security group](#delete-a-security-group) + * [Security group rule Rules](#security-group-rules) + * [Create a security group rule](#create-a-security-group-rule) + * [List security group rules](#list-security-group-rules) + * [Get a security group rule](#get-a-security-group-rule) + * [Delete a security group rule](#delete-a-security-group-rule) ## Concepts @@ -55,6 +65,10 @@ be assigned to the interfaces plugged into them. When IP addresses are associated with a port, this also implies the port is associated with a subnet because the IP address is taken from the allocation pool for a specific subnet. +* **Security Group**: TODO + +* **Security Group Rule**: TODO + ## Prerequisites ### Client @@ -468,3 +482,136 @@ $port->delete(); ``` [ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] + +## Security Groups + +TODO: Add description + +### Create a security group + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | `new-webservers` | +| `description` | Description of the security group. | String | No | `null` | `security group for webservers` | + +You can create a security group as shown in the following example: + +```php +$securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' +)); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-security-group.php) ] + +### List security groups + +You can list all the security groups to which you have access as shown in the following +example: + +```php +$securityGroups = $networkingService->listSecurityGroups(); +foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-security-groups.php) ] + +### Get a security group + +You can retrieve a specific security group by using that security group's ID, as shown in the +following example: + +```php +$securityGroup = $networkingService->getSecurityGroup('2076db17-a522-4506-91de-c6dd8e837028'); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-security-group.php) ] + +### Delete a security group + +You can delete a security group as shown in the following example: + +```php +$securityGroup->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group.php) ] + +## Security Group Rules + +TODO: Add description + +### Create a security group rule + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `securityGroupId` | The security group ID to associate with this security group rule. | String | Yes | - | `2076db17-a522-4506-91de-c6dd8e837028` | +| `direction` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (`ingress` or `egress`) | Yes | - | `ingress` | +| `ethertype` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (`IPv4` or `IPv6`) | No | `IPv4` | `IPv6` | +| `portRangeMin` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the `portRangeMax` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | +| `portRangeMax` | The maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | +| `protocol` | The protocol that is matched by the security group rule. | String (`tcp`, `udp`, `icmp`) | No | `null` | `tcp` | +| `remoteGroupId` | The remote group ID to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `85cc3048-abc3-43cc-89b3-377341426ac5` | +| `remoteIpPrefix` | The remote IP prefix to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `192.168.5.0` | + +You can create a security group rule as shown in the following example: + +```php +$securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' +)); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-security-group-rule.php) ] + +### List security group rules + +You can list all the security group rules to which you have access as shown in the following +example: + +```php +$securityGroupRules = $networkingService->listSecurityGroupRules(); +foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-security-group-rules.php) ] + +### Get a security group rule + +You can retrieve a specific security group rule by using that security group rule's ID, as shown in the +following example: + +```php +$securityGroupRule = $networkingService->getSecurityGroupRule(''); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-security-group-rule.php) ] + +### Delete a security group rule + +You can delete a security group rule as shown in the following example: + +```php +$securityGroupRule->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group-rule.php) ] From 6a07e2558d74794fcf3c45c867a9a819a1e033ec Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:24:17 -0800 Subject: [PATCH 427/835] Adding downloadable code samples for Security Groups and Security Group Rules. --- .../Networking/create-security-group-rule.php | 42 +++++++++++++++++++ samples/Networking/create-security-group.php | 37 ++++++++++++++++ .../Networking/delete-security-group-rule.php | 36 ++++++++++++++++ samples/Networking/delete-security-group.php | 36 ++++++++++++++++ .../Networking/get-security-group-rule.php | 34 +++++++++++++++ samples/Networking/get-security-group.php | 34 +++++++++++++++ .../Networking/list-security-group-rules.php | 36 ++++++++++++++++ samples/Networking/list-security-groups.php | 36 ++++++++++++++++ 8 files changed, 291 insertions(+) create mode 100644 samples/Networking/create-security-group-rule.php create mode 100644 samples/Networking/create-security-group.php create mode 100644 samples/Networking/delete-security-group-rule.php create mode 100644 samples/Networking/delete-security-group.php create mode 100644 samples/Networking/get-security-group-rule.php create mode 100644 samples/Networking/get-security-group.php create mode 100644 samples/Networking/list-security-group-rules.php create mode 100644 samples/Networking/list-security-groups.php diff --git a/samples/Networking/create-security-group-rule.php b/samples/Networking/create-security-group-rule.php new file mode 100644 index 000000000..e60059774 --- /dev/null +++ b/samples/Networking/create-security-group-rule.php @@ -0,0 +1,42 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Create a security group rule. +$securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' +)); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ diff --git a/samples/Networking/create-security-group.php b/samples/Networking/create-security-group.php new file mode 100644 index 000000000..bf69ad044 --- /dev/null +++ b/samples/Networking/create-security-group.php @@ -0,0 +1,37 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Create a security group. +$securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' +)); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ diff --git a/samples/Networking/delete-security-group-rule.php b/samples/Networking/delete-security-group-rule.php new file mode 100644 index 000000000..d7cf988dd --- /dev/null +++ b/samples/Networking/delete-security-group-rule.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group rule. +$securityGroupRule = $networkingService->getSecurityGroupRule('{securityGroupRuleId}'); + +// 4. Delete security group rule. +$securityGroupRule->delete(); diff --git a/samples/Networking/delete-security-group.php b/samples/Networking/delete-security-group.php new file mode 100644 index 000000000..758da3ae8 --- /dev/null +++ b/samples/Networking/delete-security-group.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group. +$securityGroup = $networkingService->getSecurityGroup('{securityGroupId}'); + +// 4. Delete security group. +$securityGroup->delete(); diff --git a/samples/Networking/get-security-group-rule.php b/samples/Networking/get-security-group-rule.php new file mode 100644 index 000000000..f6ea2bc25 --- /dev/null +++ b/samples/Networking/get-security-group-rule.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group rule. +$securityGroupRule = $networkingService->getSecurityGroupRule('{securityGroupRuleId}'); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ diff --git a/samples/Networking/get-security-group.php b/samples/Networking/get-security-group.php new file mode 100644 index 000000000..7573aa1f9 --- /dev/null +++ b/samples/Networking/get-security-group.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group. +$securityGroup = $networkingService->getSecurityGroup('{securityGroupId}'); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ diff --git a/samples/Networking/list-security-group-rules.php b/samples/Networking/list-security-group-rules.php new file mode 100644 index 000000000..1adba50d7 --- /dev/null +++ b/samples/Networking/list-security-group-rules.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List security group rules +$securityGroupRules = $networkingService->listSecurityGroupRules(); +foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +} diff --git a/samples/Networking/list-security-groups.php b/samples/Networking/list-security-groups.php new file mode 100644 index 000000000..82ffd0931 --- /dev/null +++ b/samples/Networking/list-security-groups.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List security groups +$securityGroups = $networkingService->listSecurityGroups(); +foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +} From 45124ab16aca2db0674a67eb41d3247291f84e78 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:40:40 -0800 Subject: [PATCH 428/835] Adding smoke tests for Security Groups and Security Group Rules. --- tests/OpenCloud/Smoke/Unit/Networking.php | 80 ++++++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 74f39fac7..b6ab4592e 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -26,9 +26,11 @@ */ class Networking extends AbstractUnit implements UnitInterface { - protected $cleanupNetworkIds = array(); - protected $cleanupSubnetIds = array(); - protected $cleanupPortIds = array(); + protected $cleanupNetworkIds = array(); + protected $cleanupSubnetIds = array(); + protected $cleanupPortIds = array(); + protected $cleanupSecurityGroupIds = array(); + protected $cleanupSecurityGroupRuleIds = array(); public function setupService() { @@ -40,6 +42,8 @@ public function main() $this->testNetworkOperations(); $this->testSubnetOperations(); $this->testPortOperations(); + $this->testSecurityGroupOperations(); + $this->testSecurityGroupRuleOperations(); } protected function testNetworkOperations() @@ -251,8 +255,78 @@ protected function testPortOperations() )); } + protected function testSecurityGroupOperations() + { + $this->step('Create security group'); + $security group = $this->getService()->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); + $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + $this->cleanupSecurityGroupIds[] = $securityGroup->getId(); + + $this->step('List security groups'); + $securityGroups = $this->getService()->listSecurityGroups(); + $this->stepInfo('%-40s | %s', 'Security Group ID', 'Security Group name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($securityGroups as $securityGroup) { + $this->stepInfo('%-40s | %s', $securityGroup->getId(), $securityGroup->getName()); + } + + $this->step('Get security group'); + $security group = $this->getService()->getSecurityGroup($securityGroup->getId()); + $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); + $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + } + + protected function testSecurityGroupRuleOperations() + { + $securityGroup1 = $this->getService()->createSecurityGroup(array( + 'name' => 'test_security_group_for_test_security_group_rule' + )); + $this->cleanupSecurityGroupIds[] = $securityGroup1->getId(); + + $this->step('Create security group rule'); + $security group rule = $this->getService()->createSecurityGroupRule(array( + 'securityGroupId' => $securityGroup1->getId(), + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); + $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); + $this->cleanupSecurityGroupRuleIds[] = $securityGroupRule->getId(); + + $this->step('List security group rules'); + $securityGroupRules = $this->getService()->listSecurityGroupRules(); + $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($securityGroupRules as $securityGroupRule) { + $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getName()); + } + + $this->step('Get security group rule'); + $security group rule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); + $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); + $this->stepInfo('Security Group Rule Name: ' . $securityGroupRule->getName()); + } + public function teardown() { + foreach ($this->cleanupSecurityGroupRuleIds as $securityGroupRuleId) { + $securityGroupRule = $this->getService()->getSecurityGroupRule($securityGroupRuleId); + $securityGroupRule->delete(); + } + + foreach ($this->cleanupSecurityGroupIds as $securityGroupId) { + $securityGroup = $this->getService()->getSecurityGroup($securityGroupId); + $securityGroup->delete(); + } + foreach ($this->cleanupPortIds as $portId) { $port = $this->getService()->getPort($portId); $port->delete(); From 2d42a5c60b3331e12dcfd069283cc5168966f99a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:41:48 -0800 Subject: [PATCH 429/835] Forgot to check-in resource classes. --- .../Networking/Resource/SecurityGroup.php | 67 +++++++++++++++ .../Networking/Resource/SecurityGroupRule.php | 82 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 lib/OpenCloud/Networking/Resource/SecurityGroup.php create mode 100644 lib/OpenCloud/Networking/Resource/SecurityGroupRule.php diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php new file mode 100644 index 000000000..105fc22a1 --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -0,0 +1,67 @@ + 'securityGroupRules', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'name', + 'description' + ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as {@see OpenCloud\Networking\Service}. + */ + public function createJson() + { + return parent::createJson(); + } + + /** + * {@inheritDoc} + */ + public function update($params = array()) + { + return $this->noUpdate(); + } +} diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php new file mode 100644 index 000000000..3659d08ac --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -0,0 +1,82 @@ + 'portRangeMin', + 'port_range_max' => 'portRangeMax', + 'remote_group_id' => 'remoteGroupId', + 'remote_ip_prefix' => 'remoteIpPrefix', + 'security_group_id' => 'securityGroupId', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'direction', + 'ethertype', + 'securityGroupId', + 'portRangeMin', + 'portRangeMax', + 'protocol', + 'remoteGroupId', + 'remoteIpPrefix' + ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as {@see OpenCloud\Networking\Service}. + */ + public function createJson() + { + return parent::createJson(); + } + + /** + * {@inheritDoc} + */ + public function update($params = array()) + { + return $this->noUpdate(); + } +} From 953ad0da38e08248eb3ccd33c509fe0ee67e6cd1 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:11 +0100 Subject: [PATCH 430/835] Add docs for Images --- doc/services/image/Images.md.rst | 107 ------------------ doc/services/image/Tags.md.rst | 28 ----- doc/services/image/images.rst | 84 ++++++++++++++ doc/services/image/index.rst | 41 +++++++ .../image/{Schemas.md.rst => schemas.rst} | 29 ++--- .../image/{Sharing.md.rst => sharing.rst} | 50 ++++---- doc/services/image/tags.rst | 29 +++++ 7 files changed, 189 insertions(+), 179 deletions(-) delete mode 100644 doc/services/image/Images.md.rst delete mode 100644 doc/services/image/Tags.md.rst create mode 100644 doc/services/image/images.rst rename doc/services/image/{Schemas.md.rst => schemas.rst} (87%) rename doc/services/image/{Sharing.md.rst => sharing.rst} (75%) create mode 100644 doc/services/image/tags.rst diff --git a/doc/services/image/Images.md.rst b/doc/services/image/Images.md.rst deleted file mode 100644 index ef187f083..000000000 --- a/doc/services/image/Images.md.rst +++ /dev/null @@ -1,107 +0,0 @@ -Images -====== - -A virtual machine image is a single file which contains a virtual disk -that has an installed bootable operating system. In the Cloud Images -API, an image is represented by a JSON-encoded data structure (the image -schema) and its raw binary data (the image file). - -An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. - -Setup ------ - -You instantiate an Image object from its ``OpenCloud\Image\Service`` -class, which is available from the OpenStack/Rackspace client: - -.. code:: php - - $service = $client->imageService('cloudImages', 'IAD'); - -View the guides for more information about `clients <../Clients.md>`__ -or `services <../Services.md>`__. - -List images ------------ - -.. code:: php - - $images = $service->listImages(); - - foreach ($images as $image) { - /** @param $image OpenCloud\Image\Resource\Image */ - } - -For more information about working with iterators, please see the -`iterators documentation <../Iterators.md>`__. - -Get image details ------------------ - -.. code:: php - - /** @param $image OpenCloud\Image\Resource\Image */ - $image = $service->getImage(''); - -A note on schema classes -~~~~~~~~~~~~~~~~~~~~~~~~ - -Both ``OpenCloud\Image\Resource\Image`` and -``OpenCloud\Image\Resource\Member`` extend the -``AbstractSchemaResource`` abstract class, which offers some unique -functionality. - -Because these resources are inherently dynamic - i.e. they are modelled -on dynamic JSON schema - you need to access their state in a way -different than conventional getter/setter methods, and even class -properties. For this reason, they implement SPL's native -```ArrayAccess`` `__ -interface which allows you to access their state as a conventional -array: - -.. code:: php - - $image = $service->getImage(''); - - $id = $image['id']; - $tags = $image['tags']; - -Update image ------------- - -You can only update your own custom images - you cannot update or delete -base images. The way in which you may update your image is dictated by -its `schema `__. - -Although you should be able to add new and replace existing properties, -always prepare yourself for a situation where it might be forbidden: - -.. code:: php - - use OpenCloud\Common\Exceptions\ForbiddenOperationException; - - try { - $image->update(array( - 'name' => 'foo', - 'newProperty' => 'bar' - )); - } catch (ForbiddenOperationException $e) { - // A 403 Forbidden was returned - } - -There are three operations that can take place for each Image property: - -- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation - will occur, removing the property from the JSON document -- If a non-false value is provided and the property does not exist, an - ``ADD`` operation will add it to the document -- If a non-false value is provided and the property does exist, a - ``REPLACE`` operation will modify the property in the document - -Delete image ------------- - -.. code:: php - - $image->delete(); - diff --git a/doc/services/image/Tags.md.rst b/doc/services/image/Tags.md.rst deleted file mode 100644 index af5baf157..000000000 --- a/doc/services/image/Tags.md.rst +++ /dev/null @@ -1,28 +0,0 @@ -Image tags -========== - -An image tag is a string of characters used to identify a specific image -or images. - -Setup ------ - -All member operations are executed against an `Image `__, so -you will need to set this up first. - -Add image tag -------------- - -.. code:: php - - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->addTag('jamie_dev'); - -Delete image tag ----------------- - -.. code:: php - - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->deleteTag('jamie_dev'); - diff --git a/doc/services/image/images.rst b/doc/services/image/images.rst new file mode 100644 index 000000000..ec1a17c34 --- /dev/null +++ b/doc/services/image/images.rst @@ -0,0 +1,84 @@ +Images +====== + +List images +----------- + +.. code-block:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + + +Get image details +----------------- + +.. code-block:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage('{imageId}'); + + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and ``OpenCloud\Image\Resource\Member`` +extend the ``AbstractSchemaResource`` class, which offers some unique functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a different way +than conventional getter/setter methods, and even class properties. For this +reason, they implement SPL's native +`ArrayAccess `_ +interface which allows you to access their state as a conventional +array: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + $id = $image['id']; + $tags = $image['tags']; + + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +* If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +* If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +* If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code-block:: php + + $image->delete(); diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index e69de29bb..d54b09ad0 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -0,0 +1,41 @@ +Images v1 +========= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +.. code-block:: php + + $service = $client->imageService(null, '{region}'); + + +Operations +---------- + +.. toctree:: + + images + schemas + sharing + tags + + +Glossary +-------- + + image + A virtual machine image is a single file which contains a virtual disk + that has an installed bootable operating system. In the Cloud Images + API, an image is represented by a JSON-encoded data structure (the image + schema) and its raw binary data (the image file). + + schema + The Cloud Images API supplies JSON documents describing the JSON-encoded + data structures that represent domain objects, so that a client knows + exactly what to expect in an API response. + + tag + An image tag is a string of characters used to identify a specific image + or images. diff --git a/doc/services/image/Schemas.md.rst b/doc/services/image/schemas.rst similarity index 87% rename from doc/services/image/Schemas.md.rst rename to doc/services/image/schemas.rst index 8f792c4c0..c1b85b8de 100644 --- a/doc/services/image/Schemas.md.rst +++ b/doc/services/image/schemas.rst @@ -1,13 +1,6 @@ JSON schemas ============ -The Cloud Images API supplies json documents describing the JSON-encoded -data structures that represent domain objects, so that a client knows -exactly what to expect in an API response. - -A JSON Schema is represented by the -``OpenCloud\Image\Resource\Schema\Schema`` class. - Schema types ------------ @@ -19,7 +12,7 @@ Example response from the API A sample response from the API, for an Images schema might be: -.. code:: json +.. code-block:: json { "name": "images", @@ -70,7 +63,7 @@ links and a properties object. Inside this properties object we see the structure of this top-level ``images`` object. So we know that it will take this form: -.. code:: json +.. code-block:: json { "images": [something...] @@ -80,7 +73,7 @@ Within this object, we can see that it contains an array of anonymous objects, each of which is called ``image`` and has its own set of nested properties: -.. code:: json +.. code-block:: json { "images": [ @@ -101,7 +94,7 @@ i.e. a *subschema*. We know that each object has an ID property (string), a name property (string), a visibility property (can either be ``private`` or ``public``), etc. -.. code:: json +.. code-block:: json { "images": [ @@ -147,21 +140,21 @@ Requests need to use the In order for the operation to occur, the request entity body needs to contain a very particular structure: -:: +.. code-block:: json [ {"op": "replace", "path": "/name", "value": "Fedora 17"}, {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} ] -The ``op`` key refers to the type of Operation (see -``OpenCloud\Image\Enum\OperationType`` for a full list). +* The ``op`` key refers to the type of Operation (see + ``OpenCloud\Image\Enum\OperationType`` for a full list). -The ``path`` key is a JSON pointer to the document property you want to -modify or insert. JSON pointers are defined in `RFC -6901 `__. +* The ``path`` key is a JSON pointer to the document property you want to + modify or insert. JSON pointers are defined in `RFC + 6901 `__. -The ``value`` key is the value. +* The ``value`` key is the value. Because this is all handled for you behind the scenes, we will not go into exhaustive depth about how this operation is handled. You can diff --git a/doc/services/image/Sharing.md.rst b/doc/services/image/sharing.rst similarity index 75% rename from doc/services/image/Sharing.md.rst rename to doc/services/image/sharing.rst index b1a1ea382..5edf63ab4 100644 --- a/doc/services/image/Sharing.md.rst +++ b/doc/services/image/sharing.rst @@ -63,66 +63,64 @@ Additional notes Setup ----- -All member operations are executed against an `Image `__, so -you will need to set this up first. +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + List image members ------------------ This operation is available for both producers and consumers. -.. code:: php +.. code-block:: php - $members = $image->listMembers(); + $members = $image->listMembers(); - foreach ($members as $member) { - /** @param $member OpenCloud\Image\Resource\Member */ - } + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } -For more information about working with iterators, please see the -`iterators documentation <../Iterators.md>`__. Create image member ------------------- This operation is only available for producers. -.. code:: php +.. code-block:: php - $tenantId = 12345; + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember('{tenantId}'); - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->createMember($tenantId); Delete image member ------------------- This operation is only available for producers. -.. code:: php +.. code-block:: php - $tenantId = 12345; + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); + $member->delete(); - /** @param $member OpenCloud\Image\Resource\Member */ - $member = $image->getMember($tenantId); - - $member->delete(); Update image member status -------------------------- This operation is only available for consumers. -.. code:: php - - use OpenCloud\Images\Enum\MemberStatus; +.. code-block:: php - $tenantId = 12345; + use OpenCloud\Images\Enum\MemberStatus; - /** @param $member OpenCloud\Image\Resource\Member */ - $member = $image->getMember($tenantId); + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); - $member->updateStatus(MemberStatus::ACCEPTED); + $member->updateStatus(MemberStatus::ACCEPTED); The acceptable states you may pass in are made available to you through the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` diff --git a/doc/services/image/tags.rst b/doc/services/image/tags.rst new file mode 100644 index 000000000..0a376e6de --- /dev/null +++ b/doc/services/image/tags.rst @@ -0,0 +1,29 @@ +Image tags +========== + +Setup +----- + +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + +Add image tag +------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); From fe09985dc0b8b8e115b87fee6b6d6d210b96bfab Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:21 +0100 Subject: [PATCH 431/835] Add LB docs --- doc/services/load-balancer/README.md.rst | 92 -- doc/services/load-balancer/USERGUIDE.md.rst | 840 ------------------ doc/services/load-balancer/access.rst | 79 ++ doc/services/load-balancer/caching.rst | 35 + doc/services/load-balancer/errors.rst | 44 + doc/services/load-balancer/index.rst | 93 ++ .../load-balancer/lb-setup.sample.rst | 9 + doc/services/load-balancer/load-balancer.rst | 165 ++++ doc/services/load-balancer/logging.rst | 33 + doc/services/load-balancer/metadata.rst | 46 + doc/services/load-balancer/monitors.rst | 43 + doc/services/load-balancer/nodes.rst | 124 +++ doc/services/load-balancer/sessions.rst | 53 ++ doc/services/load-balancer/ssl.rst | 52 ++ doc/services/load-balancer/stats.rst | 59 ++ doc/services/load-balancer/virtual-ips.rst | 74 ++ 16 files changed, 909 insertions(+), 932 deletions(-) delete mode 100644 doc/services/load-balancer/README.md.rst delete mode 100644 doc/services/load-balancer/USERGUIDE.md.rst create mode 100644 doc/services/load-balancer/access.rst create mode 100644 doc/services/load-balancer/caching.rst create mode 100644 doc/services/load-balancer/errors.rst create mode 100644 doc/services/load-balancer/lb-setup.sample.rst create mode 100644 doc/services/load-balancer/load-balancer.rst create mode 100644 doc/services/load-balancer/logging.rst create mode 100644 doc/services/load-balancer/metadata.rst create mode 100644 doc/services/load-balancer/monitors.rst create mode 100644 doc/services/load-balancer/nodes.rst create mode 100644 doc/services/load-balancer/sessions.rst create mode 100644 doc/services/load-balancer/ssl.rst create mode 100644 doc/services/load-balancer/stats.rst create mode 100644 doc/services/load-balancer/virtual-ips.rst diff --git a/doc/services/load-balancer/README.md.rst b/doc/services/load-balancer/README.md.rst deleted file mode 100644 index 862cf20d2..000000000 --- a/doc/services/load-balancer/README.md.rst +++ /dev/null @@ -1,92 +0,0 @@ -Load Balancers -============== - -A **load balancer** is a device that distributes incoming network -traffic amongst multiple back-end systems. These back-end systems are -called the **nodes** of the load balancer. - -Getting started ---------------- - -1. Instantiate a Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Retrieve the server instances you want to add as nodes of the load balancer. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); - - $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); - $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); - -The example above uses two server instances that have already been -created. It retrieves the server instances using their IDs. See also: -`creating server instances <>`__. - -3. Obtain a Load Balancer service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This object will be used to first define the load balancer nodes and -later create the load balancer itself. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -4. Define a load balancer node for each server. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $serverOneNode = $loadBalancer->node(); - $serverOneNode->address = $serverOne->addresses->private[0]->addr; - $serverOneNode->port = 8080; - $serverOneNode->condition = 'ENABLED'; - - $serverTwoNode = $loadBalancer->node(); - $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; - $serverTwoNode->port = 8080; - $serverTwoNode->condition = 'ENABLED'; - -In the example above, each node runs a service that listens on port -8080. Further, each node will start out as ``ENABLED``, which means it -will be ready to receive network traffic from the load balancer as soon -as it is created. - -5. Create the load balancer with the two nodes. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->addVirtualIp('PUBLIC'); - $loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) - )); - -In the example above, the load balancer will have a virtual IP address -accessible from the public Internet. Also notice that the port the load -balancer listens on (80) does not need to match the ports of its nodes -(8080). - -Next steps ----------- - -Once you have created a load balancer, there is a lot you can do with -it. See the `complete user guide for load balancers `__. diff --git a/doc/services/load-balancer/USERGUIDE.md.rst b/doc/services/load-balancer/USERGUIDE.md.rst deleted file mode 100644 index 788651f2b..000000000 --- a/doc/services/load-balancer/USERGUIDE.md.rst +++ /dev/null @@ -1,840 +0,0 @@ -The Complete User Guide to Load Balancers -========================================= - -Prerequisites -------------- - -Client -~~~~~~ - -To use the load balancers service, you must first instantiate a -``Rackspace`` client object. - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Load Balancer Service -~~~~~~~~~~~~~~~~~~~~~ - -All operations on load balancers are done via a load balancer service -object. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -Cloud Servers -~~~~~~~~~~~~~ - -Many of the examples in this document use two cloud servers as nodes for -the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer -to these two cloud servers. - -Load Balancers --------------- - -A **load balancer** is a device that distributes incoming network -traffic amongst multiple back-end systems. These back-end systems are -called the **nodes** of the load balancer. - -Create Load Balancer -~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $serverOneNode = $loadBalancer->node(); - $serverOneNode->address = $serverOne->addresses->private[0]->addr; - $serverOneNode->port = 8080; - $serverOneNode->condition = 'ENABLED'; - - $serverTwoNode = $loadBalancer->node(); - $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; - $serverTwoNode->port = 8080; - $serverTwoNode->condition = 'ENABLED'; - - $loadBalancer->addVirtualIp('PUBLIC'); - $loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) - )); - -List Load Balancer Details -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a single load balancer's details by using its ID. - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer('254889'); - - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - -List Load Balancers -~~~~~~~~~~~~~~~~~~~ - -You can retrieve a list of all your load balancers. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $loadBalancers = $loadBalancerService->loadBalancerList(); - foreach ($loadBalancers as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - } - -Update Load Balancer Attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can update one or more of the following load balancer attributes: - -- ``name``: The name of the load balancer -- ``algorithm``: The algorithm used by the load balancer to distribute - traffic amongst its nodes. See also: `Load balancing - algorithms <#algorithms>`__. -- ``protocol``: The network protocol used by traffic coming in to the - load balancer. See also: `Protocols <#protocols>`__. -- ``port``: The network port on which the load balancer listens for - incoming traffic. -- ``halfClosed``: Enable or Disable Half-Closed support for the load - balancer. -- ``timeout``: The timeout value for the load balancer to communicate - with its nodes. -- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for - the load balancer. When enabled, any HTTP request will return status - code 301 (Moved Permanently), and the requestor will be redirected to - the requested URL via the HTTPS protocol on port 443. For example, - http://example.com/page.html would be redirected to https:// - example.com/page.html. Only available for HTTPS protocol (``port`` = - 443), or HTTP Protocol with a properly configured SSL Termination - (\`secureTrafficOnly=true, securePort=443). See also: `SSL - Termination <#ssl-termination>`__. - -Updating a single attribute of a load balancer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->update(array( - 'name' => 'New name' - )); - -Updating multiple attributes of a load balancer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->update(array( - 'name' => 'New name', - 'algorithm' => 'ROUND_ROBIN' - )); - -Remove Load Balancer -~~~~~~~~~~~~~~~~~~~~ - -When you no longer have a need for the load balancer, you can remove it. - -.. code:: php - - $loadBalancer->delete(); - -Nodes ------ - -A **node** is a backend device that provides a service on specified IP -and port. An example of a load balancer node might be a web server -serving HTTP traffic on port 8080. - -A load balancer typically has multiple nodes attached to it so it can -distribute incoming network traffic amongst them. - -List Nodes -~~~~~~~~~~ - -You can list the nodes attached to a load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $nodes = $loadBalancer->nodeList(); - foreach ($nodes as $node) { - /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ - } - -Add Nodes -~~~~~~~~~ - -You can attach additional nodes to a load balancer. Assume -``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` -and ``$serverTwo`` - and you want to attach a third node to it, say -``$serverThree``, which provides a service on port 8080. - -**Important:** Remember to call ``$loadBalancer->addNodes()`` after all -the calls to ``$loadBalancer->addNode()`` as shown below. - -.. code:: php - - $address = $serverThree->addresses->private[0]->addr; - $loadBalancer->addNode($address, 8080); - $loadBalancer->addNodes(); - -The ``addNode`` method accepts three more optional parameters, in -addition to the two shown above: - -| Position \| Description \| Data type \| Required? \| Default value \| -| ----------- \| --------------- \| --------------\| -------------- \| ------------------ \| -|  1 \| IP address of node \| String \| Yes \| - \| -|  2 \| Port used by node's service \| Integer \| Yes \| - \| -|  3 \| Starting condition of node: -|  4 \| Type of node to add: -|  5 \| Weight, between 1 and 100, given to node when distributing -traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the -``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| -No \| 1 \| - -Modify Nodes -~~~~~~~~~~~~ - -You can modify one or more of the following node attributes: - -- ``condition``: The condition of the load balancer: - - - ``ENABLED`` – Node is ready to receive traffic from the load - balancer. - - ``DISABLED`` – Node should not receive traffic from the load - balancer. - - ``DRAINING`` – Node should process any traffic it is already - receiving but should not receive any further traffic from the load - balancer. - -- ``type``: The type of the node: - - - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation - to receive traffic from the load balancer. - - ``SECONDARY`` – Nodes defined as SECONDARY are only in the - rotation to receive traffic from the load balancer when all the - primary nodes fail. - -- ``weight``: The weight, between 1 and 100, given to node when - distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the - ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. - -Modifying a single attribute of a node -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\NodeCondition; - - $node->update(array( - 'condition' => NodeCondition::DISABLED - )); - -Modifying multiple attributes of a node -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\NodeCondition; - use OpenCloud\LoadBalancer\Enum\NodeType; - - $node->update(array( - 'condition' => NodeCondition::DISABLED, - 'type' => NodeType::SECONDARY - )); - -Remove Nodes -~~~~~~~~~~~~ - -There are two ways to remove a node. - -Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $node->delete(); - -Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->removeNode(490639); - -The ``removeNode`` method, as shown above, accepts the following -arguments: - -+------------+---------------+-------------+-------------+-----------------+ -| Position | Description | Data type | Required? | Default value | -+============+===============+=============+=============+=================+ -| 1 | ID of node | Integer | Yes | - | -+------------+---------------+-------------+-------------+-----------------+ - -View Node Service Events -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can view events associated with the activity between a node and a -load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $nodeEvents = $loadBalancer->nodeEventList(); - foreach ($nodeEvents as $nodeEvent) { - /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ - } - -Virtual IPs ------------ - -A **virtual IP (VIP)** makes a load balancer accessible by clients. The -load balancing service supports either a public VIP address -(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP -address (``SERVICENET``), routable only within the region in which the -load balancer resides. - -List Virtual IPs -~~~~~~~~~~~~~~~~ - -You can list the VIPs associated with a load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $vips = $loadBalancer->virtualIpList(); - foreach ($vips as $vip) { - /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ - } - -Add Virtual IPv6 -~~~~~~~~~~~~~~~~ - -You can add additional IPv6 VIPs to a load balancer. - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\IpType; - - $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); - -The ``addVirtualIp`` method, as shown above, accepts the following -arguments: - -| Position \| Description \| Data type \| Required? \| Default value \| -| ----------- \| --------------- \| -------------- \|-------------- \| ------------------ \| -|  1 \| Type of VIP: -|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| - -Remove Virtual IPs -~~~~~~~~~~~~~~~~~~ - -You can remove a VIP from a load balancer. - -.. code:: php - - $vip->remove(); - -Please note that a load balancer must have at least one VIP associated -with it. If you try to remove a load balancer's last VIP, a -``ClientErrorResponseException`` will be thrown. - -Algorithms ----------- - -Load balancers use an **algorithm** to determine how incoming traffic is -distributed amongst the back-end nodes. - -List Load Balancing Algorithms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all supported load balancing algorithms using a load -balancer service object. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $algorithms = $loadBalancerService->algorithmList(); - foreach ($algorithms as $algorithm) { - /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ - } - -Protocols ---------- - -When a load balancer is created a network protocol must be specified. -This network protocol should be based on the network protocol of the -back-end service being load balanced. - -List Load Balancing Protocols -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all supported network protocols using a load balancer -service object. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $protocols = $loadBalancerService->protocolList(); - foreach ($protocols as $protocol) { - /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ - } - -Session Persistence -------------------- - -**Session persistence** is a feature of the load balancing service that -forces multiple requests, of the same protocol, from clients to be -directed to the same node. This is common with many web applications -that do not inherently share application state between back-end servers. - -There are two types (or modes) of session persistence: - -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Name | Description | -+===================+===================================================================================================================================================================================================================================+ -| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -List Session Persistence Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistenceType = $sessionPersistence->persistenceType; - - /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ - -In the example above: - -- If session persistence is enabled, the value of - ``$sessionPersistenceType`` is the type of session persistence: - either ``HTTP_COOKIE`` or ``SOURCE_IP``. -- If session persistence is disabled, the value of - ``$sessionPersistenceType`` is ``null``. - -Enable Session Persistence -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistence->update(array( - 'persistenceType' => 'HTTP_COOKIE' - )); - -Disable Session Persistence -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistence->delete(); - -Connection Logging ------------------- - -The **connection logging** feature allows logs to be delivered to a -Cloud Files account every hour. For HTTP-based protocol traffic, these -are Apache-style access logs. For all other traffic, this is connection -and transfer logging. - -Check Logging Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $connectionLogging bool **/ - - $connectionLogging = $loadBalancer->hasConnectionLogging(); - -In the example above: - -- If connection logging is enabled, the value of ``$connectionLogging`` - is ``true``. -- If connection logging is disabled, the value of - ``$connectionLogging`` is ``false``. - -Enable Connection Logging -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableConnectionLogging(true); - -Disable Connection Logging -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableConnectionLogging(false); - -Error Page ----------- - -An **error page** is the html file that is shown to the end user when an -error in the service has been thrown. By default every virtual server is -provided with the default error file. It is also possible to set a -custom error page for a load balancer. - -View Error Page Content -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPageContent = $errorPage->content; - - /** @var $errorPageContent string **/ - -In the example above the value of ``$errorPageContent`` is the HTML for -that page. This could either be the HTML of the default error page or of -your custom error page. - -Set Custom Error Page -~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPage->update(array( - 'content' => '' - )); - -Delete Custom Error Page -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPage->delete(); - -Allowed Domains ---------------- - -**Allowed domains** are a restricted set of domain names that are -allowed to add load balancer nodes. - -List Allowed Domains -~~~~~~~~~~~~~~~~~~~~ - -You can list all allowed domains using a load balancer service object. -An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is -returned. - -.. code:: php - - $allowedDomains = $loadBalancerService->allowedDomainList(); - foreach ($allowedDomains as $allowedDomain) { - /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ - } - -Access Lists ------------- - -**Access Lists** allow fine-grained network access to a load balancer's -VIP. Using access lists, network traffic to a load balancer's VIP can be -allowed or denied from a single IP address, multiple IP addresses or -entire network subnets. - -Note that ``ALLOW`` network items will take precedence over ``DENY`` -network items in an access list. - -To reject traffic from all network items except those with the ``ALLOW`` -type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. - -View Access List -~~~~~~~~~~~~~~~~ - -You can view a load balancer's access list. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $accessList = $loadBalancer->accessList(); - foreach ($accessList as $networkItem) { - /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ - } - -Add Network Items To Access List -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can add network items to a load balancer's access list very easily: - -.. code:: php - - $loadBalancer->createAccessList(array( - (object) array( - 'type' => 'ALLOW', - 'address' => '206.160.165.1/24' - ), - (object) array( - 'type' => 'DENY', - 'address' => '0.0.0.0/0' - ) - )); - -In the above example, we allowed access for 1 IP address, and used the -"0.0.0.0" wildcard to blacklist all other traffic. - -Remove Network Item From Access List -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You an remove a network item from a load balancer's access list. - -.. code:: php - - $networkItem->delete(); - -Content Caching ---------------- - -When **content caching** is enabled on a load balancer, -recently-accessed files are stored on the load balancer for easy -retrieval by web clients. Requests to the load balancer for these files -are serviced by the load balancer itself, which reduces load off its -back-end nodes and improves response times as well. - -Check Content Caching Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $contentCaching bool **/ - - $contentCaching = $loadBalancer->hasContentCaching(); - -In the example above: - -- If content caching is enabled, the value of ``$contentCaching`` is - ``true``. -- If content caching is disabled, the value of ``$contentCaching`` is - ``false``. - -Enable Content Caching -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableContentCaching(true); - -Disable Content Caching -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableContentCaching(false); - -SSL Termination ---------------- - -The SSL Termination feature allows a load balancer user to terminate SSL -traffic at the load balancer layer versus at the web server layer. A -user may choose to configure SSL Termination using a key and an SSL -certificate or an (Intermediate) SSL certificate. - -When SSL Termination is configured on a load balancer, a secure shadow -server is created that listens only for secure traffic on a -user-specified port. This shadow server is only visible to and -manageable by the system. Existing or updated attributes on a load -balancer with SSL Termination will also apply to its shadow server. For -example, if Connection Logging is enabled on an SSL load balancer, it -will also be enabled on the shadow server and Cloud Files logs will -contain log files for both. - -View current SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ - - $sslConfig = $loadBalancer->SSLTermination(); - -Update SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sslConfig->update(array( - 'enabled' => true, - 'securePort' => 443, - 'privateKey' => $key, - 'certificate' => $cert - )); - -For a full list, with explanations, of required and optional attributes, -please consult the `official -documentation `__ - -Delete SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sslConfig->delete(); - -Metadata --------- - -Metadata can be associated with each load balancer and each node for the -client's personal use. It is defined using key-value pairs where the key -and value consist of alphanumeric characters. A key is unique per load -balancer. - -List metadata -~~~~~~~~~~~~~ - -.. code:: php - - $metadataList = $loadBalancer->metadataList(); - - foreach ($metadataList as $metadataItem) { - printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); - } - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Add metadata -~~~~~~~~~~~~ - -.. code:: php - - $metadataItem = $loadBalancer->metadata(); - $metadataItem->create(array( - 'key' => 'foo', - 'value' => 'bar' - )); - -Modify metadata -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadataItem = $loadBalancer->metadata('foo'); - $metadataItem->update(array( - 'value' => 'baz' - )); - -Remove metadata -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadataItem->delete(); - -Monitors --------- - -The load balancing service includes a health monitoring operation which -periodically checks your back-end nodes to ensure they are responding -correctly. If a node is not responding, it is removed from rotation -until the health monitor determines that the node is functional. In -addition to being performed periodically, the health check also is -performed against every node that is added to ensure that the node is -operating properly before allowing it to service traffic. Only one -health monitor is allowed to be enabled on a load balancer at a time. - -.. code:: php - - /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ - - $healthMonitor = $loadBalancer->healthMonitor(); - - printf( - "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", - $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout - ); - -For a full list, with explanations, of required and optional attributes, -please consult the `official -documentation `__ - -Update or delete -~~~~~~~~~~~~~~~~ - -.. code:: php - - // Update - $healthMonitor->update(array( - 'delay' => 120, - 'timeout' => 60, - 'type' => 'CONNECT' - 'attemptsBeforeDeactivation' => 3 - )); - - // Delete - $healthMonitor->delete(); - -Statistics ----------- - -You can retrieve detailed stats about your load balancer, including the -following information: - -- ``connectTimeOut`` – Connections closed by this load balancer because - the 'connect\_timeout' interval was exceeded. -- ``connectError`` – Number of transaction or protocol errors in this - load balancer. -- ``connectFailure`` – Number of connection failures in this load - balancer. -- ``dataTimedOut`` – Connections closed by this load balancer because - the 'timeout' interval was exceeded. -- ``keepAliveTimedOut`` – Connections closed by this load balancer - because the 'keepalive\_timeout' interval was exceeded. -- ``maxConn`` – Maximum number of simultaneous TCP connections this - load balancer has processed at any one time. - -.. code:: php - - /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ - - $stats = $loadBalancer->stats(); - -Usage Reports -------------- - -The load balancer usage reports provide a view of all transfer activity, -average number of connections, and number of virtual IPs associated with -the load balancing service. Current usage represents all usage recorded -within the preceding 24 hours. Values for both incomingTransfer and -outgoingTransfer are expressed in bytes transferred. - -The optional startTime and endTime parameters can be used to filter all -usage. If the startTime parameter is supplied but the endTime parameter -is not, then all usage beginning with the startTime will be provided. -Likewise, if the endTime parameter is supplied but the startTime -parameter is not, then all usage will be returned up to the endTime -specified. - -.. code:: php - - # View billable LBs - $billable = $service->billableLoadBalancerList(); - - foreach ($billable as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - - # View usage - /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ - $usage = $loadBalancer->usage(); - - echo $usage->averageNumConnections, PHP_EOL; - } - diff --git a/doc/services/load-balancer/access.rst b/doc/services/load-balancer/access.rst new file mode 100644 index 000000000..4387dd8de --- /dev/null +++ b/doc/services/load-balancer/access.rst @@ -0,0 +1,79 @@ +Allowed Domains +=============== + +List Allowed Domains +-------------------- + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code-block:: php + + $allowedDomains = $service->allowedDomainList(); + + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + + +Access Lists +============ + +Access Lists allow fine-grained network access to a load balancer's VIP. Using +access lists, network traffic to a load balancer's VIP can be allowed or denied +from a single IP address, multiple IP addresses or entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` network +items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +.. include:: lb-setup.sample.rst + + +View Access List +---------------- + +You can view a load balancer's access list: + +.. code-block:: php + + $accessList = $loadBalancer->accessList(); + + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + + +Add Network Items To Access List +-------------------------------- + +You can add network items to a load balancer's access list very easily: + +.. code-block:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + + +Remove Network Item From Access List +------------------------------------ + +You an remove a network item from a load balancer's access list: + +.. code-block:: php + + $networkItem->delete(); diff --git a/doc/services/load-balancer/caching.rst b/doc/services/load-balancer/caching.rst new file mode 100644 index 000000000..6678e048a --- /dev/null +++ b/doc/services/load-balancer/caching.rst @@ -0,0 +1,35 @@ +Content Caching +=============== + +When content caching is enabled on a load balancer, recently-accessed files are +stored on the load balancer for easy retrieval by web clients. Requests to the +load balancer for these files are serviced by the load balancer itself, which +reduces load off its back-end nodes and improves response times as well. + + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $contentCaching = $loadBalancer->hasContentCaching(); + + +Enable Content Caching +---------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(true); + + +Disable Content Caching +----------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(false); diff --git a/doc/services/load-balancer/errors.rst b/doc/services/load-balancer/errors.rst new file mode 100644 index 000000000..041c19d45 --- /dev/null +++ b/doc/services/load-balancer/errors.rst @@ -0,0 +1,44 @@ +Error Pages +=========== + +.. include:: lb-setup.sample.rst + +An error page is the html file that is shown to the end user when an error in +the service has been thrown. By default every virtual server is provided with +the default error file. It is also possible to set a custom error page for a +load balancer. + + +View Error Page Content +----------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + + +Set Custom Error Page +--------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + + +Delete Custom Error Page +------------------------ + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index e69de29bb..bf2b6dd0c 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -0,0 +1,93 @@ +Load Balancer v1 +================ + +.. note:: + + This service is only available for Rackspace users. + + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Load Balancer service: + +.. code-block:: php + + $service = $client->loadBalancerService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + load-balancers + nodes + virtual-ips + access + caching + errors + logging + monitors + metadata + sessions + ssl + stats + + +Glossary +-------- + + allowed domain + Allowed domains are a restricted set of domain names that are allowed to add + load balancer nodes. + + content caching + When content caching is enabled on a load balancer, recently-accessed files + are stored on the load balancer for easy retrieval by web clients. Requests to + the load balancer for these files are serviced by the load balancer itself, + which reduces load off its back-end nodes and improves response times as well. + + health monitor + The load balancing service includes a health monitoring operation which + periodically checks your back-end nodes to ensure they are responding + correctly. If a node is not responding, it is removed from rotation until the + health monitor determines that the node is functional. In addition to being + performed periodically, the health check also is performed against every node + that is added to ensure that the node is operating properly before allowing it + to service traffic. Only one health monitor is allowed to be enabled on a load + balancer at a time. + + load balancer + A load balancer is a device that distributes incoming network + traffic amongst multiple back-end systems. These back-end systems are + called the nodes of the load balancer. + + metadata + Metadata can be associated with each load balancer and each node for the + client's personal use. It is defined using key-value pairs where the key + and value consist of alphanumeric characters. A key is unique per load + balancer. + + node + A node is a backend device that provides a service on specified IP and port. + An example of a load balancer node might be a web server serving HTTP + traffic on port 8080. A load balancer typically has multiple nodes attached + to it so it can distribute incoming network traffic amongst them. + + session persistence + Session persistence is a feature of the load balancing service that forces + multiple requests, of the same protocol, from clients to be directed to the + same node. This is common with many web applications that do not inherently + share application state between back-end servers. + + virtual IP + A virtual IP (VIP) makes a load balancer accessible by clients. The + load balancing service supports either a public VIP address + (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP + address (``SERVICENET``), routable only within the region in which the + load balancer resides. diff --git a/doc/services/load-balancer/lb-setup.sample.rst b/doc/services/load-balancer/lb-setup.sample.rst new file mode 100644 index 000000000..96e228b8d --- /dev/null +++ b/doc/services/load-balancer/lb-setup.sample.rst @@ -0,0 +1,9 @@ +Setup +----- + +In order to interact with this feature you must first retrieve a particular +load balancer, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer('{id}'); diff --git a/doc/services/load-balancer/load-balancer.rst b/doc/services/load-balancer/load-balancer.rst new file mode 100644 index 000000000..7071794df --- /dev/null +++ b/doc/services/load-balancer/load-balancer.rst @@ -0,0 +1,165 @@ +Load Balancer +============= + +.. note:: + + Many of the examples in this document use two cloud servers as nodes for + the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer + to these two cloud servers. + + +Create Load Balancer +-------------------- + +The first step is to instantiate an empty object, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer(); + +In essence, all a load balancer does is evenly distribute traffic between +various back-end nodes - which can be Compute or Database instances. So we will +need to add a few when creating our load balancer: + +.. code-block:: php + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + +All that remains is apply final configuration touches, such as name and the +port number, before submitting to the API: + +.. code-block:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode), + 'algorithm' => 'ROUND_ROBIN', + )); + +For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ +please see the sections below. + + +List Load Balancer Details +-------------------------- + +You can retrieve a single load balancer's details by using its ID: + +.. code-block:: php + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + $loadBalancer = $service->loadBalancer('{loadBalancerId}'); + + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers: + +.. code-block:: php + + $loadBalancers = $service->loadBalancerList(); + + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + + +Update a Load Balancer +---------------------- + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +.. code-block:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it: + +.. code-block:: php + + $loadBalancer->delete(); + + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. Common protocols are ``HTTP``, ``HTTPS`` +and ``MYSQL``. A full list is available `here `_. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols like so: + +.. code-block:: php + + $protocols = $service->protocolList(); + + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. A full list is available `here +`_. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can programmatically list all supported load balancing algorithms: + +.. code-block:: php + + $algorithms = $service->algorithmList(); + + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } diff --git a/doc/services/load-balancer/logging.rst b/doc/services/load-balancer/logging.rst new file mode 100644 index 000000000..29c37aee2 --- /dev/null +++ b/doc/services/load-balancer/logging.rst @@ -0,0 +1,33 @@ +Connection Logging +================== + +The connection logging feature allows logs to be delivered to a Cloud Files +account every hour. For HTTP-based protocol traffic, these are Apache-style +access logs. For all other traffic, this is connection and transfer logging. + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $connectionLogging = $loadBalancer->hasConnectionLogging(); + + +Enable Connection Logging +------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(true); + + +Disable Connection Logging +-------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(false); diff --git a/doc/services/load-balancer/metadata.rst b/doc/services/load-balancer/metadata.rst new file mode 100644 index 000000000..2b38de129 --- /dev/null +++ b/doc/services/load-balancer/metadata.rst @@ -0,0 +1,46 @@ +Metadata +======== + +.. include:: lb-setup.sample.rst + +List metadata +------------- + +.. code-block:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + + +Add metadata +------------ + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + + +Modify metadata +--------------- + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + + +Remove metadata +--------------- + +.. code-block:: php + + $metadataItem->delete(); diff --git a/doc/services/load-balancer/monitors.rst b/doc/services/load-balancer/monitors.rst new file mode 100644 index 000000000..9c463f270 --- /dev/null +++ b/doc/services/load-balancer/monitors.rst @@ -0,0 +1,43 @@ +Health Monitors +=============== + +.. include:: lb-setup.sample.rst + +Retrieve monitor details +------------------------ + +.. code-block:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Update monitor +-------------- + +.. code-block:: php + + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + +Delete monitor +-------------- + +.. code-block:: php + + $healthMonitor->delete(); diff --git a/doc/services/load-balancer/nodes.rst b/doc/services/load-balancer/nodes.rst new file mode 100644 index 000000000..dfaadc404 --- /dev/null +++ b/doc/services/load-balancer/nodes.rst @@ -0,0 +1,124 @@ +Nodes +===== + +.. include:: lb-setup.sample.rst + +List Nodes +---------- + +You can list the nodes attached to a load balancer: + +.. code-block:: php + + $nodes = $loadBalancer->nodeList(); + + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + + +Add Nodes +--------- + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code-block:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + + +The signature for ``addNodes`` is as follows: + +.. function:: addNodes($address, $port[, $condition = 'ENABLED'[, $type = null[, $weight = null]]]) + + Add a node to a load balancer + + :param string $address: the IP address of the node + :param integer $port: the port number of the node + :param string $condition: the initial condition of the code. Defaults to ``ENABLED`` + :param string $type: either ``PRIMARY`` or ``SECONDARY`` + :param integer $weight: the node weight (for round-robin algorithm) + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +Modify Nodes +------------ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + + +Remove Nodes +------------ + +There are two ways to remove a node. The first way is on an +``OpenCloud\LoadBalancer\Resource\Node`` instance, like so: + + +.. code-block:: php + + $node->delete(); + +The second is with an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` +instance and the node's ID, like so: + +.. code-block:: php + + $loadBalancer->removeNode('{nodeId}'); + +where '{nodeId}' is the integer ID of the node itself - this is a required value. + + +View Node Service Events +------------------------ + +You can view events associated with the activity between a node and a +load balancer: + +.. code-block:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } diff --git a/doc/services/load-balancer/sessions.rst b/doc/services/load-balancer/sessions.rst new file mode 100644 index 000000000..3d7f71cfe --- /dev/null +++ b/doc/services/load-balancer/sessions.rst @@ -0,0 +1,53 @@ +Session Persistence +=================== + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. include:: lb-setup.sample.rst + + +List Session Persistence Configuration +-------------------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + $sessionPersistenceType = $sessionPersistence->persistenceType; + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + + +Enable Session Persistence +-------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + + +Disable Session Persistence +--------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); diff --git a/doc/services/load-balancer/ssl.rst b/doc/services/load-balancer/ssl.rst new file mode 100644 index 000000000..12b28a6af --- /dev/null +++ b/doc/services/load-balancer/ssl.rst @@ -0,0 +1,52 @@ +SSL Termination +=============== + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +.. include:: lb-setup.sample.rst + + +View configuration +------------------ + +.. code-block:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + $sslConfig = $loadBalancer->SSLTermination(); + + +Update configuration +-------------------- + +.. code-block:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Delete configuration +-------------------- + +.. code-block:: php + + $sslConfig->delete(); diff --git a/doc/services/load-balancer/stats.rst b/doc/services/load-balancer/stats.rst new file mode 100644 index 000000000..65077643f --- /dev/null +++ b/doc/services/load-balancer/stats.rst @@ -0,0 +1,59 @@ +Statistics and Usage Reports +============================ + +.. include:: lb-setup.sample.rst + +Retrieve LB stats +----------------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code-block:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + $stats = $loadBalancer->stats(); + + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code-block:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } diff --git a/doc/services/load-balancer/virtual-ips.rst b/doc/services/load-balancer/virtual-ips.rst new file mode 100644 index 000000000..fa0add613 --- /dev/null +++ b/doc/services/load-balancer/virtual-ips.rst @@ -0,0 +1,74 @@ +Virtual IPs +=========== + +.. include:: lb-setup.sample.rst + + +List Virtual IPs +---------------- + +You can list the VIPs associated with a load balancer like so: + +.. code-block:: php + + $vips = $loadBalancer->virtualIpList(); + + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + + +Get existing VIP +---------------- + +To retrieve the details of an existing VIP on a load balancer, you will need +its ID: + +.. code-block:: + + $vip = $loadBalancer->virtualIp('{virtualIpId}'); + + +Add Virtual IPv6 +---------------- + +You can add additional IPv6 VIPs to a load balancer using the following method: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +the first argument is the type of network your IP address will server traffic in +- and can either be ``PUBLIC`` or ``PRIVATE``. The second argument is the version +of IP address, either ``4`` or ``6``. + + +Add Virtual IPv4 +---------------- + +Similar to above: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 4); + + +Remove Virtual IP +----------------- + +You can remove a VIP from a load balancer. + +.. code-block:: php + + $vip->remove(); + + +.. note:: + + A load balancer must have at least one VIP associated with it. If you try to + remove a load balancer's last VIP, a ``ClientErrorResponseException`` will be + thrown. From c2683a363fa642f61f5912f509b3537713a28f57 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:32 +0100 Subject: [PATCH 432/835] Add monitoring docs --- doc/services/monitoring/Agents.md.rst | 202 -------------- doc/services/monitoring/Entities.md.rst | 77 ------ doc/services/monitoring/Service.md.rst | 23 -- doc/services/monitoring/agents.rst | 188 +++++++++++++ .../monitoring/{Alarms.md.rst => alarms.rst} | 84 +++--- .../{Changelogs.md.rst => changelogs.rst} | 17 +- .../monitoring/{Checks.md.rst => checks.rst} | 189 +++++++------ doc/services/monitoring/entities.rst | 77 ++++++ doc/services/monitoring/index.rst | 34 +++ .../{Metrics.md.rst => metrics.rst} | 54 ++-- ...Notifications.md.rst => notifications.rst} | 252 ++++++++++-------- .../monitoring/{Views.md.rst => views.rst} | 20 +- .../monitoring/{Zones.md.rst => zones.rst} | 42 ++- 13 files changed, 661 insertions(+), 598 deletions(-) delete mode 100644 doc/services/monitoring/Agents.md.rst delete mode 100644 doc/services/monitoring/Entities.md.rst delete mode 100644 doc/services/monitoring/Service.md.rst create mode 100644 doc/services/monitoring/agents.rst rename doc/services/monitoring/{Alarms.md.rst => alarms.rst} (66%) rename doc/services/monitoring/{Changelogs.md.rst => changelogs.rst} (52%) rename doc/services/monitoring/{Checks.md.rst => checks.rst} (80%) create mode 100644 doc/services/monitoring/entities.rst rename doc/services/monitoring/{Metrics.md.rst => metrics.rst} (83%) rename doc/services/monitoring/{Notifications.md.rst => notifications.rst} (64%) rename doc/services/monitoring/{Views.md.rst => views.rst} (58%) rename doc/services/monitoring/{Zones.md.rst => zones.rst} (71%) diff --git a/doc/services/monitoring/Agents.md.rst b/doc/services/monitoring/Agents.md.rst deleted file mode 100644 index 943983f7e..000000000 --- a/doc/services/monitoring/Agents.md.rst +++ /dev/null @@ -1,202 +0,0 @@ -Agents -====== - -Intro ------ - -The Monitoring Agent resides on the host server being monitored. The -agent allows you to gather on-host metrics based on agent checks and -push them to Cloud Monitoring where you can analyze them, use them with -the Cloud Monitoring infrastructure (such as alarms), and archive them. - -For more information about this feature, including a brief overview of -its core design principles and security layers, see the `official API -documentation `__. - -Setup ------ - -.. code:: php - - $agentId = '00-agent.example.com'; - $agent = $service->getAgent($agentId); - -You can view the `service page `__ for more information -about setting up the Cloud Monitoring service. - -List agents ------------ - -.. code:: php - - $agents = $service->getAgents(); - - foreach ($agents as $agent) { - echo $agent->getLastConnected(); - } - -Please consult the `iterator doc `__ for -more information about iterators. - -List connections ----------------- - -.. code:: php - - $connections = $agent->getConnections(); - -Please consult the `iterator doc `__ for -more information about iterators. - -Get connection --------------- - -.. code:: php - - /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ - $connection = $agent->getConnection('cntl4qsIbA'); - -Agent Connection properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------------+---------------+--------+---------------------------+ -| Name | Description | Type | Method | -+====================+===============+========+===========================+ -| id | - | - | ``getId()`` | -+--------------------+---------------+--------+---------------------------+ -| guid | - | - | ``getGuid()`` | -+--------------------+---------------+--------+---------------------------+ -| agent\_id | - | - | ``getAgentId()`` | -+--------------------+---------------+--------+---------------------------+ -| endpoint | - | - | ``getEndpoint()`` | -+--------------------+---------------+--------+---------------------------+ -| process\_version | - | - | ``getProcessVersion()`` | -+--------------------+---------------+--------+---------------------------+ -| bundle\_version | - | - | ``getBundleVersion()`` | -+--------------------+---------------+--------+---------------------------+ -| agent\_ip | - | - | ``getAgentIp()`` | -+--------------------+---------------+--------+---------------------------+ - -Agent tokens -============ - -Intro ------ - -Agent tokens are used to authenticate Monitoring agents to the -Monitoring Service. Multiple agents can share a single token. - -Setup ------ - -.. code:: php - - $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; - $agentToken = $service->getAgentToken($tokenId); - -Create agent token ------------------- - -.. code:: php - - $newToken = $service->getAgentToken(); - $newToken->create(array('label' => 'Foobar')); - -List agent tokens ------------------ - -.. code:: php - - $agentTokens = $service->getAgentTokens(); - - foreach ($agentTokens as $token) { - echo $token->getLabel(); - } - -Please consult the `iterator doc `__ for -more information about iterators. - -Update and delete Agent Token ------------------------------ - -.. code:: php - - // Update - $token->update(array( - 'label' => 'New label' - )); - - // Delete - $token->delete(); - -Agent Host Information -====================== - -Info ----- - -An agent can gather host information, such as process lists, network -configuration, and memory usage, on demand. You can use the -host-information API requests to gather this information for use in -dashboards or other utilities. - -Setup ------ - -.. code:: php - - $host = $monitoringService->getAgentHost(); - -Get some metrics ----------------- - -.. code:: php - - $cpuInfo = $host->info('cpus'); - $diskInfo = $host->info('disks'); - $filesystemInfo = $host->info('filesystems'); - $memoryInfo = $host->info('memory'); - $networkIntInfo = $host->info('network_interfaces'); - $processesInfo = $host->info('processes'); - $systemInfo = $host->info('system'); - $userInfo = $host->info('who'); - - // What CPU models do we have? - foreach ($cpuInfo as $cpuMetric) { - echo $cpuMetric->model, PHP_EOL; - } - - // How many disks do we have? - echo $diskInfo->count(); - - // What's the available space on our ext4 filesystem? - foreach ($filesystemInfo as $filesystemMetric) { - if ($filesystemMetric->sys_type_name == 'ext4') { - echo $filesystemMetric->avail; - } - } - -Agent targets -============= - -Info ----- - -Each agent check type gathers data for a related set of target devices -on the server where the agent is installed. For example, -``agent.network`` gathers data for network devices. The actual list of -target devices is specific to the configuration of the host server. By -focusing on specific targets, you can efficiently narrow the metric data -that the agent gathers. - -List agent targets -~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $targets = $service->getAgentTargets(); - - foreach ($targets as $target) { - echo $target->getType(); - } - diff --git a/doc/services/monitoring/Entities.md.rst b/doc/services/monitoring/Entities.md.rst deleted file mode 100644 index 068aec122..000000000 --- a/doc/services/monitoring/Entities.md.rst +++ /dev/null @@ -1,77 +0,0 @@ - Entities -========= - -Info ----- - -An entity is the target of what you are monitoring. For example, you can -create an entity to monitor your website, a particular web service, or -your Rackspace server. Note that an entity represents only one item in -the monitoring system -- if you wanted to monitor each server in a -cluster, you would create an entity for each of the servers. You would -not create a single entity to represent the entire cluster. - -An entity can have multiple checks associated with it. This allows you -to check multiple services on the same host by creating multiple checks -on the same entity, instead of multiple entities each with a single -check. - -Setup ------ - -.. code:: php - - $entity = $service->getEntity(); - -For more information about setting up the ``$service`` object, please -see the userguide tutorial for `services `__. - -Attributes ----------- - -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| Name | Description | Required? | Data type | Method | -+=================+=========================================================================+=============+=====================================================+========================+ -| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ - -Create Entity -------------- - -.. code:: php - - $service->createEntity(array( - 'label' => 'Brand New Entity', - 'ip_addresses' => array( - 'default' => '127.0.0.4', - 'b' => '127.0.0.5', - 'c' => '127.0.0.6', - 'test' => '127.0.0.7' - ), - 'metadata' => array( - 'all' => 'kinds', - 'of' => 'stuff', - 'can' => 'go', - 'here' => 'null is not a valid value' - ) - )); - -Update and delete Entity ------------------------- - -.. code:: php - - // Update - $entity->update(array( - 'label' => 'New label for my entity' - )); - - // Delete - $entity->delete(); - diff --git a/doc/services/monitoring/Service.md.rst b/doc/services/monitoring/Service.md.rst deleted file mode 100644 index 09c25c21d..000000000 --- a/doc/services/monitoring/Service.md.rst +++ /dev/null @@ -1,23 +0,0 @@ -Cloud Monitoring Service -======================== - -Initializing the Cloud Monitoring is easy - and can be done in a similar -way to all other Rackspace services: - -1. Create client and pass in auth details. For more information about - creating clients, please consult the `Client - documentation <../Clients.md>`__. -2. Use the factory method, specifying additional parameters where - necessary: - -.. code:: php - - $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); - -All three parameters are optional - if not specified, it will revert to -the service's default values which are: - -- Name = ``cloudMonitoring`` -- Region = ``DFW`` -- URL type = ``publicURL`` - diff --git a/doc/services/monitoring/agents.rst b/doc/services/monitoring/agents.rst new file mode 100644 index 000000000..f4c673608 --- /dev/null +++ b/doc/services/monitoring/agents.rst @@ -0,0 +1,188 @@ +Agents +====== + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Retrieve details about an agent +------------------------------- + +.. code-block:: php + + $agent = $service->getAgent('{agentId}'); + + +List agents +----------- + +.. code-block:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + + +List connections +---------------- + +.. code-block:: php + + $connections = $agent->getConnections(); + + +Get connection +-------------- + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('{connectionId}'); + + +Once you have access to an agent's ``OpenCloud\CloudMonitoring\Resource\AgentConnection`` +object, these are the attributes you can access: + ++--------------------+---------------------------+ +| Name | Method | ++====================+===========================+ +| id | ``getId()`` | ++--------------------+---------------------------+ +| guid | ``getGuid()`` | ++--------------------+---------------------------+ +| agent_id | ``getAgentId()`` | ++--------------------+---------------------------+ +| endpoint | ``getEndpoint()`` | ++--------------------+---------------------------+ +| process_version | ``getProcessVersion()`` | ++--------------------+---------------------------+ +| bundle_version | ``getBundleVersion()`` | ++--------------------+---------------------------+ +| agent_ip | ``getAgentIp()`` | ++--------------------+---------------------------+ + +Agent tokens +============ + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Retrieve an agent token +----------------------- + +.. code-block:: php + + $agentToken = $service->getAgentToken('{tokenId}'); + + +Create agent token +------------------ + +.. code-block:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + + +List agent tokens +----------------- + +.. code-block:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + + +Update agent token +------------------ + +.. code-block:: php + + $token->update(array( + 'label' => 'New label' + )); + + +Update agent token +------------------ + +.. code-block:: php + + $token->delete(); + + +Agent Host Information +====================== + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code-block:: php + + $host = $service->getAgentHost(); + + +Get some metrics +---------------- + +.. code-block:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +------------------ + +.. code-block:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } diff --git a/doc/services/monitoring/Alarms.md.rst b/doc/services/monitoring/alarms.rst similarity index 66% rename from doc/services/monitoring/Alarms.md.rst rename to doc/services/monitoring/alarms.rst index 2918637e9..297a57407 100644 --- a/doc/services/monitoring/Alarms.md.rst +++ b/doc/services/monitoring/alarms.rst @@ -1,9 +1,6 @@ Alarms ====== -Info ----- - Alarms bind alerting rules, entities, and notification plans into a logical unit. Alarms are responsible for determining a state (``OK``, ``WARNING`` or ``CRITICAL``) based on the result of a Check, and @@ -15,25 +12,39 @@ documentation getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + +Retrieve alarm +-------------- -.. code:: php +.. code-block:: php - $alarmId = 'alAAAA'; - $alarm = $check->getAlarm(); + $alarm = $check->getAlarm('{alarmId}'); -For more information about working with Checks, please see the -`appropriate documentation `__. -Attributes ----------- +Once you have access to a ``OpenCloud\Monitoring\Resource\Alarm`` object, these +are the attributes you can access: +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ | Name | Description | Required? | Method | +==========================+=============================================================================+=============+=================================+ -| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | +| check_id | The ID of the check to alert on. | Required | ``getCheckId()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ -| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | +| notification_plan_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ | criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ @@ -44,38 +55,45 @@ Attributes | metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + Create Alarm ------------ -.. code:: php +.. code-block:: php + + $alarm = $check->getAlarm(); + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); - $alarm->create(array( - 'check_id' => 'chAAAA', - 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', - 'notification_plan_id' => 'npAAAAA' - )); List Alarms ----------- -.. code:: php +.. code-block:: php - $alarms = $entity->getAlarms(); + $alarms = $entity->getAlarms(); - foreach ($alarms as $alarm) { - echo $alarm->getId(); - } + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } -Update and delete Alarm ------------------------ -.. code:: php +Update Alarm +------------ + +.. code-block:: php - // Update - $alarm->update(array( - 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' - )); + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + +Delete alarm +------------ - // Delete - $alarm->delete(); +.. code-block:: php + $alarm->delete(); diff --git a/doc/services/monitoring/Changelogs.md.rst b/doc/services/monitoring/changelogs.rst similarity index 52% rename from doc/services/monitoring/Changelogs.md.rst rename to doc/services/monitoring/changelogs.rst index 2e9059e26..a6b26af25 100644 --- a/doc/services/monitoring/Changelogs.md.rst +++ b/doc/services/monitoring/changelogs.rst @@ -1,21 +1,18 @@ Changelogs ========== -Info ----- - The monitoring service records changelogs for alarm statuses. Changelogs are accessible as a Time Series Collection. By default the API queries the last 7 days of changelog information. -Working with Changelogs ------------------------ -.. code:: php +View Changelog +-------------- - $changelog = $service->getChangelog(); +.. code-block:: php - foreach ($changelog as $item) { - $entity = $item->getEntityId(); - } + $changelog = $service->getChangelog(); + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } diff --git a/doc/services/monitoring/Checks.md.rst b/doc/services/monitoring/checks.rst similarity index 80% rename from doc/services/monitoring/Checks.md.rst rename to doc/services/monitoring/checks.rst index 0e8e902a5..73474c170 100644 --- a/doc/services/monitoring/Checks.md.rst +++ b/doc/services/monitoring/checks.rst @@ -1,8 +1,6 @@ Checks ====== -Info ----- A check is one of the foundational building blocks of the monitoring system. The check determines the parts or pieces of the entity that you @@ -23,18 +21,31 @@ servers stops responding) or multiple alarms off of a single check. (e.g. ensure both that a HTTPS server is responding and that it has a valid certificate). -Setup ------ +Create a check +-------------- + +There are various attributes available to you when creating a new monitoring +check: -Checks are sub-resources of Entities: +.. code-block:: php -.. code:: php + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); - $checkId = 'chAAAA'; - $check = $entity->getCheck($checkId); +For a full list of available attributes, consult the list below. Attributes ----------- +~~~~~~~~~~ +------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ | Name | Description | Required? | Data type | @@ -54,92 +65,90 @@ Attributes | timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | +------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ -Attributes used for remote Checks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Optional attributes to be used with remote checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ | Name | Description | Required? | Data type | +===========================+========================================================================================================================================================+=============+============================================================+ -| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | +| monitoring_zones_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | +| target_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | +| target_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | +| target_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -Test parameters (before create) -------------------------------- - -.. code:: php - - $params = array( - 'type' => 'remote.http', - 'details' => array( - 'url' => 'http://example.com', - 'method' => 'GET' - ), - 'monitoring_zones_poll' => array('mzlon'), - 'period' => '100', - 'timeout' => '30', - 'target_alias' => 'default', - 'label' => 'Website check 1' - ); - - // You can do a test to see what would happen - // if a Check is launched with these params - $response = $entity->testNewCheckParams($params); - - echo $response->timestamp; // When was it executed? - echo $response->available; // Was it available? - echo $response->status; // Status code - -Create a Check --------------- -.. code:: php +Test parameters +--------------- + +Sometimes it can be useful to test out the parameters before sending them as a +create call. To do this, pass in the ``$params`` like so: + +.. code-block:: php + + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + + +Send parameters +~~~~~~~~~~~~~~~ + +Once you are satisfied with your configuration parameters, you can complete the +operation and send it to the API like so: + +.. code-block:: php + + $entity->createCheck($params); - $entity->createCheck($params); Test existing Check ------------------- -.. code:: php +.. code-block:: php - // Set arg to TRUE for debug information - $response = $check->test(true); + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; - echo $response->debug_info; List Checks ----------- -.. code:: php +.. code-block:: php + + $checks = $entity->getChecks(); - $checks = $entity->getChecks(); + foreach ($checks as $check) { + echo $check->getId(); + } - foreach ($checks as $check) { - echo $check->getId(); - } -Update and delete Check -~~~~~~~~~~~~~~~~~~~~~~~ +Update Check +------------ -.. code:: php +.. code-block:: php - // Update - $check->update(array('period' => 500)); + $check->update(array('period' => 500)); + + +Delete check +------------ + +.. code-block:: php + + $check->delete(); - // Delete - $check->delete(); Check types =========== -Info ----- - Each check within the Rackspace Cloud Monitoring has a designated check type. The check type instructs the monitoring system how to check the monitored resource. **Note:** Users cannot create, update or delete @@ -168,26 +177,46 @@ using the 'extract' attribute on the remote.http check, however the default metrics will always be present. To determine the exact metrics available, the Test Check API is provided. -Setup ------ +Find an existing check's type +----------------------------- + +If you want to see the type for an existing Check resource: + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + + +List all possible check types +----------------------------- + +.. code-block:: php + + $checkTypes = $service->getCheckTypes(); -If you want to see the type for an existing Check: + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } -.. code:: php - /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ - $checkType = $check->getCheckType(); +Retrieve details about a Type by its ID +--------------------------------------- Alternatively, you can retrieve a specific type based on its ID: -.. code:: php +.. code-block:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); - $checkTypeId = 'remote.dns'; - $checkType = $service->getCheckType($checkTypeId); Attributes ---------- +Once you have access to a ``OpenCloud\CloudMonitoring\Resource\CheckType`` object, +you can query these attributes: + +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ | Name | Description | Data type | Method | +========================+==========================================================================================================================================================================================+=============+===============================+ @@ -195,17 +224,5 @@ Attributes +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ | fields | Check type fields. | Array | ``getFields()`` | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ -| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | +| supported_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ - -List all possible check types ------------------------------ - -.. code:: php - - $checkTypes = $service->getCheckTypes(); - - foreach ($checkTypes as $checkType) { - echo $checkType->getId(); - } - diff --git a/doc/services/monitoring/entities.rst b/doc/services/monitoring/entities.rst new file mode 100644 index 000000000..7e4132af1 --- /dev/null +++ b/doc/services/monitoring/entities.rst @@ -0,0 +1,77 @@ + Entities +========= + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Create Entity +------------- + +.. code-block:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + + +Retrive an entity +----------------- + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + + +Attributes +~~~~~~~~~~ + ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+=========================================================================+=====================================================+========================+ +| label | Defines a name for the entity. | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| agent_id | Agent to which this entity is bound to. | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| ip_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ + + +Update an entity +---------------- + +.. code-block:: php + + $entity->update(array( + 'label' => 'New label for my entity' + )); + + +Delete entity +------------- + +.. code-block:: php + + $entity->delete(); diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index e69de29bb..df0597e8c 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -0,0 +1,34 @@ +Monitoring v1 +============= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Cloud Monitoring service: + +.. code-block:: php + + $service = $client->monitoringService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + +Operations +---------- + +.. toctree:: + + entities + checks + alarms + agents + changelogs + metrics + notifications + views + zones + + +Glossary +-------- diff --git a/doc/services/monitoring/Metrics.md.rst b/doc/services/monitoring/metrics.rst similarity index 83% rename from doc/services/monitoring/Metrics.md.rst rename to doc/services/monitoring/metrics.rst index 4231cf914..0b412a6a3 100644 --- a/doc/services/monitoring/Metrics.md.rst +++ b/doc/services/monitoring/metrics.rst @@ -1,9 +1,6 @@  Metrics ======== -Info ----- - When Monitoring checks run, they generate metrics. These metrics are stored as full resolution data points in the Cloud Monitoring system. Full resolution data points are periodically rolled up (condensed) into @@ -13,6 +10,7 @@ Depending on your needs, you can use the metrics API to fetch individual data points (fine-grained) or rolled up data points (coarse-grained) over a period of time. + Data Granularity ---------------- @@ -42,7 +40,7 @@ elapsed since January 1, 1970** ). The following table shows the number of points that the API returns for a given resolution. Specifying resolution to retrieve data in 48 hour period -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-----------------------------+-------------------------+ | You specify resolution... | API returns points... | @@ -61,7 +59,7 @@ Specifying resolution to retrieve data in 48 hour period +-----------------------------+-------------------------+ Specifying number of points to retrieve data in 48 hour period -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------------+-----------------------------+ | You specify points in the range... | API calculates resolution | @@ -80,7 +78,7 @@ Specifying number of points to retrieve data in 48 hour period +--------------------------------------+-----------------------------+ Data Point Expiration -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ Cloud Monitoring expires data points according to the following schedule: @@ -101,31 +99,45 @@ schedule: | MIN1440 | 365 days | +--------------+--------------+ - Setup +Setup ------ -Metrics are sub-resources of Checks. For more information about working -with Checks, please see the `relevant documentation `__. +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + List all metrics ---------------- -.. code:: php +.. code-block:: php - $metrics = $check->getMetrics(); + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } - foreach ($metrics as $metric) { - echo $metric->getName(); - } Fetch data points ----------------- -.. code:: php - - $data = $check->fetchDataPoints('mzdfw.available', array( - 'resolution' => 'FULL', - 'from' => 1369756378450, - 'to' => 1369760279018 - )); +.. code-block:: php + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); diff --git a/doc/services/monitoring/Notifications.md.rst b/doc/services/monitoring/notifications.rst similarity index 64% rename from doc/services/monitoring/Notifications.md.rst rename to doc/services/monitoring/notifications.rst index f361e3c9c..d7e289ad5 100644 --- a/doc/services/monitoring/Notifications.md.rst +++ b/doc/services/monitoring/notifications.rst @@ -1,9 +1,6 @@ Notifications ============= -Info ----- - A notification is a destination to send an alarm; it can be a variety of different types, and will evolve over time. @@ -11,16 +8,15 @@ For instance, with a webhook type notification, Cloud Monitoring posts JSON formatted data to a user-specified URL on an alert condition (Check goes from ``OK`` -> ``CRITICAL`` and so on). -Setup ------ +Get notification +---------------- -.. code:: php +.. code-block:: php - $id = 'ntAAAA'; - $notification = $service->getNotification($id); + $notification = $service->getNotification('{id}'); -Attributes ----------- +Once you have access to a ``OpenCloud\Monitoring\Resource\Notification`` object, +these are the attributes available for use: +-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ | Name | Description | Data type | Method | @@ -32,76 +28,96 @@ Attributes | type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | +-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +Creating notifications +---------------------- + +The first thing to do when creating a new notification is configure the +parameters which will define the behaviour of your resource: + +.. code-block:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + Test parameters ---------------- +~~~~~~~~~~~~~~~ + +Once this is done, it is often useful to test them out to check whether they +will result in a successful creation: -.. code:: php +.. code-block:: php - $params = array( - 'label' => 'My webhook #1', - 'type' => 'webhook', - 'details' => array( - 'url' => 'http://example.com' - ) - ); + // Test it + $response = $notification->testParams($params); - // Test it - $response = $notification->testParams($params); + if ($response->status == 'Success') { + echo $response->message; + } - if ($response->status == 'Success') { - echo $response->message; - } -Create Notification -------------------- +Send parameters +~~~~~~~~~~~~~~~ -.. code:: php +When you're happy with the parameters you've defined, you can complete the +operation by sending them to the API like so: + +.. code-block:: php + + $notification->create($params); - $notification->create($params); Test existing notification -------------------------- -.. code:: php +.. code-block:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; - $response = $notification->testExisting(true); - echo $response->debug_info; List Notifications ------------------ -.. code:: php +.. code-block:: php - $notifications = $service->getNotifications(); + $notifications = $service->getNotifications(); - foreach ($notifications as $notification) { - echo $notification->getId(); - } + foreach ($notifications as $notification) { + echo $notification->getId(); + } -Update and delete Notifications -------------------------------- -.. code:: php +Update a Notification +--------------------- - // Update - $notification->update(array( - 'label' => 'New notification label' - )); +.. code-block:: php + + $notification->update(array( + 'label' => 'New notification label' + )); + + +Delete a Notification +--------------------- + +.. code-block:: php + + $notification->delete(); - // Delete - $notification->delete(); Notification types ================== -Info ----- - -Pretty self-explanatory. Rackspace Cloud Monitoring currently supports -the following notification types: +Rackspace Cloud Monitoring currently supports the following notification types: Webhook -^^^^^^^ +~~~~~~~ Industry-standard web hooks, where JSON is posted to a configurable URL. It has these attributes: @@ -113,7 +129,7 @@ It has these attributes: +-----------+------------------------------------------+---------------+ Email -^^^^^ +~~~~~ Email alerts where the message is delivered to a specified address. It has these attributes: @@ -124,6 +140,7 @@ has these attributes: | url | An HTTP or HTTPS URL to POST to | Valid URL | +--------+-----------------------------------+-------------+ + Setup ----- @@ -131,34 +148,33 @@ If you've already set up a main Notification object, and want to access functionality for this Notification's particular Notification Type, you can access its property: -.. code:: php +.. code-block:: php - $type = $notification->getNotificationType(); + $type = $notification->getNotificationType(); Alternatively, you can retrieve an independent resource using the ID: -.. code:: php +.. code-block:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); - $typeId = 'pagerduty'; - $type = $service->getNotificationType($typeId); List all possible notification types ------------------------------------ -.. code:: php +.. code-block:: php + + $types = $service->getNotificationTypes(); - $types = $service->getNotificationTypes(); + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } - foreach ($types as $type) { - echo sprintf('%s %s', $type->getName(), $type->getDescription()); - } Notification plans ================== -Info ----- - A notification plan contains a set of notification actions that Rackspace Cloud Monitoring executes when triggered by an alarm. Rackspace Cloud Monitoring currently supports webhook and email @@ -180,60 +196,62 @@ A notification plan, ``npTechnicalContactsEmail``, is provided by default which will email all of the technical contacts on file for an account whenever there is a state change. -Setup ------ +Get a notification plan +----------------------- -.. code:: php +.. code-block:: php - $planId = 'npAAAA'; - $plan = $service->getNotificationPlan(); + $plan = $service->getNotificationPlan('{planId}'); -Attributes ----------- +Once you have access to a ``OpenCloud\\Monitoring\\Resource\\NotificationPlan`` +object, you can access these resources: +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ | Name | Description | Required? | Data type | Method | +===================+====================================================================+=============+=========================+==========================+ | label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | +| critical_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | +| ok_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | +| warning_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ Create Notification Plan ------------------------ -.. code:: php +.. code-block:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + + +Update notification plan +------------------------ + +.. code-block:: php - $plan->create(array( - 'label' => 'New Notification Plan', - 'critical_state' => array('ntAAAA'), - 'ok_state' => array('ntBBBB'), - 'warning_state' => array('ntCCCC') - )); + $plan->update(array( + 'label' => 'New label for my plan' + )); -Update and delete Notification Plan ------------------------------------ -.. code:: php +Delete notification plan +------------------------ + +.. code-block:: php - // Update - $plan->update(array( - 'label' => 'New label for my plan' - )); + $plan->delete(); - // Delete - $plan->delete(); Alarm Notification History ========================== -Info ----- - The monitoring service keeps a record of notifications sent for each alarm. This history is further subdivided by the check on which the notification occurred. Every attempt to send a notification is recorded, @@ -245,12 +263,31 @@ Alarm notification history is accessible as a Time Series Collection. By default alarm notification history is stored for 30 days and the API queries the last 7 days of information. - Setup +Setup ------ -Notification History is a sub-resource of an Alarm. For more information -about working with Alarms, please consult the relevant -`documentation `__. +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +and finally, retrieve the alarm: + +.. code-block:: php + + $alarm = $check->getAlarm('{alarmId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + Discover which Checks have a Notification History ------------------------------------------------- @@ -258,24 +295,25 @@ Discover which Checks have a Notification History This operation list checks for which alarm notification history is available: -.. code:: php +.. code-block:: php + + $checks = $alarm->getRecordedChecks(); - $checks = $alarm->getRecordedChecks(); List Alarm Notification History for a particular Check ------------------------------------------------------ -.. code:: php +.. code-block:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); - $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); Get a particular Notification History item ------------------------------------------ -.. code:: php - - $checkId = 'chAAAA'; - $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; +.. code-block:: php - $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); diff --git a/doc/services/monitoring/Views.md.rst b/doc/services/monitoring/views.rst similarity index 58% rename from doc/services/monitoring/Views.md.rst rename to doc/services/monitoring/views.rst index a5b7245fa..e83b97154 100644 --- a/doc/services/monitoring/Views.md.rst +++ b/doc/services/monitoring/views.rst @@ -1,27 +1,21 @@ Views ===== -Info ----- - Views contain a combination of data that usually includes multiple, different objects. The primary purpose of a view is to save API calls and make data retrieval more efficient. Instead of doing multiple API calls and then combining the result yourself, you can perform a single API call against the view endpoint. + List all Views -------------- -\`\`\`php $views = $service->getViews(); - -foreach ($views as $view) { $entity = $view->getEntity(); - -:: - - echo $view->getTimestamp(); +.. code-block:: php -} \`\`\` + $views = $service->getViews(); -Please consult the `iterator doc `__ for -more information about iterators. + foreach ($views as $view) { + $entity = $view->getEntity(); + echo $view->getTimestamp(); + } diff --git a/doc/services/monitoring/Zones.md.rst b/doc/services/monitoring/zones.rst similarity index 71% rename from doc/services/monitoring/Zones.md.rst rename to doc/services/monitoring/zones.rst index ffe4bba1e..bb588a303 100644 --- a/doc/services/monitoring/Zones.md.rst +++ b/doc/services/monitoring/zones.rst @@ -1,9 +1,6 @@ Zones ===== -Info ----- - A monitoring zone is a location that Rackspace Cloud Monitoring collects data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". It is an abstraction for a general location from which data is @@ -18,50 +15,43 @@ addresses or unrelated machines within that IP address range. A check references a list of monitoring zones it should be run from. - Setup ------- - -.. code:: php +Get details about a zone +------------------------ - $zoneId = 'mzAAAAA'; - $zone = $monitoringService->getMonitoringZone($zoneId); +.. code-block:: php -Attributes ----------- + $zone = $monitoringService->getMonitoringZone('{zoneId}'); +-----------------+------------------+-----------------------------------+------------------------+ | Name | Description | Data type | Method | +=================+==================+===================================+========================+ -| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | +| country_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | +-----------------+------------------+-----------------------------------+------------------------+ | label | Label | String | ``getLabel()`` | +-----------------+------------------+-----------------------------------+------------------------+ -| source\_ips | Source IP list | Array | ``getSourceIps()`` | +| source_ips | Source IP list | Array | ``getSourceIps()`` | +-----------------+------------------+-----------------------------------+------------------------+  List all zones --------------- -.. code:: php +.. code-block:: php $zones = $service->getMonitoringZones(); -Please consult the `iterator doc `__ for -more information about iterators. Perform a traceroute -------------------- -.. code:: php - - $traceroute = $zone->traceroute(array( - 'target' => 'http://test.com', - 'target_resolver' => 'IPv4' - )); +.. code-block:: php - // How many hops? - echo count($traceroute); + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); - // What was the first hop's IP? - echo $traceroute[0]->ip; + // How many hops? + echo count($traceroute); + // What was the first hop's IP? + echo $traceroute[0]->ip; From 4e903fef89e7452689503063539f038b6a03f9e9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:41 +0100 Subject: [PATCH 433/835] Networking docs --- doc/services/networking/README.md.rst | 108 ----- doc/services/networking/USERGUIDE.md.rst | 582 ----------------------- doc/services/networking/index.rst | 50 ++ doc/services/networking/networks.rst | 128 +++++ doc/services/networking/ports.rst | 150 ++++++ doc/services/networking/subnets.rst | 152 ++++++ 6 files changed, 480 insertions(+), 690 deletions(-) delete mode 100644 doc/services/networking/README.md.rst delete mode 100644 doc/services/networking/USERGUIDE.md.rst create mode 100644 doc/services/networking/networks.rst create mode 100644 doc/services/networking/ports.rst create mode 100644 doc/services/networking/subnets.rst diff --git a/doc/services/networking/README.md.rst b/doc/services/networking/README.md.rst deleted file mode 100644 index 605805fe1..000000000 --- a/doc/services/networking/README.md.rst +++ /dev/null @@ -1,108 +0,0 @@ -Networking -========== - -**Networking** is a service that you can use to create virtual networks -and attach cloud devices such as servers to these networks. - -Concepts --------- - -Concepts --------- - -To use the Networking service effectively, you should understand the -following key concepts: - -- **Network**: A network is an isolated virtual layer-2 broadcast - domain that is typically reserved for the tenant who created it - unless you configure the network to be shared. The network is the - main entity in the Networking service. Ports and subnets are always - associated with a network. - -- **Subnet**: A subnet represents an IP address block that can be used - to assign IP addresses to virtual instances (such as servers created - using the Compute service). Each subnet must have a CIDR and must be - associated with a network. - -- **Port**: A port represents a virtual switch port on a logical - network switch. Virtual instances (such as servers created using the - Compute service) attach their interfaces into ports. The port also - defines the MAC address and the IP address(es) to be assigned to the - interfaces plugged into them. When IP addresses are associated to a - port, this also implies the port is associated with a subet, as the - IP address is taken from the allocation pool for a specific subnet. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the Networking service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Networking service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All Networking operations are done via an *networking service object*. -To instantiate this object, call the ``networkingService`` method on the -``$client`` object. This method takes two arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+=====================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ - -.. code:: php - - $region = ''; - $networkingService = $client->networkingService(null, $region); - -Any networks, subnets, and ports created with this -``$networkingService`` instance will be stored in the cloud region -specified by ``$region``. - -3. Create a network. -~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -Once you have created a network, there is more you can do with it. See -`complete user guide for networking `__. diff --git a/doc/services/networking/USERGUIDE.md.rst b/doc/services/networking/USERGUIDE.md.rst deleted file mode 100644 index bfeb27774..000000000 --- a/doc/services/networking/USERGUIDE.md.rst +++ /dev/null @@ -1,582 +0,0 @@ -Complete User Guide for the Networking Service -============================================== - -Networking is a service that you can use to create virtual networks and -attach cloud devices such as servers to these networks. - -This user guide introduces you the entities in the Networking service — -networks, subnets, and ports — and shows you how to create and manage -these entities. - -Table of contents ------------------ - -- `Concepts <#concepts>`__ -- `Prerequisites <#prerequisites>`__ - - - `Client <#client>`__ - - `Networking service <#networking-service>`__ - -- `Networks <#networks>`__ - - - `Create a network <#create-a-network>`__ - - `Create multiple networks <#create-multiple-networks>`__ - - `List networks <#list-networks>`__ - - `Get a network <#get-a-network>`__ - - `Update a network <#update-a-network>`__ - - `Delete a network <#delete-a-network>`__ - -- `Subnets <#subnets>`__ - - - `Create a subnet <#create-a-subnet>`__ - - `Create multiple subnets <#create-multiple-subnets>`__ - - `List subnets <#list-subnets>`__ - - `Get a subnet <#get-a-subnet>`__ - - `Update a subnet <#update-a-subnet>`__ - - `Delete a subnet <#delete-a-subnet>`__ - -- `Ports <#ports>`__ - - - `Create a port <#create-a-port>`__ - - `Create multiple ports <#create-multiple-ports>`__ - - `List ports <#list-ports>`__ - - `Get a port <#get-a-port>`__ - - `Update a port <#update-a-port>`__ - - `Delete a port <#delete-a-port>`__ - -Concepts --------- - -To use the Networking service effectively, you should understand the -following key concepts: - -- **Network**: An isolated virtual layer-2 broadcast domain that is - typically reserved for the tenant who created it unless it is - configured to be shared. The network is the main entity in the - Networking service. Ports and subnets are always associated with a - network. - -- **Subnet**: An IP address block that can be used to assign IP - addresses to virtual instances (such as servers created using the - Compute service). Each subnet must have a CIDR and must be associated - with a network. - -- **Port**: A virtual switch port on a logical network switch. Virtual - instances (such as servers created using the Compute service) attach - their interfaces into ports. The port also defines the MAC address - and the IP address or addresses to be assigned to the interfaces - plugged into them. When IP addresses are associated with a port, this - also implies the port is associated with a subnet because the IP - address is taken from the allocation pool for a specific subnet. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the Networking service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate an - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Networking service -~~~~~~~~~~~~~~~~~~ - -All Networking operations are done via a *networking service object*. To -instantiate this object, call the ``networkingService`` method on the -``$client`` object. This method takes the following arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+=====================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ - -.. code:: php - - $region = ''; - $networkingService = $client->networkingService(null, $region); - -Any networks, subnets, and ports created with this -``$networkingService`` instance are stored in the cloud region specified -by ``$region``. - -Networks --------- - -A network is an isolated virtual layer-2 broadcast domain that is -typically reserved for the tenant who created it unless it is configured -to be shared. The network is the main entity in the Networking service. -Ports and subnets are always associated with a network. - -Create a network -~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+====================+===================================================================================================+=============+=============+=======================================+==================================+ -| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ - -You can create a network as shown in the following example: - -.. code:: php - - $network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' - )); - /** @var $network OpenCloud\Networking\Resource\Network **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple networks -~~~~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-network>`__. - -You can create multiple networks as shown in the following example: - -.. code:: php - - $networks = $networkingService->createNetworks(array( - array( - 'name' => 'My private backend network #1' - ), - array( - 'name' => 'My private backend network #2' - ) - )); - - foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List networks -~~~~~~~~~~~~~ - -You can list all the networks to which you have access as shown in the -following example: - -.. code:: php - - $networks = $networkingService->listNetworks(); - foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a network -~~~~~~~~~~~~~ - -You can retrieve a specific network by using that network's ID, as shown -in the following example: - -.. code:: php - - $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); - /** @var $network OpenCloud\Networking\Resource\Network **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a network -~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+====================+===================================================================================================+=============+=============+=================+==========================================+ -| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ - -You can update a network as shown in the following example: - -.. code:: php - - $network->update(array( - 'name' => 'My updated private backend network' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a network -~~~~~~~~~~~~~~~~ - -You can delete a network as shown in the following example: - -.. code:: php - - $network->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Subnets -------- - -A subnet represents an IP address block that can be used to assign IP -addresses to virtual instances (such as servers created using the -Compute service). Each subnet must have a CIDR and must be associated -with a network. - -Create a subnet -~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ -| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ - -You can create a subnet as shown in the following example: - -.. code:: php - - $subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25' - )); - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple subnets -~~~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-subnet>`__. - -You can create multiple subnets as shown in the following example: - -.. code:: php - - $subnets = $networkingService->createSubnets(array( - array( - 'name' => 'My subnet #1' - ), - array( - 'name' => 'My subnet #2' - ) - )); - - foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List subnets -~~~~~~~~~~~~ - -You can list all the subnets to which you have access as shown in the -following example: - -.. code:: php - - $subnets = $networkingService->listSubnets(); - foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a subnet -~~~~~~~~~~~~ - -You can retrieve a specific subnet by using that subnet's ID, as shown -in the following example: - -.. code:: php - - $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a subnet -~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ -| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ - -You can update a subnet as shown in the following example: - -.. code:: php - - $subnet->update(array( - 'name' => 'My updated subnet', - 'hostRoutes' => array( - array( - 'destination' => '1.1.1.0/24', - 'nexthop' => '192.168.17.19' - ) - ), - 'gatewayIp' => '192.168.62.155' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a subnet -~~~~~~~~~~~~~~~ - -You can delete a subnet as shown in the following example: - -.. code:: php - - $subnet->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Ports ------ - -A port represents a virtual switch port on a logical network switch. -Virtual instances (such as servers created using the Compute service) -attach their interfaces into ports. The port also defines the MAC -address and the IP address or addresses to be assigned to the interfaces -plugged into them. When IP addresses are associated with a port, this -also implies the port is associated with a subnet because the IP address -is taken from the allocation pool for a specific subnet. - -Create a port -~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ -| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ - -You can create a port as shown in the following example: - -.. code:: php - - $port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - )); - /** @var $port OpenCloud\Networking\Resource\Port **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple ports -~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-port>`__. - -You can create multiple ports as shown in the following example: - -.. code:: php - - $ports = $networkingService->createPorts(array( - array( - 'name' => 'My port #1', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ), - array( - 'name' => 'My port #2', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ) - )); - - foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List ports -~~~~~~~~~~ - -You can list all the ports to which you have access as shown in the -following example: - -.. code:: php - - $ports = $networkingService->listPorts(); - foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a port -~~~~~~~~~~ - -You can retrieve a specific port by using that port's ID, as shown in -the following example: - -.. code:: php - - $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); - /** @var $port OpenCloud\Networking\Resource\Port **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a port -~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ -| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ - -You can update a port as shown in the following example: - -.. code:: php - - $port->update(array( - 'fixedIps' => array( - array( - 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', - 'ipAddress' => '192.168.199.59' - ) - ) - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a port -~~~~~~~~~~~~~ - -You can delete a port as shown in the following example: - -.. code:: php - - $port->delete(); - -[ `Get the executable PHP script for this -example `__ ] diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index e69de29bb..92270fe86 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -0,0 +1,50 @@ +Networking v2 +============= + +Setup +----- + +.. include:: rs-client.sample.rst + +Now, set up the Cloud Monitoring service: + +.. code-block:: php + + $service = $client->networkingService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + networks + subnets + ports + +Glossary +-------- + +.. glossary:: + + network + A network is an isolated virtual layer-2 broadcast domain that is typically + reserved for the tenant who created it unless you configure the network to + be shared. The network is the main entity in the Networking service. Ports + and subnets are always associated with a network. + + subnet + A subnet represents an IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the Compute + service). Each subnet must have a CIDR and must be associated with a network. + + port + A port represents a virtual switch port on a logical network switch. + Virtual instances (such as servers created using the Compute service) + attach their interfaces into ports. The port also defines the MAC address + and the IP address(es) to be assigned to the interfaces plugged into them. + When IP addresses are associated to a port, this also implies the port is + associated with a subet, as the IP address is taken from the allocation + pool for a specific subnet. diff --git a/doc/services/networking/networks.rst b/doc/services/networking/networks.rst new file mode 100644 index 000000000..a290a34e7 --- /dev/null +++ b/doc/services/networking/networks.rst @@ -0,0 +1,128 @@ +Networks +======== + +Create a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +`Get the executable PHP script for this example `__ + + +Create multiple networks +------------------------ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code-block:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +`Get the executable PHP script for this example `_ + +List networks +------------- + +You can list all the networks to which you have access as shown in the +following example: + +.. code-block:: php + + $networks = $networkingService->listNetworks(); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + + +`Get the executable PHP script for this example `_ + + +Get a network +------------- + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->getNetwork('{networkId}'); + +`Get the executable PHP script for this example `_ + + +Update a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code-block:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +`Get the executable PHP script for this example `_ + + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code-block:: php + + $network->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst new file mode 100644 index 000000000..06d2ec2cf --- /dev/null +++ b/doc/services/networking/ports.rst @@ -0,0 +1,150 @@ +Ports +===== + +Create a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + + +`Get the executable PHP script for this example `_ + + +Create multiple ports +--------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +List ports +---------- + +You can list all the ports to which you have access as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->listPorts(); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +Get a port +---------- + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->getPort('{portId}'); + +`Get the executable PHP script for this example `_ + + +Update a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code-block:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +`Get the executable PHP script for this example `_ + + +Delete a port +------------- + +You can delete a port as shown in the following example: + +.. code-block:: php + + $port->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/subnets.rst b/doc/services/networking/subnets.rst new file mode 100644 index 000000000..e8d6951a0 --- /dev/null +++ b/doc/services/networking/subnets.rst @@ -0,0 +1,152 @@ +Subnets +======= + +Create a subnet +--------------- + +This operation takes one parameter, an associative array, with the following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + +`Get the executable PHP script for this example `_ + + +Create multiple subnets +----------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code-block:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +List subnets +------------ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code-block:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +Get a subnet +------------ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->getSubnet('{subnetId}'); + +`Get the executable PHP script for this example `_ + + +Update a subnet +--------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code-block:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +`Get the executable PHP script for this example `_ + + +Delete a subnet +--------------- + +You can delete a subnet as shown in the following example: + +.. code-block:: php + + $subnet->delete(); + +`Get the executable PHP script for this example `_ From 8b140e21162ccdfc40487f61646dcf10764cb09a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:50 +0100 Subject: [PATCH 434/835] Object Store docs --- doc/services/object-store/Account.md.rst | 33 - .../object-store/Container.md.cdn.rst | 90 -- doc/services/object-store/Object.md.cdn.rst | 25 - .../object-store/Object.md.storage.rst | 330 ------- doc/services/object-store/README.md.rst | 86 -- doc/services/object-store/USERGUIDE.md.rst | 900 ------------------ .../{Access.md.rst => access.rst} | 69 +- doc/services/object-store/account.rst | 46 + doc/services/object-store/cdn.rst | 124 +++ ...ontainer.md.storage.rst => containers.rst} | 193 ++-- doc/services/object-store/index.rst | 57 ++ ...d.storage.rst => migrating-containers.rst} | 64 +- doc/services/object-store/objects.rst | 427 +++++++++ doc/services/object-store/rs-only.rst | 3 + 14 files changed, 834 insertions(+), 1613 deletions(-) delete mode 100644 doc/services/object-store/Account.md.rst delete mode 100644 doc/services/object-store/Container.md.cdn.rst delete mode 100644 doc/services/object-store/Object.md.cdn.rst delete mode 100644 doc/services/object-store/Object.md.storage.rst delete mode 100644 doc/services/object-store/README.md.rst delete mode 100644 doc/services/object-store/USERGUIDE.md.rst rename doc/services/object-store/{Access.md.rst => access.rst} (52%) create mode 100644 doc/services/object-store/account.rst create mode 100644 doc/services/object-store/cdn.rst rename doc/services/object-store/{Container.md.storage.rst => containers.rst} (53%) rename doc/services/object-store/{Migrating.md.storage.rst => migrating-containers.rst} (82%) create mode 100644 doc/services/object-store/objects.rst create mode 100644 doc/services/object-store/rs-only.rst diff --git a/doc/services/object-store/Account.md.rst b/doc/services/object-store/Account.md.rst deleted file mode 100644 index a2d380b2a..000000000 --- a/doc/services/object-store/Account.md.rst +++ /dev/null @@ -1,33 +0,0 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles'); - -View Account Details --------------------- - -To see how many containers you have in your account -(X-Account-Container-Count), how many objects are in your account -(X-Account-Object-Count), and how many total bytes your account uses -(X-Account-Bytes-Used): - -.. code:: php - - $account = $service->getAccount(); - - // Either return the full Metadata object - $details = $account->getDetails(); - - // or individual values - $account->getContainerCount(); - $account->getObjectCount(); - $account->getBytesUsed(); - diff --git a/doc/services/object-store/Container.md.cdn.rst b/doc/services/object-store/Container.md.cdn.rst deleted file mode 100644 index 2f1c8ea68..000000000 --- a/doc/services/object-store/Container.md.cdn.rst +++ /dev/null @@ -1,90 +0,0 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles'); - -To access the CDN functionality of a particular container: - -.. code:: php - - $container = $service->getContainer('foo_bar'); - - $cdn = $container->getCdn(); - -List CDN-enabled container --------------------------- - -To list CDN-only containers, follow the same operation for Storage which -lists all containers. The only difference is which service object you -execute the method on: - -.. code:: php - - $cdnService = $service->getCdnService(); - $cdnContainers = $cdnService->listContainers(); - - foreach ($cdnContainers as $cdnContainer) { - - } - -CDN-enable and -disable a container ------------------------------------ - -Before a container can be CDN-enabled, it must exist in the storage -system. When a container is CDN-enabled, any objects stored in it are -publicly accessible over the Content Delivery Network by combining the -container's CDN URL with the object name. - -Any CDN-accessed objects are cached in the CDN for the specified amount -of time called the TTL. The default TTL value is 259200 seconds, or 72 -hours. Each time the object is accessed after the TTL expires, the CDN -refetches and caches the object for the TTL period. - -.. code:: php - - $container->enableCdn(); - $container->disableCdn(); - -Serving containers through SSL ------------------------------- - -.. code:: php - - $cdn->getCdnSslUri(); - -Streaming CDN-enabled containers --------------------------------- - -.. code:: php - - $cdn->getCdnStreamingUri(); - -iOS streaming -------------- - -The Cloud Files CDN allows you to stream video to iOS devices without -needing to convert your video. Once you CDN-enable your container, you -have the tools necessary for streaming media to multiple devices. - -.. code:: php - - $cdn->getIosStreamingUri(); - -CDN logging ------------ - -To enable and disable logging for your CDN: - -.. code:: php - - $cdn->enableCdnLogging(); - $cdn->disableCdnLogging(); - diff --git a/doc/services/object-store/Object.md.cdn.rst b/doc/services/object-store/Object.md.cdn.rst deleted file mode 100644 index 64ac143b3..000000000 --- a/doc/services/object-store/Object.md.cdn.rst +++ /dev/null @@ -1,25 +0,0 @@ -Setup ------ - -You will need to instantiate the container object and access its CDN -functionality as `documented -here `__. - -Purge CDN-enabled objects -------------------------- - -To remove a CDN object from public access: - -.. code:: php - - $object->purge(); - -You can also provide an optional e-mail address (or comma-delimeted list -of e-mails), which the API will send a confirmation message to once the -object has been completely purged: - -.. code:: php - - $object->purge('jamie.hannaford@rackspace.com'); - $object->purge('hello@example.com,hallo@example.com'); - diff --git a/doc/services/object-store/Object.md.storage.rst b/doc/services/object-store/Object.md.storage.rst deleted file mode 100644 index 16dab18f4..000000000 --- a/doc/services/object-store/Object.md.storage.rst +++ /dev/null @@ -1,330 +0,0 @@ -Setup ------ - -Conceptually, a container contains objects (also known as files). In -order to work with objects, you will need to instantiate a container -object first as `documented -here `__. - -Note on object properties -------------------------- - -Please be aware that you cannot directly access the properties of -DataObject anymore, you **must** use appropriate getter/ setter methods: - -+----------------------+------------------------+ -| Property | Method | -+======================+========================+ -| Parent container | ``getContainer`` | -+----------------------+------------------------+ -| Name | ``getName`` | -+----------------------+------------------------+ -| Body of file | ``getContent`` | -+----------------------+------------------------+ -| Size of file | ``getContentLength`` | -+----------------------+------------------------+ -| Type of file | ``getContentType`` | -+----------------------+------------------------+ -| ETag checksum | ``getEtag`` | -+----------------------+------------------------+ -| Last modified date | ``getLastModified`` | -+----------------------+------------------------+ - -Create an object ----------------- - -There are three ways to upload a new file, each of which has different -business needs. - - **Note:** Unlike previous versions, you do not need to manually - specify your object's content type. The API will do this for you. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -To upload a single/basic file: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - use OpenCloud\ObjectStore\Resource\DataObject; - - $data = fopen('/path/to/sample.mp3', 'r+'); - - // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) - - $meta = array( - 'Author' => 'Camera Obscura', - 'Origin' => 'Glasgow' - ); - - $metaHeaders = DataObject::stockHeaders($meta); - $customHeaders = array(); - $allHeaders = $metaHeaders + $customHeaders; - - $container->uploadObject('sample.mp3', $data, $allHeaders); - -To upload multiple small-to-mid sized files: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $files = array( - array( - 'name' => 'apache.log', - 'path' => '/etc/httpd/logs/error_log' - ), - array( - 'name' => 'mysql.log', - 'body' => fopen('/tmp/mysql.log', 'r+') - ), - array( - 'name' => 'to_do_list.txt', - 'body' => 'PHONE HOME' - ) - ); - - $container->uploadObjects($files); - -As you can see, the ``name`` key is required for every file. You must -also specify *either* a path key (to an existing file), or a ``body``. -The ``body`` can either be a PHP resource or a string representation of -the content you want to upload. - -To upload large files -~~~~~~~~~~~~~~~~~~~~~ - -For files over 5GB, you will need to use the -``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your -transfer, upon which you can execute your upload functionality. For your -convenience, the Container resource object contains a simple method to -do this heavy lifting for you: - -.. code:: php - - $transfer = $container->setupObjectTransfer(array( - 'name' => 'video.mov', - 'path' => '/home/jamie/video.mov', - 'metadata' => array( - 'Author' => 'Jamie' - ), - 'concurrency' => 4, - 'partSize' => 1.5 * Size::GB - )); - - $transfer->upload(); - -You can specify how many concurrent cURL connections are used to upload -parts of your file. The file is fragmented into chunks, each of which is -uploaded individually as a separate file (the filename of each part will -indicate that it's a segment rather than the full file). After all parts -are uploaded, a manifest is uploaded. When the end-user accesses the 5GB -by its true filename, it actually references the manifest file which -concatenates each segment into a streaming download. - -List objects in a container ---------------------------- - -To return a list of objects: - -.. code:: php - - $files = $container->objectList(); - - foreach ($files as $file) { - // ... do something - } - -By default, 10,000 objects are returned as a maximum. To get around -this, you can construct a query which refines your result set. For a -full specification of query parameters relating to collection filtering, -see the `official -docs `__. - -.. code:: php - - $container->objectList(array('prefix' => 'logFile_')); - -Get object ----------- - -To retrieve a specific file from Cloud Files: - -.. code:: php - - $file = $container->getObject('summer_vacation.mp4'); - -Conditional requests -~~~~~~~~~~~~~~~~~~~~ - -You can also perform conditional requests according to `RFC 2616 -specification `__ (§§ 14.24-26). -Supported headers are ``If-Match``, ``If-None-Match``, -``If-Modified-Since`` and ``If-Unmodified-Since``. - -So, to retrieve a file's contents only if it's been recently changed - -.. code:: php - - $file = $container->getObject('error_log.txt', array( - 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' - )); - - if ($file->getContentLength()) { - echo 'Has been changed since the above date'; - } else { - echo 'Has not been changed'; - } - -Retrieve a file only if it has NOT been modified (and expect a 412 on -failure): - -:: - - use Guzzle\Http\Exception\ClientErrorResponseException; - - try { - $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( - 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' - )); - } catch (ClientErrorResponseException $e) { - echo 'This file has been modified...'; - } - -Finally, you can specify a range - which will return a subset of bytes -from the file specified. To return the last 20B of a file: - -.. code:: php - - $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); - -Update an existing object -------------------------- - -Updating content is easy: - -.. code:: php - - $file->setContent(fopen('/path/to/new/content', 'r+')); - $file->update(); - -Bear in mind that updating a file name will result in a new file being -generated (under the new name). You will need to delete the old file. - -Copy object ------------ - -To copy a file to another location, you need to specify a string-based -destination path: - -.. code:: php - - $object->copy('/container_2/new_object_name'); - -Delete object -------------- - -.. code:: php - - $object->delete(); - -Get object metadata -------------------- - -You can fetch just the object metadata without fetching the full -content: - -.. code:: php - - $container->getPartialObject('summer_vacation.mp4'); - -In order to access the metadata on a partial or complete object, use: - -.. code:: php - - $object->getMetadata(); - -You can turn a partial object into a full object to get the content -after looking at the metadata: - -.. code:: php - - $object->refresh(); - -You can also update to get the latest metadata: - -.. code:: php - - $object->retrieveMetadata(); - -Update object metadata ----------------------- - -Similarly, with setting metadata there are two options: you can update -the metadata values of the local object (i.e. no HTTP request) if you -anticipate you'll be executing one soon (an update operation for -example): - -.. code:: php - - // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation - $object->setMetadata(array( - 'Author' => 'Hemingway' - )); - - // ... code here - - $object->update(); - -Alternatively, you can update the API straight away - so that everything -is retained: - -.. code:: php - - $object->saveMetadata(array( - 'Author' => 'Hemingway' - )); - -Please be aware that these methods override and wipe existing values. If -you want to append values to your metadata, use the correct method: - -.. code:: php - - $metadata = $object->appendToMetadata(array( - 'Author' => 'Hemingway' - )); - - $object->saveMetadata($metadata); - -Extract archive ---------------- - -CloudFiles provides you the ability to extract uploaded archives to -particular destinations. The archive will be extracted and its contents -will populate the particular area specified. To upload file (which might -represent a directory structure) into a particular container: - -.. code:: php - - use OpenCloud\ObjectStore\Constants\UrlType; - - $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); - -You can also omit the container name (i.e. provide an empty string as -the first argument). If you do this, the API will create the containers -necessary to house the extracted files - this is done based on the -filenames inside the archive. - -Bulk delete ------------ - -Bulk delete a set of paths: - -.. code:: php - - $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - - $service->bulkDelete($pathsToBeDeleted); - diff --git a/doc/services/object-store/README.md.rst b/doc/services/object-store/README.md.rst deleted file mode 100644 index 01cf26733..000000000 --- a/doc/services/object-store/README.md.rst +++ /dev/null @@ -1,86 +0,0 @@ -Object Store -============ - -**Object Store** is an object-based storage system that stores content -and metadata as objects in a cloud. - -Specifically, a cloud is made up of one or more regions. Each region can -have several **containers**, created by a user. Each container can -container several **objects** (sometimes referred to as files), uploaded -by the user. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Choose one of the following two options: - -- If you are working with a vanilla OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as shown below. - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as shown below. - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Object Store service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $region = 'DFW'; - $objectStoreService = $client->objectStoreService(null, $region); - -In the example above, you are connecting to the ``DFW`` region of the -cloud. Any containers and objects created with this -``$objectStoreService`` instance will be stored in that cloud region. - -3. Create a container for your objects (also referred to as files). -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $container = $objectStoreService->createContainer('logos'); - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -4. Upload an object to the container. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -There is a lot more you can do with containers and objects. See the -`complete user guide to the Object Store service `__. diff --git a/doc/services/object-store/USERGUIDE.md.rst b/doc/services/object-store/USERGUIDE.md.rst deleted file mode 100644 index d9732ea36..000000000 --- a/doc/services/object-store/USERGUIDE.md.rst +++ /dev/null @@ -1,900 +0,0 @@ -The Complete User Guide to the Object Store Service -=================================================== - -**Object Store** is an object-based storage system that stores content -and metadata as objects in a cloud. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the object store service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with a vanilla OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as shown below. - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as shown below. - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Object Store Service -~~~~~~~~~~~~~~~~~~~~ - -All operations on the object store are done via an object store service -object. - -.. code:: php - - $region = 'DFW'; - $objectStoreService = $client->objectStoreService(null, $region); - -In the example above, you are connecting to the ``DFW`` region of the -cloud. Any containers and objects created with this -``$objectStoreService`` instance will be stored in that cloud region. - -Containers ----------- - -A **container** defines a namespace for **objects**. An object with the -same name in two different containers represents two different objects. - -For example, you may create a container called ``logos`` to hold all the -image files for your blog. - -A container may contain zero or more objects in it. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -Create Container -~~~~~~~~~~~~~~~~ - -.. code:: php - - $container = $objectStoreService->createContainer('logos'); - -[ `Get the executable PHP script for this -example `__ ] - -Get Container Details -~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a single container's details by using its name. An -instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. - -.. code:: php - - $container = $objectStoreService->getContainer('logos'); - - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - -[ `Get the executable PHP script for this -example `__ ] - -List Containers -~~~~~~~~~~~~~~~ - -You can retrieve a list of all your containers. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $containers = $objectStoreService->listContainers(); - foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Set or Update Container Metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can set metadata on a container. - -.. code:: php - - $container->saveMetadata(array( - 'author' => 'John Doe' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Get Container Metadata -~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve the metadata for a container. - -.. code:: php - - $containerMetadata = $container->getMetadata(); - -[ `Get the executable PHP script for this -example `__ ] - -Delete Container -~~~~~~~~~~~~~~~~ - -When you no longer have a need for the container, you can remove it. - -If the container is empty (that is, it has no objects in it), you can -remove it as shown below: - -.. code:: php - - $container->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -If the container is not empty (that is, it has objects in it), you have -two choices in how to remove it: - -- `Individually remove each object <#delete-object>`__ in the - container, then remove the container itself as shown above, or - -- Remove the container and all the objects within it as shown below: - - .. code:: php - - $container->delete(true); - - [ `Get the executable PHP script for this - example `__ ] - -Get Object Count -~~~~~~~~~~~~~~~~ - -You can quickly find out how many objects are in a container. - -.. code:: php - - $containerObjectCount = $container->getObjectCount(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$containerObjectCount`` will contain the number -of objects in the container represented by ``$container``. - -Get Bytes Used -~~~~~~~~~~~~~~ - -You can quickly find out the space used by a container, in bytes. - -.. code:: php - - $containerSizeInBytes = $container->getBytesUsed(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$containerSizeInBytes`` will contain the space -used, in bytes, by the container represented by ``$container``. - -Container Quotas -~~~~~~~~~~~~~~~~ - -Set Quota for Number of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can set a quota for the maximum number of objects that may be stored -in a container. - -.. code:: php - - $maximumNumberOfObjectsAllowedInContainer = 25; - $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); - -[ `Get the executable PHP script for this -example `__ ] - -Set Quota for Total Size of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can set a quota for the maximum total space (in bytes) used by -objects in a container. - -.. code:: php - - use OpenCloud\Common\Constants\Size; - - $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; - $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); - -[ `Get the executable PHP script for this -example `__ ] - -Get Quota for Number of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can retrieve the quota for the maximum number of objects that may be -stored in a container. - -.. code:: php - - $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); - -[ `Get the executable PHP script for this -example `__ ] - -Get Quota for Total Size of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can retrieve the quota for the maximum total space (in bytes) used -by objects in a container. - -.. code:: php - - $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); - -[ `Get the executable PHP script for this -example `__ ] - -Objects -------- - -An **object** (sometimes referred to as a file) is the unit of storage -in an Object Store. An object is a combination of content (data) and -metadata. - -For example, you may upload an object named ``php-elephant.jpg``, a JPEG -image file, to the ``logos`` container. Further, you may assign metadata -to this object to indicate that the author of this object was someone -named Jane Doe. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -Upload Object -~~~~~~~~~~~~~ - -Once you have created a container, you can upload objects to it. - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, an image file from the local filesystem -(``path/to/local/php-elephant.jpg``) is uploaded to a container in the -Object Store. - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -It is also possible to upload an object and associate metadata with it. - -.. code:: php - - use OpenCloud\ObjectStore\Resource\DataObject; - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - $metadata = array('author' => 'Jane Doe'); - - $customHeaders = array(); - $metadataHeaders = DataObject::stockHeaders($metadata); - $allHeaders = $customHeaders + $metadataHeaders; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData, $allHeaders); - -[ `Get the executable PHP script for this -example `__ ] - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -Pseudo-hierarchical Folders -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Although you cannot nest directories in an Object Store, you can -simulate a hierarchical structure within a single container by adding -forward slash characters (``/``) in the object name. - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'languages/php/elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, an image file from the local filesystem -(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the -Object Store. Within that container, the filename is -``languages/php/elephant.jpg``, where ``languages/php/`` is a -pseudo-hierarchical folder hierarchy. - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -Upload Multiple Objects -^^^^^^^^^^^^^^^^^^^^^^^ - -You can upload more than one object at a time to a container. - -.. code:: php - - $objects = array( - array( - 'name' => 'php-elephant.jpg', - 'path' => '/path/to/local/php-elephant.jpg' - ), - array( - 'name' => 'python-snake.jpg', - 'path' => '/path/to/local/python-snake.jpg' - ), - a - ); - - $container->uploadObjects($objects); - -[ `Get the executable PHP script for this -example `__ ] - -In the above example, the contents of two files present on the local -filesystem are uploaded as objects to the container referenced by -``$container``. - -Instead of specifying the ``path`` key in an element of the ``$objects`` -array, you can specify a ``body`` key whose value is a string or a -stream representation. - -Finally, you can pass headers as the second parameter to the -``uploadObjects`` method. These headers will be applied to every object -that is uploaded. - -:: - - $metadata = array('author' => 'Jane Doe'); - - $customHeaders = array(); - $metadataHeaders = DataObject::stockHeaders($metadata); - $allHeaders = $customHeaders + $metadataHeaders; - - $container->uploadObjects($objects, $allHeaders); - -[ `Get the executable PHP script for this -example `__ -] - -In the example above, every object referenced within the ``$objects`` -array will be uploaded with the same metadata. - -Large Objects -~~~~~~~~~~~~~ - -If you want to upload objects larger than 5GB in size, you must use a -different upload process. - -.. code:: php - - $options = array( - 'name' => 'san_diego_vacation_video.mp4', - 'path' => '/path/to/local/videos/san_diego_vacation.mp4' - ); - $objectTransfer = $container->setupObjectTransfer($options); - $objectTransfer->upload(); - -[ `Get the executable PHP script for this -example `__ ] - -The process shown above will automatically partition your large object -into small chunks and upload them concurrently to the container -represented by ``$container``. - -You can tune the parameters of this process by specifying additional -options in the ``$options`` array. Here is a complete listing of keys -that can be specified in the ``$options`` array: - -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| Key name | Description | Data Type | Required? | Default Value | Example | -+===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ -| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ - -Auto-extract Archive Files -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can upload a tar archive file and have the Object Store service -automatically extract it into a container. - -.. code:: php - - use OpenCloud\ObjectStore\Constants\UrlType; - - $localArchiveFileName = '/path/to/local/image_files.tar.gz'; - $remotePath = 'images/'; - - $fileData = fopen($localArchiveFileName, 'r'); - $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); - -[ `Get the executable PHP script for this -example `__ ] - -In the above example, a local archive file named ``image_files.tar.gz`` -is uploaded to an Object Store container named ``images`` (defined by -the ``$remotePath`` variable). - -Note that while we call ``fopen`` to open a file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``bulkExtract`` call. - -The third parameter to ``bulkExtract`` is the type of the archive file -being uploaded. The acceptable values for this are: - -- ``UrlType::TAR`` for tar archive files, *or*, -- ``UrlType:TAR_GZ`` for tar archive files that are compressed with - gzip, *or* -- ``UrlType::TAR_BZ`` for tar archive file that are compressed with - bzip - -Note that the value of ``$remotePath`` could have been a -(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as -``images/blog`` as well. - -List Objects in a Container -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all the objects stored in a container. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $objects = $container->objectList(); - foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -You can list only those objects in the container whose names start with -a certain prefix. - -.. code:: php - - $options = array( - 'prefix' => 'php' - ); - - $objects = $container->objectList($options); - -[ `Get the executable PHP script for this -example `__ ] - -In general, the ``objectList()`` method described above takes an -optional parameter (``$options`` in the example above). This parameter -is an associative array of various options. Here is a complete listing -of keys that can be specified in the ``$options`` array: - -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| Key name | Description | Data Type | Required? | Default Value | Example | -+==================+=============================================================================+=============+=============+=================+=========================+ -| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ - -Retrieve Object -~~~~~~~~~~~~~~~ - -You can retrieve an object and its metadata, given the object's -container and name. - -.. code:: php - - $objectName = 'php-elephant.jpg'; - $object = $container->getObject($objectName); - - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - - $objectContent = $object->getContent(); - - /** @var $objectContent Guzzle\Http\EntityBody **/ - - // Write object content to file on local filesystem. - $objectContent->rewind(); - $stream = $objectContent->getStream(); - $localFilename = tempnam("/tmp", 'php-opencloud-'); - file_put_contents($localFilename, $stream); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$object`` is the object named -``php-elephant.jpg`` in the container represented by ``$container``. -Further, ``$objectContent`` represents the contents of the object. It is -of type -```Guzzle\Http\EntityBody`` `__. - -Retrieve Object Metadata -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve just an object's metadata without retrieving its -contents. - -.. code:: php - - $objectName = 'php-elephant.jpg'; - $object = $container->getPartialObject($objectName); - $objectMetadata = $object->getMetadata(); - - /** @var $objectMetadata \OpenCloud\Common\Metadata **/ - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, while ``$object`` is an instance of -``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only -partially populated. Specifically, only properties of the instance -relating to object metadata are populated. - -Temporary URLs -~~~~~~~~~~~~~~ - -The Temporary URL feature allows you to create limited-time Internet -addresses that allow you to grant limited access to your Object Store -account. Using this feature, you can allow others to retrieve or place -objects in your Object Store account for a specified amount of time. -Access to the temporary URL is independent of whether or not your -account is `CDN-enabled <#cdn-containers>`__. Even if you do not -CDN-enable a container, you can still grant temporary public access -through a temporary URL. - -First, you must set the temporary URL secret on your account. This is a -one-time operation; you only need to perform it the very first time you -wish to use the temporary URLs feature. - -.. code:: php - - $account->setTempUrlSecret(); - -[ `Get the executable PHP script for this -example `__ ] - -Note that this operation is carried out on ``$account``, which is an -instance of ``OpenCloud\ObjectStore\Resource\Account``, a class -representing `your object store account <#accounts>`__. - -The above operation will generate a random secret and set it on your -account. Instead of a random secret, if you wish to provide a secret, -you can supply it as a parameter to the ``setTempUrlSecret`` method. - -.. code:: php - - $account->setTempUrlSecret(''); - -[ `Get the executable PHP script for this -example `__ -] - -Once a temporary URL secret has been set on your account, you can -generate a temporary URL for any object in your Object Store. - -.. code:: php - - $expirationTimeInSeconds = 3600; // one hour from now - $httpMethodAllowed = 'GET'; - $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, a temporary URL for the object is generated. This -temporary URL will provide public access to the object for an hour (3600 -seconds), as specified by the ``$expirationTimeInSeconds`` variable. -Further, only GET HTTP methods will be allowed on this URL, as specified -by the ``$httpMethodAllowed`` variable. The other value allowed for the -``$httpMethodAllowed`` variable would be ``PUT``. - -You can also retrieve the temporary URL secret that has been set on your -account. - -.. code:: php - - $tempUrlSecret = $account->getTempUrlSecret(); - -[ `Get the executable PHP script for this -example `__ ] - -Update Object -~~~~~~~~~~~~~ - -You can update an object's contents (as opposed to `updating its -metadata <#update-object-metadata>`__) by simply re-\ `uploading the -object <#upload-object>`__ to its container using the same object name -as before. - -Update Object Metadata -~~~~~~~~~~~~~~~~~~~~~~ - -You can update an object's metadata after it has been uploaded to a -container. - -.. code:: php - - $object->saveMetadata(array( - 'author' => 'John Doe' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Copy Object -~~~~~~~~~~~ - -You can copy an object from one container to another, provided the -destination container already exists. - -.. code:: php - - $object->copy('logos_copy/php.jpg'); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, both the name of the destination container -(``logos_copy``)and the name of the destination object (``php.jpg``) -have to be specified, separated by a ``/``. - -Delete Object -~~~~~~~~~~~~~ - -When you no longer need an object, you can delete it. - -.. code:: php - - $object->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Bulk Delete -~~~~~~~~~~~ - -While you can delete individual objects as shown above, you can also -delete objects and empty containers in bulk. - -.. code:: php - - $objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' - )); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, two objects (``some_container/object_a.png``, -``some_other_container/object_z.png``) and one empty container -(``some_empty_container``) are all being deleted in bulk via a single -command. - -CDN Containers --------------- - -Note: The functionality described in this section is available only on -the Rackspace cloud. It will not work as described when working with a -vanilla OpenStack cloud. - -Any container can be converted to a CDN-enabled container. When this is -done, the objects within the container can be accessed from anywhere on -the Internet via a URL. - -Enable CDN Container -~~~~~~~~~~~~~~~~~~~~ - -To take advantage of CDN capabilities for a container and its objects, -you must CDN-enable that container. - -.. code:: php - - $container->enableCdn(); - -[ `Get the executable PHP script for this -example `__ ] - -Public URLs -~~~~~~~~~~~ - -Once you have CDN-enabled a container, you can retrieve a -publicly-accessible URL for any of its objects. There are four types of -publicly-accessible URLs for each object. Each type of URL is meant for -a different purpose. The sections below describe each of these URL types -and how to retrieve them. - -HTTP URL -^^^^^^^^ - -You can use this type of URL to access the object over HTTP. - -:: - - $httpUrl = $object->getPublicUrl(); - -[ `Get the executable PHP script for this -example `__ ] - -Secure HTTP URL -^^^^^^^^^^^^^^^ - -You can use this type of URL to access the object over HTTP + TLS/SSL. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $httpsUrl = $object->getPublicUrl(UrlType::SSL); - -[ `Get the executable PHP script for this -example `__ ] - -Streaming URL -^^^^^^^^^^^^^ - -You can use this type of URL to stream a video or audio object using -Adobe's HTTP Dynamic Streaming. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); - -[ `Get the executable PHP script for this -example `__ ] - -IOS Streaming URL -^^^^^^^^^^^^^^^^^ - -You can use this type of URL to stream an audio or video object to an -iOS device. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); - -[ `Get the executable PHP script for this -example `__ ] - -Update CDN Container TTL -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can update the TTL of a CDN-enabled container. - -.. code:: php - - $cdnContainer = $container->getCdn(); - $cdnContainer->setTtl(); - -[ `Get the executable PHP script for this -example `__ ] - -Disable CDN Container -~~~~~~~~~~~~~~~~~~~~~ - -If you no longer need CDN capabilities for a container, you can disable -them. - -.. code:: php - - $container->disableCdn(); - -[ `Get the executable PHP script for this -example `__ ] - -Accounts --------- - -An **account** defines a namespace for **containers**. An account can -have zero or more containers in it. - -Retrieve Account -~~~~~~~~~~~~~~~~ - -You must retrieve the account before performing any operations on it. - -.. code:: php - - $account = $objectStoreService->getAccount(); - -Get Container Count -~~~~~~~~~~~~~~~~~~~ - -You can quickly find out how many containers are in your account. - -.. code:: php - - $accountContainerCount = $account->getContainerCount(); - -[ `Get the executable PHP script for this -example `__ ] - -Get Object Count -~~~~~~~~~~~~~~~~ - -You can quickly find out how many objects are in your account. - -.. code:: php - - $accountObjectCount = $account->getObjectCount(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$accountObjectCount`` will contain the number of -objects in the account represented by ``$account``. - -Get Bytes Used -~~~~~~~~~~~~~~ - -You can quickly find out the space used by your account, in bytes. - -.. code:: php - - $accountSizeInBytes = $account->getBytesUsed(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$accountSizeInBytes`` will contain the space -used, in bytes, by the account represented by ``$account``. diff --git a/doc/services/object-store/Access.md.rst b/doc/services/object-store/access.rst similarity index 52% rename from doc/services/object-store/Access.md.rst rename to doc/services/object-store/access.rst index ee85a938e..62cb541ca 100644 --- a/doc/services/object-store/Access.md.rst +++ b/doc/services/object-store/access.rst @@ -1,31 +1,19 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want - Temporary URLs --------------- +============== Temporary URLs allow you to create time-limited Internet addresses that allow you to grant access to your Cloud Files account. Using Temporary URL, you may allow others to retrieve or place objects in your containers - regardless of whether they're CDN-enabled. + Set "temporary URL" metadata key -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------- You must set this "secret" value on your account, where it can be used in a global state: -.. code:: php +.. code-block:: php $account = $service->getAccount(); $account->setTempUrlSecret('my_secret'); @@ -35,41 +23,64 @@ in a global state: The string argument of ``setTempUrlSecret()`` is optional - if left out, the SDK will generate a random hashed secret for you. + Create a temporary URL -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- Once you've set an account secret, you can create a temporary URL for your object. To allow GET access to your object for 1 minute: -.. code:: php +.. code-block:: php $object->getTemporaryUrl(60, 'GET'); + To allow PUT access for 1 hour: -.. code:: php +.. code-block:: php $object->getTemporaryUrl(360, 'PUT'); -Hosting websites on CloudFiles ------------------------------- -To host a static (i.e. HTML) website on CloudFiles, you must follow +Hosting HTML sites on CDN +========================= + +.. include:: rs-only.rst + +To host a static (i.e. HTML) website on Cloud Files, you must follow these steps: -1. CDN-enable a container +1. CDN-enable a container: + +.. code-block:: php + + $container = $service->getContainer('html_site'); + $container->enableCdn(); + 2. Upload all HTML content. You can use nested directory structures. -3. Tell CloudFiles what to use for your default index page like this: -.. code:: php +.. code-block:: php + + $container->uploadObjects(array( + array('name' => 'index.html', 'path' => 'index.html'), + array('name' => 'contact.html', 'path' => 'contact.html'), + array('name' => 'error.html', 'path' => 'error.html'), + array('name' => 'styles.css', 'path' => 'styles.css'), + array('name' => 'main.js', 'path' => 'main.js'), + )); + +3. Tell Cloud Files what to use for your default index page like this: + +.. code-block:: php + + $container->setStaticIndexPage('index.html'); - $container->setStaticIndexPage('index.html'); +4. (Optional) Tell Cloud Files which error page to use by default: -4. (Optional) Tell CloudFiles which error page to use by default: +.. code-block:: php -.. code:: php + $container->setStaticErrorPage('error.html'); - $container->setStaticErrorPage('error.html'); Bear in mind that steps 3 & 4 do not upload content, but rather specify a reference to an existing page/CloudFiles object. diff --git a/doc/services/object-store/account.rst b/doc/services/object-store/account.rst new file mode 100644 index 000000000..fb51c8855 --- /dev/null +++ b/doc/services/object-store/account.rst @@ -0,0 +1,46 @@ +Account Details +=============== + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +Setup +----- + +.. code-block:: php + + $account = $service->getAccount(); + + +View all details +---------------- + +.. code-block:: php + + $details = $account->getDetails(); + + +Retrieve total container count +------------------------ + +.. code-block:: php + + $account->getContainerCount(); + + +Retrieve total object count +--------------------- + +.. code-block:: php + + $account->getObjectCount(); + + +Retrieve total bytes used +------------------------- + +.. code-block:: php + + $account->getBytesUsed(); diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst new file mode 100644 index 000000000..5ad72b6cd --- /dev/null +++ b/doc/services/object-store/cdn.rst @@ -0,0 +1,124 @@ +CDN Containers +============== + +.. include:: rs-only.rst + +Setup +----- + +In order to interact with CDN containers, you first need to instantiate a +CDN service object: + +.. code-block:: php + + $cdnService = $service->getCdnService(); + + +List CDN-enabled containers +--------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code-block:: php + + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ + } + + +CDN-enable a container +---------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code-block:: php + + $container->enableCdn(); + + +CDN-disable a container +----------------------- + +.. code-block:: php + + $container->disableCdn(); + + +Operations on CDN-enabled containers +------------------------------------ + +Once a container has been CDN-enabled, you can retrieve it like so: + +.. code-block:: php + + $cdnContainer = $cdnService->cdnContainer('{containerName}'); + + +Retrieve the SSL URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnSslUri(); + + +Retrieve the streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnStreamingUri(); + + +Retrieve the iOS streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code-block:: php + + $cdnContainer->getIosStreamingUri(); + + +CDN logging +~~~~~~~~~~~ + +To enable and disable logging for your CDN-enabled container: + +.. code-block:: php + + $cdnContainer->enableCdnLogging(); + $cdnContainer->disableCdnLogging(); + + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code-block:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code-block:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); diff --git a/doc/services/object-store/Container.md.storage.rst b/doc/services/object-store/containers.rst similarity index 53% rename from doc/services/object-store/Container.md.storage.rst rename to doc/services/object-store/containers.rst index 89798fdd0..4b9c66ac3 100644 --- a/doc/services/object-store/Container.md.storage.rst +++ b/doc/services/object-store/containers.rst @@ -1,26 +1,12 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - // Create a client object to communicate with various Rackspace Cloud services. - $client = new Rackspace(RACKSPACE_US, array( - 'username' => 'Replace this with your Rackspace Cloud user name', - 'apiKey' => 'Replace this with your Rackspace Cloud API key' - )); - - // Create a service object to use the object store service. The sample code - // creates the object store in the 'DFW' region. - $service = $client->objectStoreService('cloudFiles', 'DFW'); +Containers +========== Create container ---------------- To create a new container, you just need to define its name: -.. code:: php +.. code-block:: php $container = $service->createContainer('my_amazing_container'); @@ -30,133 +16,153 @@ likely due to the fact you have a naming collision. Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in List containers --------------- -Return a list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $containerList = $service->listContainers(); + $containers = $service->listContainers(); - while ($container = $containerList->next()) { - // Do stuff; some examples below - printf("Container name: %s\n", $container->name); - printf("Number of objects within container: %d\n", $container->getObjectCount()); - } + foreach ($containers as $container) { + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } Container names are sorted based on a binary comparison, a single built-in collating sequence that compares string data using SQLite's memcmp() function, regardless of text encoding. -The list is limited to 10,000 containers at a time. See 1.3 for ways to -limit and navigate this list. +The list is limited to 10,000 containers at a time. To work with larger +collections, please read the next section. -Return a formatted list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, the SDK only supports JSON-formatted responses. - -Controlling a large list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Filtering large collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You may limit and control this list of results by using the ``marker`` +When you need more control over collections of containers, you can filter the +results and return back a subset of the total collection by using the ``marker`` and ``end_marker`` parameters. The former parameter (``marker``) tells the API where to begin the list, and the latter (``end_marker``) tells it where to end the list. You may use either of them independently or -together. You may also use the ``limit`` parameter to fix the number of +together. + +You may also use the ``limit`` parameter to fix the number of containers returned. To list a set of containers between two fixed points: -.. code:: php +.. code-block:: php - $someContainers = $service->listContainers(array( - 'marker' => 'container_55', - 'end_marker' => 'container_2001' - )); + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); Or to return a limited set: -.. code:: php +.. code-block:: php + + $someContainers = $service->listContainers(array('limit' => 560)); - $someContainers = $service->listContainers(array('limit' => 560)); Get container ------------- -To retrieve a certain container, either to access its object or -metadata: +To retrieve a certain container: + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $service->getContainer('{containerName}'); + + +Retrieve a container's name +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php -.. code:: php + $name = $container->name; - $container = $service->getContainer('container_name'); - echo $container->getObjectCount(); - echo $container->getBytesUsed(); +Retrieve a container's object count +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $count = $container->getObjectCount(); + + +Retrieve a container's total bytes used +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $bytes = $container->getBytesUsed(); + Delete container ---------------- -Deleting a container is easy: +Deleting an empty container is easy: -.. code:: php +.. code-block:: php + + $container->delete(); - $container->delete(); Please bear mind that you must delete all objects inside a container before deleting it. This is done for you if you set the ``$deleteObjects`` parameter to ``TRUE`` like so: -.. code:: php +.. code-block:: php + + $container->delete(true); - $container->delete(TRUE); -You can also do it manually: +You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ +first, and then call ``delete``. -.. code:: php - $container->deleteAllObjects(); - $container->delete(); +Deleting all objects inside a container +--------------------------------------- + +.. code-block:: php + + $container->deleteAllObjects(); + Create or update container metadata ----------------------------------- -.. code:: php +.. code-block:: php - $container->saveMetadata(array( - 'Author' => 'Virginia Woolf', - 'Published' => '1931' - )); + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); Please bear in mind that this action will set metadata to this array - overriding existing values and wiping those left out. To *append* values to the current metadata: -.. code:: php - - $metadata = $container->appendToMetadata(array( - 'Publisher' => 'Hogarth' - )); +.. code-block:: php -If you only want to set the metadata to the local object, and not -immediately retain these values on the API, you can use a standard -setter method - which can contribute to eventual actions like an update: + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); -.. code:: php - - $container->setMetadata(array('Foo' => 'Bar')); Container quotas ---------------- -The container\_quotas middleware implements simple quotas that can be +The ``container_quotas`` middleware implements simple quotas that can be imposed on Cloud Files containers by a user. Setting container quotas can be useful for limiting the scope of containers that are delegated to non-admin users, exposed to formpost uploads, or just as a self-imposed @@ -164,19 +170,20 @@ sanity check. To set quotas for a container: -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Size; + use OpenCloud\Common\Constants\Size; - $container->setCountQuota(1000); - $container->setBytesQuota(2.5 * Size::GB); + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); And to retrieve them: -.. code:: php +.. code-block:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); - echo $container->getCountQuota(); - echo $container->getBytesQuota(); Access log delivery ------------------- @@ -186,10 +193,11 @@ access logs to analyze the number of people who access your objects, where they come from, how many requests for each object you receive, and time-based usage patterns (such as monthly or seasonal usage). -.. code:: php +.. code-block:: php + + $container->enableLogging(); + $container->disableLogging(); - $container->enableLogging(); - $container->disableLogging(); Syncing containers ------------------ @@ -198,9 +206,9 @@ You can synchronize local directories with your CloudFiles/Swift containers very easily. When you do this, the container will mirror exactly the nested file structure within your local directory: -.. code:: php +.. code-block:: php - $container->uploadDirectory('/home/Jamie/blog'); + $container->uploadDirectory('/home/user/my-blog'); There are four scenarios you should be aware of: @@ -215,4 +223,3 @@ There are four scenarios you should be aware of: +------------------------+-----------------------+----------------------+--------------------------------+ | Files does not exist | File exists | - | Remote file deleted | +------------------------+-----------------------+----------------------+--------------------------------+ - diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index e69de29bb..6b3a18b6f 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -0,0 +1,57 @@ +Object Store v1 +=============== + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Object Store service: + +.. code-block:: php + + $service = $client->objectStoreService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + account + containers + objects + cdn + migrating-containers + access + +Glossary +-------- + +.. glossary:: + + account + The portion of the system designated for your use. An Object Store system is + typically designed to be used by many different customers, and your user + account is your portion of it. + + container + A storage compartment that provides a way for you to organize data. A + container is similar to a folder in Windows or a directory in UNIX. The + primary difference between a container and these other file system concepts + is that containers cannot be nested. + + cdn + A system of distributed servers (network) that delivers web pages and other + web content to a user based on the geographic locations of the user, the + origin of the web page, and a content delivery server. + + metadata + Optional information that you can assign to Cloud Files accounts, + containers, and objects through the use of a metadata header. + + object + An object (sometimes referred to as a file) is the unit of storage in an + Object Store. An object is a combination of content (data) and metadata. diff --git a/doc/services/object-store/Migrating.md.storage.rst b/doc/services/object-store/migrating-containers.rst similarity index 82% rename from doc/services/object-store/Migrating.md.storage.rst rename to doc/services/object-store/migrating-containers.rst index 99738554b..22adb839b 100644 --- a/doc/services/object-store/Migrating.md.storage.rst +++ b/doc/services/object-store/migrating-containers.rst @@ -1,8 +1,5 @@ -Migrating containers (across regions) -===================================== - -Introduction ------------- +Migrating containers across regions +=================================== Currently, there exists no single API operation to copy containers across geographic endpoints. Although the API offers a ``COPY`` @@ -12,6 +9,7 @@ copying. The SDK, however, does offer this functionality. You **will** be charged for bandwidth between regions, so it's advisable to use ServiceNet where possible (which is free). + Requirements ------------ @@ -20,71 +18,84 @@ Requirements requests to be batched for greater efficiency). You can do this by running: -.. code:: bash +.. code-block:: bash - php composer.phar install --dev + composer require guzzle/guzzle - Depending on the size and number of transfer items, you will need to raise PHP's memory limit: -.. code:: php +.. code-block:: php - ini_set('memory_limit', '512M'); + ini_set('memory_limit', '512M'); - You will need to enact some kind of backoff/retry strategy for rate limits. Guzzle comes with a convenient feature that just needs to be added as a normal subscriber: -.. code:: php +.. code-block:: php use Guzzle\Plugin\Backoff\BackoffPlugin; - $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + // set timeout in secs + $timeout = 10; + + // set HTTP error codes + $httpErrors = array(500, 503, 408); + + $backoffPlugin = BackoffPlugin::getExponentialBackoff($timeout, $httpErrors); + $client->addSubscriber($backoffPlugin); + This tells the client to retry up to ``10`` times for failed requests have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + Setup ----- You can access all this functionality by executing: -.. code:: php +.. code-block:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); - $ordService = $client->objectStoreService('cloudFiles', 'ORD'); - $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); - $oldContainer = $ordService->getContainer('old_container'); - $newContainer = $iadService->getContainer('new_container'); + $iadService->migrateContainer($oldContainer, $newContainer); - $iadService->migrateContainer($oldContainer, $newContainer); It's advisable to do this process in a Cloud Server in one of the two regions you're migrating to/from. This allows you to use ``privateURL`` as the third argument in the ``objectStoreService`` methods like this: -.. code:: php +.. code-block:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); - $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); This will ensure that traffic between your server and your new IAD container will be held over the internal Rackspace network which is free. + Options ------- You can pass in an array of arguments to the method: -.. code:: php +.. code-block:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); - $options = array( - 'read.batchLimit' => 100, - 'read.pageLimit' => 100, - 'write.batchLimit' => 50 - ); + $iadService->migrateContainer($oldContainer, $newContainer, $options); - $iadService->migrateContainer($oldContainer, $newContainer, $options); Options explained ~~~~~~~~~~~~~~~~~ @@ -98,4 +109,3 @@ Options explained +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ | ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ - diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst new file mode 100644 index 000000000..e38e0eb8b --- /dev/null +++ b/doc/services/object-store/objects.rst @@ -0,0 +1,427 @@ +Objects +======= + +Setup +----- + +In order to interact with this feature, you must first retrieve a particular +container using its unique name: + +.. code-block:: php + + $container = $service->getContainer('{containerName}'); + + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + +.. note:: + + Unlike previous versions, you do not need to manually specify your object's + content type. The API will do this for you. + +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in. + +Upload a single file (under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The simplest way to upload a local object, without additional metadata, is by +its path: + +.. code-block:: php + + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+')); + + +The resource handle will be automatically closed by Guzzle in its destructor, +so there is no need to execute ``fclose``. + + +Upload a single file (under 5GB) with metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although the previous section handles most use cases, there are times when you +want greater control over what is being uploaded. For example, you might want +to control the object's metadata, or supply additional HTTP headers to coerce +browsers to handle the download a certain way. To add metadata to a new object: + +.. code-block:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + // specify optional metadata + $metadata = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow', + ); + + // specify optional HTTP headers + $httpHeaders = array( + 'Content-Type' => 'application/json', + ); + + // merge the two + $allHeaders = array_merge(DataObject::stockHeaders($metadata), $httpHeaders); + + // upload as usual + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+'), $allHeaders); + + +As you will notice, the first argument to ``uploadObject`` is the remote object +name, i.e. the name it will be uploaded as. The second argument is either a +file handle resource, or a string representation of object content (a temporary +resource will be created in memory), and the third is an array of additional +headers. + + +Batch upload multiple files (each under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + + +Upload large files (over 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build and execute your +transfer. For your convenience, the Container resource object contains a simple +method to do this heavy lifting for you: + +.. code-block:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/user/video.mov', + 'metadata' => array('Author' => 'Jamie'), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifestfile is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. +To find out more details, please consult the `official documentation +`_. + + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code-block:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code-block:: php + + $container->objectList(array('prefix' => 'logFile_')); + + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code-block:: php + + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + $file = $container->getObject('summer_vacation.mp4'); + +Once you have access to this ``OpenCloud\ObjectStore\Resource\DataObject`` +object, you can access these attributes: + +Get object's parent container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file name +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file size +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $size int */ + $size = $object->getContentLength(); + + +Get content of file +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $content Guzzle\Http\EntityBody */ + $content = $object->getContainer(); + + +Get type of file +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $type string */ + $type = $object->getContentType(); + + +Get file checksum +~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $etag string */ + $etag = $object->getEtag(); + + +Get last modified date of file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $lastModified string */ + $lastModified = $object->getLastModified(); + + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code-block:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code-block:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + + +Update an existing object +------------------------- + +.. code-block:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + + +Copy object to new location +--------------------------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code-block:: php + + $object->copy('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container that does not exist yet. + + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code-block:: php + + $container->getPartialObject('summer_vacation.mp4'); + + +In order to access the metadata on a partial or complete object, use: + +.. code-block:: php + + $object->getMetadata(); + + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code-block:: php + + $object->refresh(); + + +You can also update to get the latest metadata: + +.. code-block:: php + + $object->retrieveMetadata(); + + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code-block:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code-block:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code-block:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code-block:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + + +Delete object +------------- + +.. code-block:: php + + $object->delete(); + + +Delete multiple objects +----------------------- + +Bulk delete a set of paths: + +.. code-block:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); diff --git a/doc/services/object-store/rs-only.rst b/doc/services/object-store/rs-only.rst new file mode 100644 index 000000000..fab4e423f --- /dev/null +++ b/doc/services/object-store/rs-only.rst @@ -0,0 +1,3 @@ +.. note:: + + This feature is only available to Rackspace users. From 4719586d4252ff88f0c070d6da68c4963acd5b60 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:57 +0100 Subject: [PATCH 435/835] Modify toctree --- doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 45c453a40..9c06b86e8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,7 +10,7 @@ Contents: .. toctree:: :glob: - :maxdepth: 2 + :maxdepth: 1 services/**/index From 955a072a08ca4536efe47811dffa5339f168e79e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 17:58:16 +0100 Subject: [PATCH 436/835] Add orchestration docs --- doc/services/orchestration/README.md.rst | 98 --- doc/services/orchestration/USERGUIDE.md.rst | 672 ------------------ doc/services/orchestration/build-info.rst | 15 + doc/services/orchestration/events.rst | 53 ++ doc/services/orchestration/index.rst | 50 ++ doc/services/orchestration/resource-types.rst | 48 ++ doc/services/orchestration/resources.rst | 50 ++ doc/services/orchestration/stacks.rst | 299 ++++++++ doc/services/orchestration/templates.rst | 55 ++ 9 files changed, 570 insertions(+), 770 deletions(-) delete mode 100644 doc/services/orchestration/README.md.rst delete mode 100644 doc/services/orchestration/USERGUIDE.md.rst create mode 100644 doc/services/orchestration/build-info.rst create mode 100644 doc/services/orchestration/events.rst create mode 100644 doc/services/orchestration/resource-types.rst create mode 100644 doc/services/orchestration/resources.rst create mode 100644 doc/services/orchestration/stacks.rst create mode 100644 doc/services/orchestration/templates.rst diff --git a/doc/services/orchestration/README.md.rst b/doc/services/orchestration/README.md.rst deleted file mode 100644 index 1a983d187..000000000 --- a/doc/services/orchestration/README.md.rst +++ /dev/null @@ -1,98 +0,0 @@ -Orchestration -============= - -**Orchestration** is a service that can be used to create and manage -cloud resources. Examples of such resources are databases, load -balancers, servers and software installed on them. - -Concepts --------- - -To use the Orchestration service effectively, you should understand -several key concepts: - -- **Template**: An Orchestration template is a JSON or YAML document - that describes how a set of resources should be assembled to produce - a working deployment. The template specifies what resources should be - used, what attributes of these resources are parameterized and what - information is output to the user when a template is instantiated. - -- **Resource**: A resource is a template artifact that represents some - component of your desired architecture (a Cloud Server, a group of - scaled Cloud Servers, a load balancer, some configuration management - system, and so forth). - -- **Stack**: A stack is a running instance of a template. When a stack - is created, the resources specified in the template are created. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the Orchestration service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Orchestration service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All Orchestration operations are done via an *orchestration service -object*. To instantiate this object, call the ``orchestrationService`` -method on the ``$client`` object as shown in the following example: - -.. code:: php - - $region = ''; - $orchestrationService = $client->orchestrationService(null, $region); - -Any stacks and resources created with this ``$orchestrationService`` -instance will be stored in the cloud region specified by ``$region``. - -3. Create a stack from a template. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -Once you have created a stack, there is more you can do with it. See -`complete user guide for orchestration `__. diff --git a/doc/services/orchestration/USERGUIDE.md.rst b/doc/services/orchestration/USERGUIDE.md.rst deleted file mode 100644 index 98bd51db7..000000000 --- a/doc/services/orchestration/USERGUIDE.md.rst +++ /dev/null @@ -1,672 +0,0 @@ -Complete User Guide for the Orchestration Service -================================================= - -Orchestration is a service that you can use to create and manage cloud -resources such as databases, load balancers, and servers, and the -software installed on servers. - -Table of Contents ------------------ - -- `Concepts <#concepts>`__ -- `Prerequisites <#prerequisites>`__ -- `Client <#client>`__ -- `Orchestration service <#orchestration-service>`__ -- `Templates <#templates>`__ -- `Validate template <#validate-template>`__ - - - `Validate a template from a - file <#validate-a-template-from-a-file>`__ - - `Validate Template from URL <#validate-template-from-url>`__ - -- `Stacks <#stacks>`__ -- `Preview stack <#preview-stack>`__ - - - `Preview a stack from a template - file <#preview-a-stack-from-a-template-file>`__ - - `Preview a stack from a template - URL <#preview-a-stack-from-a-template-url>`__ - -- `Create stack <#create-stack>`__ - - - `Create a stack from a template - file <#create-a-stack-from-a-template-file>`__ - - `Create a stack from a template - URL <#create-a-stack-from-a-template-url>`__ - -- `List stacks <#list-stacks>`__ -- `Get stack <#get-stack>`__ -- `Get stack template <#get-stack-template>`__ -- `Update stack <#update-stack>`__ - - - `Update a stack from a template - file <#update-a-stack-from-a-template-file>`__ - - `Update Stack from Template - URL <#update-stack-from-template-url>`__ - -- `Delete stack <#delete-stack>`__ -- `Abandon Stack <#abandon-stack>`__ -- `Adopt stack <#adopt-stack>`__ -- `Stack resources <#stack-resources>`__ -- `List stack resources <#list-stack-resources>`__ -- `Get stack resource <#get-stack-resource>`__ -- `Get stack resource metadata <#get-stack-resource-metadata>`__ -- `Stack resource events <#stack-resource-events>`__ -- `List stack events <#list-stack-events>`__ -- `List stack resource events <#list-stack-resource-events>`__ -- `Get stack resource event <#get-stack-resource-event>`__ -- `Resource types <#resource-types>`__ -- `List resource types <#list-resource-types>`__ -- `Get resource type <#get-resource-type>`__ -- `Get resource type template <#get-resource-type-template>`__ -- `Build info <#build-info>`__ -- `Get build info <#get-build-info>`__ - -Concepts --------- - -To use the Orchestration service effectively, you should understand the -following key concepts: - -- **Template**: A JSON or YAML document that describes how a set of - resources should be assembled to produce a working deployment. The - template specifies the resources to use, the attributes of these - resources that are parameterized and the information that is sent to - the user when a template is instantiated. - -- **Resource**: Some component of your architecture (a cloud server, a - group of scaled cloud servers, a load balancer, some configuration - management system, and so on) that is defined in a template. - -- **Stack**: A running instance of a template. When a stack is created, - the resources specified in the template are created. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the Orchestration service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Orchestration service -~~~~~~~~~~~~~~~~~~~~~ - -All Orchestration operations are done via an *orchestration service -object*. To instantiate this object, call the ``orchestrationService`` -method on the ``$client`` object. This method takes two arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+==========================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ - -.. code:: php - - $region = ''; - $orchestrationService = $client->orchestrationService(null, $region); - -Any stacks and resources created with this ``$orchestrationService`` -instance will be stored in the cloud region specified by ``$region``. - -Templates ---------- - -An Orchestration template is a JSON or YAML document that describes how -a set of resources should be assembled to produce a working deployment -(known as a `stack <#stacks>`__). The template specifies the resources -to use, the attributes of these resources that are parameterized and the -information that is sent to the user when a template is instantiated. - -Validate template -~~~~~~~~~~~~~~~~~ - -Before you use a template to create a stack, you might want to validate -it. - -Validate a template from a file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can validate it as shown in the following example: - -.. code:: php - - use OpenCloud\Common\Exceptions\InvalidTemplateError; - - try { - $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') - )); - } catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid - } - -[ `Get the executable PHP script for this -example `__ -] - -Validate Template from URL -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can validate it as shown in the -following example: - -.. code:: php - - use OpenCloud\Common\Exceptions\InvalidTemplateError; - - try { - $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' - )); - } catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid - } - -[ `Get the executable PHP script for this -example `__ -] - -Stacks ------- - -A stack is a running instance of a template. When a stack is created, -the `resources <#stack-resources>`__ specified in the template are -created. - -Preview stack -~~~~~~~~~~~~~ - -Before you create a stack from a template, you might want to see what -that stack will look like. This is called *previewing the stack*. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Preview a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to preview a stack as shown in the following -example: - -.. code:: php - - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Preview a stack from a template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to preview a stack as shown -in the following example: - -.. code:: php - - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Create stack -~~~~~~~~~~~~ - -You can create a stack from a template. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Create a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to create a stack as shown in the following -example: - -.. code:: php - - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Create a stack from a template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to create a stack as shown -in the following example: - -.. code:: php - - $stack = $orchestrationService->stack(); - $stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -[ `Get the executable PHP script for this -example `__ ] - -List stacks -~~~~~~~~~~~ - -You can list all the stacks that you have created as shown in the -following example: - -.. code:: php - - $stacks = $orchestrationService->listStacks(); - foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack -~~~~~~~~~ - -You can retrieve a specific stack using its name, as shown in the -following example: - -.. code:: php - - $stack = $orchestrationService->getStack('simple-lamp-setup'); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get stack template -~~~~~~~~~~~~~~~~~~ - -You can retrieve the template used to create a stack. Note that a JSON -string is returned, regardless of whether a JSON or YAML template was -used to create the stack. - -.. code:: php - - $stackTemplate = $stack->getTemplate(); - /** @var $stackTemplate string **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update stack -~~~~~~~~~~~~ - -You can update a running stack. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ - -Update a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to update a stack as shown in the following -example: - -.. code:: php - - $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Update Stack from Template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to update a stack as shown -in the following example: - -.. code:: php - - $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Delete stack -~~~~~~~~~~~~ - -If you no longer need a stack and all its resources, you can delete the -stack *and* the resources as shown in the following example: - -.. code:: php - - $stack->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Abandon Stack -~~~~~~~~~~~~~ - -If you want to delete a stack but preserve all its resources, you can -abandon the stack as shown in the following example: - -.. code:: php - - $abandonStackData = $stack->abandon(); - /** @var $abandonStackData string **/ - - file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); - -[ `Get the executable PHP script for this -example `__ ] - -Note that this operation returns data about the abandoned stack as a -string. You can use this data to recreate the stack by using the `adopt -stack <#adopt-stack>`__ operation. - -Adopt stack -~~~~~~~~~~~ - -If you have data from an abandoned stack, you can re-create the stack as -shown in the following example: - -.. code:: php - - $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Stack resources ---------------- - -A stack is made up of zero or more resources such as databases, load -balancers, and servers, and the software installed on servers. - -List stack resources -~~~~~~~~~~~~~~~~~~~~ - -You can list all the resources for a stack as shown in the following -example: - -.. code:: php - - $resources = $stack->listResources(); - foreach ($resources as $resource) { - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource -~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource in a stack bt using that resource's -name, as shown in the following example: - -.. code:: php - - $resource = $stack->getResource('load-balancer'); - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve the metadata for a specific resource in a stack as -shown in the following example: - -.. code:: php - - $resourceMetadata = $resource->getMetadata(); - /** @var $resourceMetadata \stdClass **/ - -[ `Get the executable PHP script for this -example `__ ] - -Stack resource events ---------------------- - -Operations on resources within a stack (such as the creation of a -resource) produce events. - -List stack events -~~~~~~~~~~~~~~~~~ - -You can list all of the events for all of the resources in a stack as -shown in the following example: - -.. code:: php - - $stackEvents = $stack->listEvents(); - foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List stack resource events -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all of the events for a specific resource in a stack as -shown in the following example: - -.. code:: php - - $resourceEvents = $resource->listEvents(); - foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource event -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific event for a specific resource in a stack, by -using the resource event's ID, as shown in the following example: - -.. code:: php - - $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ - -[ `Get the executable PHP script for this -example `__ ] - -Resource types --------------- - -When you define a template, you must use resource types supported by -your cloud. - -List resource types -~~~~~~~~~~~~~~~~~~~ - -You can list all supported resource types as shown in the following -example: - -.. code:: php - - $resourceTypes = $orchestrationService->listResourceTypes(); - foreach ($resourceTypes as $resourceType) { - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get resource type -~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource type's schema as shown in the -following example: - -.. code:: php - - $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get resource type template -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource type's representation as it would -appear in a template, as shown in the following example: - -.. code:: php - - $resourceTypeTemplate = $resourceType->getTemplate(); - /** @var $resourceTypeTemplate string **/ - -[ `Get the executable PHP script for this -example `__ ] - -Build info ----------- - -Get build info -~~~~~~~~~~~~~~ - -You can retrieve information about the current Orchestration service -build as shown in the following example: - -.. code:: php - - $buildInfo = $orchestrationService->getBuildInfo(); - /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ - -[ `Get the executable PHP script for this -example `__ ] diff --git a/doc/services/orchestration/build-info.rst b/doc/services/orchestration/build-info.rst new file mode 100644 index 000000000..c30ce5cfa --- /dev/null +++ b/doc/services/orchestration/build-info.rst @@ -0,0 +1,15 @@ +Build info +========== + +Get build info +-------------- + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + $buildInfo = $orchestrationService->getBuildInfo(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/events.rst b/doc/services/orchestration/events.rst new file mode 100644 index 000000000..68dbf611b --- /dev/null +++ b/doc/services/orchestration/events.rst @@ -0,0 +1,53 @@ +Stack resource events +===================== + +Operations on resources within a stack (such as the creation of a +resource) produce events. + + +List stack events +----------------- + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code-block:: php + + $stackEvents = $stack->listEvents(); + + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +List stack resource events +-------------------------- + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + $resourceEvents = $resource->listEvents(); + + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource event +------------------------ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code-block:: php + + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index e69de29bb..0dce8ac43 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -0,0 +1,50 @@ +Orchestration v1 +================ + +Setup +----- + +.. include:: rs-client.sample.rst + +Now set up the Orchestration service: + +.. code-block:: php + + $service = $client->orchestrationService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + templates + stacks + resources + resource-types + build-info + + +Glossary +-------- + +.. glossary:: + + template + An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + + resource + A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + + stack + A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. diff --git a/doc/services/orchestration/resource-types.rst b/doc/services/orchestration/resource-types.rst new file mode 100644 index 000000000..cd229d660 --- /dev/null +++ b/doc/services/orchestration/resource-types.rst @@ -0,0 +1,48 @@ +Resource types +============== + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +------------------- + +You can list all supported resource types as shown in the following +example: + +.. code-block:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +`Get the executable PHP script for this example `_ + + +Get resource type +----------------- + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + +`Get the executable PHP script for this example `_ + + +Get resource type template +-------------------------- + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code-block:: php + + /** @var $resourceTypeTemplate string **/ + $resourceTypeTemplate = $resourceType->getTemplate(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resources.rst b/doc/services/orchestration/resources.rst new file mode 100644 index 000000000..049150f58 --- /dev/null +++ b/doc/services/orchestration/resources.rst @@ -0,0 +1,50 @@ +Stack resources +=============== + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + + +List stack resources +-------------------- + +You can list all the resources for a stack as shown in the following +example: + +.. code-block:: php + + $resources = $stack->listResources(); + + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource +------------------ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code-block:: php + + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + $resource = $stack->getResource('load-balancer'); + +`Get the executable PHP script for this example `_ + + +Get stack resource metadata +--------------------------- + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + /** @var $resourceMetadata \stdClass **/ + $resourceMetadata = $resource->getMetadata(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst b/doc/services/orchestration/stacks.rst new file mode 100644 index 000000000..75a14cd4c --- /dev/null +++ b/doc/services/orchestration/stacks.rst @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/templates.rst b/doc/services/orchestration/templates.rst new file mode 100644 index 000000000..5eca2519d --- /dev/null +++ b/doc/services/orchestration/templates.rst @@ -0,0 +1,55 @@ +Templates +========= + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validating templates +-------------------- + +Before you use a template to create a stack, you might want to validate it. + + +Validate a template from a file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ + +Validate Template from URL +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ From c107488f452310ad6a9ffe45b9d3439a0ac375e2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 19:12:57 +0100 Subject: [PATCH 437/835] Add Queues docs --- doc/services/queues/Queue.md.rst | 197 ------------- .../queues/{Claim.md.rst => claims.rst} | 115 +++----- doc/services/queues/index.rst | 48 +++ .../queues/{Message.md.rst => messages.rst} | 274 +++++++----------- doc/services/queues/queues.rst | 124 ++++++++ 5 files changed, 316 insertions(+), 442 deletions(-) delete mode 100644 doc/services/queues/Queue.md.rst rename doc/services/queues/{Claim.md.rst => claims.rst} (68%) rename doc/services/queues/{Message.md.rst => messages.rst} (52%) create mode 100644 doc/services/queues/queues.rst diff --git a/doc/services/queues/Queue.md.rst b/doc/services/queues/Queue.md.rst deleted file mode 100644 index 1fe9f7036..000000000 --- a/doc/services/queues/Queue.md.rst +++ /dev/null @@ -1,197 +0,0 @@ -1. Introduction ---------------- - -A Queue is an entity that holds messages. Ideally, a queue is created -per work type. For example, if you want to compress files, you would -create a queue dedicated to this job. Any application that reads from -this queue would only compress files. - -2. Setup --------- - -.. code:: php - - $service = $client->queuesService('cloudQueues', 'ORD'); - -3. Client IDs -------------- - -With most of Marconi's operation, you must specify a **Client ID** which -will be used as a unique identifier for the process accessing this -Queue. This is basically a UUID that must be unique to each client -accessing the API - it can be an arbitrary string. - -.. code:: php - - $service->setClientId(); - - echo $service->getClientId(); - -If you call ``setClientId`` without any parameters, a UUID is -automatically generated for you. - -4. List queues --------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation lists queues for the project. The queues are sorted -alphabetically by name. - -4.2 Parameters -~~~~~~~~~~~~~~ - -\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| -\|marker\|Query\|​String\|Specifies the name of the last queue received -in a previous request, or none to get the first page of results. -Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to -return. The default value for the number of queues returned is 10. If -you do not specify this parameter, the default number of queues is -returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether -queue metadata is included in the response. The default value for this -parameter is false, which excludes the metadata. Optional.\| -\|----\|-----\|----\|-----------\| - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queues = $service->listQueues(); - - while ($queue = $queues->next()) { - echo $queue->getName() . PHP_EOL; - } - -5. Create queue ---------------- - -5.1 Description -~~~~~~~~~~~~~~~ - -This operation creates a new queue. - -5.2 Parameters -~~~~~~~~~~~~~~ - -A string representation of the name for your new Queue. The name must -not exceed 64 bytes in length, and it is limited to US-ASCII letters, -digits, underscores, and hyphens. - -5.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue = $service->createQueue('new_queue'); - -6. Retrieve queue ------------------ - -6.1 Description -~~~~~~~~~~~~~~~ - -Returns a ``Queue`` object for use. - -6.2 Parameters -~~~~~~~~~~~~~~ - -Queue name. - -6.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue = $service->getQueue('new_queue'); - -7. Check queue existence ------------------------- - -7.1 Description -~~~~~~~~~~~~~~~ - -This operation verifies whether the specified queue exists by returning -``TRUE`` or ``FALSE``. - -7.2 Parameters -~~~~~~~~~~~~~~ - -7.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - if ($service->hasQueue('new_queue')) { - // do something - } - -8. Update queue metadata (permanently to the API) -------------------------------------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation replaces any existing metadata document in its entirety. -Ensure that you do not accidentally overwrite existing metadata that you -want to retain. If you want to *append* metadata, ensure you merge a new -array to the existing values. - -4.2 Parameters -~~~~~~~~~~~~~~ - -Hash of key pairs. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue->saveMetadata(array( - 'foo' => 'bar' - )); - -9. Retrieve the queue metadata (fresh from the API) ---------------------------------------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation returns metadata, such as message TTL, for the queue. - -4.2 Parameters -~~~~~~~~~~~~~~ - -None. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadata = $queue->retrieveMetadata(); - - print_r($metadata->toArray()); - -10. Get queue stats -------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation returns queue statistics, including how many messages are -in the queue, categorized by status. - -4.2 Parameters -~~~~~~~~~~~~~~ - -None. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue->getStats(); - diff --git a/doc/services/queues/Claim.md.rst b/doc/services/queues/claims.rst similarity index 68% rename from doc/services/queues/Claim.md.rst rename to doc/services/queues/claims.rst index 42161536d..7e375c8d3 100644 --- a/doc/services/queues/Claim.md.rst +++ b/doc/services/queues/claims.rst @@ -1,28 +1,18 @@ -1. Introduction ---------------- +Claims +====== -A **Claim** is the process of a worker checking out a message to perform -a task. Claiming a message prevents other workers from attempting to -process the same messages. +Setup +----- -2. Setup --------- +In order to work with messages, you must first retrieve a queue by its name: -A Claim is initialized on its parent object, a Queue: +.. code-block:: php -.. code:: php + $queue = $service->getQueue('{queueName}'); - // To initialize an empty object: - $claim = $queue->getClaim(); - // or retrieve a specific claim: - $claim = $queue->getClaim('51db7067821e727dc24df754'); - -3. Claim messages ------------------ - -3.1 Description -~~~~~~~~~~~~~~~ +Claim messages +-------------- This operation claims a set of messages (up to the value of the limit parameter) from oldest to newest and skips any messages that are already @@ -46,8 +36,8 @@ and whether a given message's claim is about to expire. When a claim expires, it is released. If the original worker failed to process the message, another client worker can then claim the message. -3.2 Attributes -~~~~~~~~~~~~~~ +Parameters +~~~~~~~~~~ The ``ttl`` attribute specifies how long the server waits before releasing the claim. The ttl value must be between 60 and 43200 seconds @@ -67,46 +57,30 @@ The ``limit`` attribute specifies the number of messages to return, up to 20 messages. If limit is not specified, limit defaults to 10. The limit parameter is optional. -3.3 Code -~~~~~~~~ +.. code-block:: php -.. code:: php + use OpenCloud\Common\Constants\Datetime; - use OpenCloud\Common\Constants\Datetime; + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); - $queue->claimMessages(array( - 'limit' => 15, - 'grace' => 5 * Datetime::MINUTE, - 'ttl' => 5 * Datetime::MINUTE - )); - -4. Query claim --------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation queries the specified claim for the specified queue. -Claims with malformed IDs or claims that are not found by ID are -ignored. -4.2 Attributes -~~~~~~~~~~~~~~ +Query claim +----------- -Claim ID. +This operation queries the specified claim for the specified queue. Claims with +malformed IDs or claims that are not found by ID are ignored. -4.3 Code -~~~~~~~~ +.. code-block:: php -.. code:: php + $claim = $queue->getClaim('{claimId}'); - $claim = $queue->getClaim('51db7067821e727dc24df754'); -5. Update claim ---------------- - -5.1 Description -~~~~~~~~~~~~~~~ +Update claim +------------ This operation updates the specified claim for the specified queue. Claims with malformed IDs or claims that are not found by ID are @@ -118,27 +92,17 @@ renew a claim by executing this method on a specific **Claim** and including a new TTL. The API will then reset the age of the claim and apply the new TTL. -5.2 Attributes -~~~~~~~~~~~~~~ - -See section 4.2. - -5.3 Code -~~~~~~~~ - -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $claim->update(array( - 'ttl' => 10 * Datetime::MINUTE - )); + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); -6. Release claim ----------------- -6.1 Description -~~~~~~~~~~~~~~~ +Release claim +------------- This operation immediately releases a claim, making any remaining undeleted messages that are associated with the claim available to other @@ -150,15 +114,6 @@ shutdown, fails to process one or more messages, or is taking longer than expected to process messages, and wants to make the remainder of the messages available to other workers. -6.2 Attributes -~~~~~~~~~~~~~~ - -See section 4.2. - -6.3 Code -~~~~~~~~ - -.. code:: php - - $message->delete(); +.. code-block:: php + $message->delete(); diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index e69de29bb..e37845d76 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -0,0 +1,48 @@ +Queues v1 +========= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now to instantiate the Queues service: + +.. code-block:: php + + $service = $client->queuesService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + queues + messages + claims + + +Glossary +-------- + +.. glossary:: + + claim + A Claim is the process of a worker checking out a message to perform + a task. Claiming a message prevents other workers from attempting to + process the same messages. + + queue + A Queue is an entity that holds messages. Ideally, a queue is created + per work type. For example, if you want to compress files, you would + create a queue dedicated to this job. Any application that reads from + this queue would only compress files. + + message + A Message is a task, a notification, or any meaningful data that a + producer or publisher sends to the queue. A message exists until it is + deleted by a recipient or automatically by the system based on a TTL + (time-to-live) value. diff --git a/doc/services/queues/Message.md.rst b/doc/services/queues/messages.rst similarity index 52% rename from doc/services/queues/Message.md.rst rename to doc/services/queues/messages.rst index ca67e22bb..ea23caea3 100644 --- a/doc/services/queues/Message.md.rst +++ b/doc/services/queues/messages.rst @@ -1,29 +1,18 @@ -1. Introduction ---------------- +Messages +======== -A **Message** is a task, a notification, or any meaningful data that a -producer or publisher sends to the queue. A message exists until it is -deleted by a recipient or automatically by the system based on a TTL -(time-to-live) value. +Setup +----- -2. Setup --------- +In order to work with messages, you must first retrieve a queue by its name: -A message is initialized from its parent object, a Queue: +.. code-block:: php -.. code:: php + $queue = $service->getQueue('{queueName}'); - // Setup an empty object - $message = $queue->getMessage(); - // or retrieve an existing one - $message = $queue->getMessage(''); - -3. Post message ---------------- - -3.1 Description -~~~~~~~~~~~~~~~ +Post new message +---------------- This operation posts the specified message or messages. You can submit up to 10 messages in a single request. @@ -31,73 +20,67 @@ up to 10 messages in a single request. When posting new messages, you specify only the ``body`` and ``ttl`` for the message. The API will insert metadata, such as ID and age. -3.2 Parameters -~~~~~~~~~~~~~~ - How you pass through the array structure depends on whether you are -executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the -same. +executing multiple or single posts, but the keys are the +same: -The ``body`` attribute specifies an arbitrary document that constitutes -the body of the message being sent. The size of this body is limited to -256 KB, excluding whitespace. +* The ``body`` attribute specifies an arbitrary document that constitutes + the body of the message being sent. The size of this body is limited to + 256 KB, excluding whitespace. -The ``ttl`` attribute specifies how long the server waits before marking -the message as expired and removing it from the queue. The value of ttl -must be between 60 and 1209600 seconds (14 days). Note that the server -might not actually delete the message until its age has reached up to -(ttl + 60) seconds, to allow for flexibility in storage implementations. +* The ``ttl`` attribute specifies how long the server waits before marking + the message as expired and removing it from the queue. The value of ttl + must be between 60 and 1209600 seconds (14 days). Note that the server + might not actually delete the message until its age has reached up to + (ttl + 60) seconds, to allow for flexibility in storage implementations. -3.3 Code samples -~~~~~~~~~~~~~~~~ -3.3.1 Posting a single message -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Posting a single message +~~~~~~~~~~~~~~~~~~~~~~~~ -:: +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $queue->createMessage(array( - 'body' => (object) array( - 'event' => 'BackupStarted', - 'deadline' => '26.12.2013 - ), - 'ttl' => 2 * Datetime::DAY - )); + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013', + ), + 'ttl' => 2 * Datetime::DAY + )); -3.3.2 Post a batch of messages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Post a batch of messages +~~~~~~~~~~~~~~~~~~~~~~~~ Please note that the list of messages will be truncated at 10. For more, please execute another method call. -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $messages = array( - array( - 'body' => (object) array( - 'play' => 'football' - ), - 'ttl' => 2 * Datetime::DAY - ), - array( - 'body' => (object) array( - 'play' => 'tennis' - ), - 'ttl' => 50 * Datetime::HOUR - ) - ); + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); - $queue->createMessages($messages); + $queue->createMessages($messages); -4. Get messages ---------------- -4.1 Description -~~~~~~~~~~~~~~~ +Get messages +------------ This operation gets the message or messages in the specified queue. @@ -109,10 +92,11 @@ variety of storage driver implementations. Results are ordered by age, oldest message first. -4.2 Parameters -~~~~~~~~~~~~~~ -A hash of options. +Parameters +~~~~~~~~~~ + +When retrieving messages, you can filter using these options: +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Name | Style | Type | Description | @@ -123,135 +107,95 @@ A hash of options. +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | +| include_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $messages = $queue->listMessages(array( - 'marker' => '51db6f78c508f17ddc924357', - 'limit' => 20, - 'echo' => true - )); + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); - while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; - } + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } -5. Get a set of messages by ID ------------------------------- -5.1 Description -~~~~~~~~~~~~~~~ +Get a set of messages by ID +--------------------------- This operation provides a more efficient way to query multiple messages compared to using a series of individual GET. Note that the list of IDs cannot exceed 20. If a malformed ID or a nonexistent message ID is provided, it is ignored, and the remaining messages are returned. -5.2 Parameters -~~~~~~~~~~~~~~ - -A hash of options. +Parameters +~~~~~~~~~~ -\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| -\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format -multiple message ID values by separating them with commas -(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies -the claim ID with which the message is associated. Optional.\| -\|----\|-----\|----\|-----------\| ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++============+=========+============+========================================================================================================================================+ +| ids | Query | String | Specifies the IDs of the messages to get. Format multiple message ID values by separating them with commas (comma-separated). Optional | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| claim_id | Query | ​Boolean | Specifies the claim ID with which the message is associated. Optional. | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ -5.3 Code sample -~~~~~~~~~~~~~~~ -.. code:: php +.. code-block:: php - $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + $ids = array('id_1', 'id_2'); - $messages = $queue->listMessages(array('ids' => $ids)); + $messages = $queue->listMessages(array('ids' => $ids)); - while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; - } + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } -6. Delete a set of messages by ID ---------------------------------- -6.1 Description -~~~~~~~~~~~~~~~ +Delete a set of messages by ID +------------------------------ This operation immediately deletes the specified messages. If any of the message IDs are malformed or non-existent, they are ignored. The remaining valid messages IDs are deleted. -6.2 Parameters -~~~~~~~~~~~~~~ - -An array of IDs. - -6.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + $ids = array('id_1', 'id_2'); + $response = $queue->deleteMessages($ids); - $response = $queue->deleteMessages($ids); -7. Get a specific message -------------------------- - -7.1 Description -~~~~~~~~~~~~~~~ +Get a specific message +---------------------- This operation gets the specified message from the specified queue. -7.2 Parameters -~~~~~~~~~~~~~~ - -Message ID. - -7.3 Object properties -~~~~~~~~~~~~~~~~~~~~~ - -``href`` is an opaque relative URI that the client can use to uniquely -identify a message resource and interact with it. - -``ttl`` is the TTL that was set on the message when it was posted. The -message expires after (ttl - age) seconds. - -``age`` is the number of seconds relative to the server's clock. - -``body`` is the arbitrary document that was submitted with the original -request to post the message. - -7.4 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $message = $queue->getMessage('51db6f78c508f17ddc924357'); - -8. Delete message ------------------ +.. code-block:: php -8.1 Description -~~~~~~~~~~~~~~~ + /** @var $message OpenCloud\Queues\Message */ + $message = $queue->getMessage('{messageId}'); -This operation immediately deletes the specified message. -8.2 Parameters -~~~~~~~~~~~~~~ +Once you have access to the ``Message`` object, you access its attributes: -None. ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| attribute | method | description | ++===========+=============+==============================================================================================================+ +| href | ``getHref`` | An opaque relative URI that the client can use to uniquely identify a message resource and interact with it. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| ttl | ``getTtl`` | The TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| age | ``getAge`` | The number of seconds relative to the server's clock. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| body | ``getBody`` | The arbitrary document that was submitted with the original request to post the message. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ -8.3 Code sample -~~~~~~~~~~~~~~~ -.. code:: php +Delete message +-------------- - $message->delete(); +.. code-block:: php + $message->delete(); diff --git a/doc/services/queues/queues.rst b/doc/services/queues/queues.rst new file mode 100644 index 000000000..7958adfae --- /dev/null +++ b/doc/services/queues/queues.rst @@ -0,0 +1,124 @@ +Queues +====== + +A note on Client IDs +-------------------- + +For most of the operations in Cloud Queues, you must specify a **Client ID** +which will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code-block:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + + +List queues +----------- + +This operation lists queues for the project. The queues are sorted alphabetically by name. + +.. code-block:: php + + $queues = $service->listQueues(); + + foreach ($queues as $queue) { + echo $queue->getName() , PHP_EOL; + } + + +Filtering lists +~~~~~~~~~~~~~~~ + +You can also filter collections using the following query parameters: + ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++==========+=======+=========+================================================================================================================================================================================================+ +| marker | Query | ​String | Specifies the name of the last queue received in a previous request, or none to get the first page of results. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | Specifies the number of queues to return. The default value for the number of queues returned is 10. If you do not specify this parameter, the default number of queues is returned. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| detailed | Query | ​Boolean | Determines whether queue metadata is included in the response. The default value for this parameter is false, which excludes the metadata. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. code-block:: php + + $queues = $service->listQueues(array('detailed' => false)); + + +Create queue +------------ + +The only parameter required is the name of the queue you're creating. The name +must not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +.. code-block:: php + + $queue = $service->createQueue('new_queue'); + + +Find queue details +------------------ + +.. code-block:: php + + /** @var $queue OpenCloud\Queues\Resource\Queues */ + $queue = $service->getQueue('{name}'); + + +Check queue existence +--------------------- + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +.. code-block:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + + +Update queue metadata +--------------------- + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +.. code-block:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + + +Retrieve the queue metadata +--------------------------- + +This operation returns metadata, such as message TTL, for the queue. + +.. code-block:: php + + $metadata = $queue->retrieveMetadata(); + print_r($metadata->toArray()); + + +Get queue stats +--------------- + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +.. code-block:: php + + $queue->getStats(); From 39ef78bbdbdbde62cbf4b10ecfc722b3415cfd9c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:20:43 -0800 Subject: [PATCH 438/835] Fix what properties can be updated. --- lib/OpenCloud/Networking/Resource/Network.php | 4 +--- lib/OpenCloud/Networking/Resource/Port.php | 6 +----- tests/OpenCloud/Smoke/Unit/Networking.php | 8 +------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 53716a8f0..19454445e 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -57,9 +57,7 @@ class Network extends PersistentResource implements NetworkInterface ); protected $updateKeys = array( - 'adminStateUp', - 'name', - 'shared' + 'name' ); /** diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 484de675e..bc7ee4dd0 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -78,11 +78,7 @@ class Port extends PersistentResource protected $updateKeys = array( 'name', - 'adminStateUp', - 'deviceId', - 'deviceOwner', - 'fixedIps', - 'securityGroups' + 'deviceId' ); /** diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index b6ab4592e..2518119bd 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -245,13 +245,7 @@ protected function testPortOperations() $this->step('Update port'); $port->update(array( - 'name' => 'updated_test_port', - 'fixedIps' => array( - array( - 'subnetId' => $subnet1->getId(), - 'ipAddress' => '192.168.62.17' - ) - ) + 'name' => 'updated_test_port' )); } From 8471547d2eee3b2c9f81ed9dfa09529084b45ecc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:12 -0800 Subject: [PATCH 439/835] Fix variable names. --- tests/OpenCloud/Smoke/Unit/Networking.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 2518119bd..7b424c46f 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -252,7 +252,7 @@ protected function testPortOperations() protected function testSecurityGroupOperations() { $this->step('Create security group'); - $security group = $this->getService()->createSecurityGroup(array( + $securityGroup = $this->getService()->createSecurityGroup(array( 'name' => 'new-webservers', 'description' => 'security group for webservers' )); @@ -269,7 +269,7 @@ protected function testSecurityGroupOperations() } $this->step('Get security group'); - $security group = $this->getService()->getSecurityGroup($securityGroup->getId()); + $securityGroup = $this->getService()->getSecurityGroup($securityGroup->getId()); $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); } @@ -282,7 +282,7 @@ protected function testSecurityGroupRuleOperations() $this->cleanupSecurityGroupIds[] = $securityGroup1->getId(); $this->step('Create security group rule'); - $security group rule = $this->getService()->createSecurityGroupRule(array( + $securityGroupRule = $this->getService()->createSecurityGroupRule(array( 'securityGroupId' => $securityGroup1->getId(), 'direction' => 'egress', 'ethertype' => 'IPv4', From a6302b4cad689e3f8034c5b660e66b4636e6f3aa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:27 -0800 Subject: [PATCH 440/835] Only ingress rules are currently allowed. --- tests/OpenCloud/Smoke/Unit/Networking.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 7b424c46f..38d903ff0 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -284,7 +284,7 @@ protected function testSecurityGroupRuleOperations() $this->step('Create security group rule'); $securityGroupRule = $this->getService()->createSecurityGroupRule(array( 'securityGroupId' => $securityGroup1->getId(), - 'direction' => 'egress', + 'direction' => 'ingress', 'ethertype' => 'IPv4', 'portRangeMin' => 80, 'portRangeMax' => 80, From f833f87fb4205ff20e06656f2d88f20d5d6e8c79 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:41 -0800 Subject: [PATCH 441/835] Remote groups are not currently allowed. --- tests/OpenCloud/Smoke/Unit/Networking.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 38d903ff0..deaefda33 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -288,8 +288,7 @@ protected function testSecurityGroupRuleOperations() 'ethertype' => 'IPv4', 'portRangeMin' => 80, 'portRangeMax' => 80, - 'protocol' => 'tcp', - 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + 'protocol' => 'tcp' )); $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); From 433e48bc81dca0be8c368406ba9ce919d02f4ceb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:55 -0800 Subject: [PATCH 442/835] Security group rules don't have names. s/name/direction/. --- tests/OpenCloud/Smoke/Unit/Networking.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index deaefda33..8ccd30a94 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -296,16 +296,16 @@ protected function testSecurityGroupRuleOperations() $this->step('List security group rules'); $securityGroupRules = $this->getService()->listSecurityGroupRules(); - $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule name'); + $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule direction'); $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); foreach ($securityGroupRules as $securityGroupRule) { - $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getName()); + $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getDirection()); } $this->step('Get security group rule'); - $security group rule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); + $securityGroupRule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); - $this->stepInfo('Security Group Rule Name: ' . $securityGroupRule->getName()); + $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); } public function teardown() From b1777f281fef1a7d98c708405ba6633acca0feb6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:38:43 -0800 Subject: [PATCH 443/835] Appeasing the linter. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 +- lib/OpenCloud/Networking/Resource/SecurityGroupRule.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index 105fc22a1..2892f5471 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * + * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * * @package OpenCloud\Networking\Resource diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php index 3659d08ac..5527ce272 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * + * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * * @package OpenCloud\Networking\Resource From 05dbbf928f0484df436a2943da9aeb5ba9e6ac2a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 11:24:03 +0100 Subject: [PATCH 444/835] Shuffle setup org --- doc/services/autoscale/index.rst | 8 +- doc/services/common/clients.sample.rst | 22 ++++++ doc/services/common/os-client.sample.rst | 13 --- .../{rs-client.sample.rst => rs-client.rst} | 8 +- doc/services/common/rs-only.sample.rst | 8 ++ doc/services/common/service-args.rst | 23 +++--- doc/services/compute/index.rst | 13 +-- doc/services/database/index.rst | 8 +- doc/services/dns/index.rst | 8 +- doc/services/identity/index.rst | 8 +- doc/services/image/index.rst | 8 +- doc/services/load-balancer/index.rst | 12 +-- doc/services/monitoring/index.rst | 8 +- doc/services/networking/index.rst | 8 +- doc/services/object-store/index.rst | 10 +-- doc/services/orchestration/index.rst | 8 +- doc/services/queues/index.rst | 6 +- doc/services/volume/index.rst | 43 ++++++++++ doc/services/volume/snapshots.rst | 48 +++++++++++ doc/services/volume/volume-types.rst | 30 +++++++ doc/services/volume/volumes.rst | 79 +++++++++++++++++++ doc/url-types.rst | 2 + 22 files changed, 293 insertions(+), 88 deletions(-) create mode 100644 doc/services/common/clients.sample.rst delete mode 100644 doc/services/common/os-client.sample.rst rename doc/services/common/{rs-client.sample.rst => rs-client.rst} (58%) create mode 100644 doc/services/common/rs-only.sample.rst create mode 100644 doc/services/volume/snapshots.rst create mode 100644 doc/services/volume/volume-types.rst create mode 100644 doc/services/volume/volumes.rst create mode 100644 doc/url-types.rst diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst index d6ec512ac..1bfd20e68 100644 --- a/doc/services/autoscale/index.rst +++ b/doc/services/autoscale/index.rst @@ -1,12 +1,12 @@ Auto Scale v2 ============= -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +Auto Scale service +~~~~~~~~~~~~~~~~~~ -Now, set up the Auto Scale service: +Now to instantiate the Auto Scale service: .. code-block:: php diff --git a/doc/services/common/clients.sample.rst b/doc/services/common/clients.sample.rst new file mode 100644 index 000000000..a952da730 --- /dev/null +++ b/doc/services/common/clients.sample.rst @@ -0,0 +1,22 @@ +Setup +----- + +Rackspace setup +~~~~~~~~~~~~~~~ + +.. include:: /services/common/rs-client.rst + + +OpenStack setup +~~~~~~~~~~~~~~~ + +If you're an OpenStack user, you will also need to prove a few other +configuration parameters: + +.. code-block:: php + + $client = new OpenCloud\OpenStack('{keystoneUrl}', array( + 'username' => '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/services/common/os-client.sample.rst b/doc/services/common/os-client.sample.rst deleted file mode 100644 index b4614474d..000000000 --- a/doc/services/common/os-client.sample.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. code-block:: php - - '{username}', - 'password' => '{apiKey}', - 'tenantId' => '{tenantId}', - )); diff --git a/doc/services/common/rs-client.sample.rst b/doc/services/common/rs-client.rst similarity index 58% rename from doc/services/common/rs-client.sample.rst rename to doc/services/common/rs-client.rst index e33983270..46f9a5291 100644 --- a/doc/services/common/rs-client.sample.rst +++ b/doc/services/common/rs-client.rst @@ -1,12 +1,8 @@ -The first thing to do is pass in your credentials and instantiate a Rackspace -client: +The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key: .. code-block:: php - `_. +* ``{region}`` is the region the service will operate in. For Rackspace + users, you can select one of the following from the `supported regions page + `_. -``{urlType}`` is the type of URL to use, depending on what endpoints your -catalog provides. For Rackspace, you may use either ``internalURL`` or -``publicURL``. The former will execute HTTP transactions over the internal -network configured for your service, possibly reducing latency and the overall -bandwidth cost - the caveat is that all of your resources must be in same region. -``publicURL``, however, which is the default, will operate over the public -Internet and is to be used for multi-region installations. +* ``{urlType}`` is the `type of URL `_ to use, depending on which + endpoints your catalog provides. If omitted, it will default to the public + network. diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 207151d50..234517661 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -1,17 +1,12 @@ Compute v2 ========== -.. note:: +.. include:: ../common/clients.sample.rst - This is a joint service that supports both Rackspace Cloud Servers v2 API, and - OpenStack Nova v2 API. +Compute service +~~~~~~~~~~~~~~~ -Setup ------ - -.. include:: ../common/rs-client.sample.rst - -Now, set up the Compute service: +Now to instantiate the Compute service: .. code-block:: php diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index 850e56c8a..c330d4ac8 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -1,12 +1,12 @@ Databases v1 ============ -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Databases service +~~~~~~~~~~~~~~~~~ -Now, set up the Database service: +Now to instantiate the Databases service: .. code-block:: php diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index 4caca9fe5..99d19c5e8 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -1,12 +1,12 @@ DNS v1 ====== -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +DNS service +~~~~~~~~~~~ -Now, set up the DNS service: +Now to instantiate the DNS service: .. code-block:: php diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index 35c27131b..3eaa08de6 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -1,10 +1,12 @@ Identity v2 =========== -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample +Identity service +~~~~~~~~~~~~~~~~ + +Now to instantiate the Identity service: .. code-block:: php diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index d54b09ad0..c6451554b 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -1,10 +1,12 @@ Images v1 ========= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Images service +~~~~~~~~~~~~~~ + +Now to instantiate the Images service: .. code-block:: php diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index bf2b6dd0c..e2e4ca973 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -1,17 +1,13 @@ Load Balancer v1 ================ -.. note:: +.. include:: ../common/rs-only.sample.rst - This service is only available for Rackspace users. +Load Balancer service +~~~~~~~~~~~~~~~~~~~~~ -Setup ------ - -.. include:: ../common/rs-client.sample.rst - -Now, set up the Load Balancer service: +Now to instantiate the Load Balancer service: .. code-block:: php diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index df0597e8c..be38d1067 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -1,12 +1,12 @@ Monitoring v1 ============= -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +Monitoring service +~~~~~~~~~~~~~~~~~~ -Now, set up the Cloud Monitoring service: +Now to instantiate the Monitoring service: .. code-block:: php diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 92270fe86..89a159d4f 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -1,12 +1,12 @@ Networking v2 ============= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: rs-client.sample.rst +Networking service +~~~~~~~~~~~~~~~~~~ -Now, set up the Cloud Monitoring service: +Now to instantiate the Networking service: .. code-block:: php diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index 6b3a18b6f..8c50d7de0 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -1,12 +1,12 @@ Object Store v1 =============== -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Object Store service +~~~~~~~~~~~~~~~~~~~~ -Now, set up the Object Store service: +Now to instantiate the Object Store service: .. code-block:: php @@ -38,7 +38,7 @@ Glossary account is your portion of it. container - A storage compartment that provides a way for you to organize data. A + A storage compartment that provides a way for you to organize data. A container is similar to a folder in Windows or a directory in UNIX. The primary difference between a container and these other file system concepts is that containers cannot be nested. diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index 0dce8ac43..fcb5e46b9 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -1,12 +1,12 @@ Orchestration v1 ================ -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: rs-client.sample.rst +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ -Now set up the Orchestration service: +Now to instantiate the Orchestration service: .. code-block:: php diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index e37845d76..317257d65 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -1,10 +1,10 @@ Queues v1 ========= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Queues service +~~~~~~~~~~~~~~ Now to instantiate the Queues service: diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst index e69de29bb..7e318a5cc 100644 --- a/doc/services/volume/index.rst +++ b/doc/services/volume/index.rst @@ -0,0 +1,43 @@ +Volumes v1 +========== + +.. include:: ../common/clients.sample.rst + +Volume service +~~~~~~~~~~~~~~ + +Now to instantiate the Volume service: + +.. code-block:: php + + $service = $client->volumeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + volumes + volume-types + snapshots + + +Glossary +-------- + +.. glossary:: + + volume + A volume is a detachable block storage device. You can think of it as a USB + hard drive. It can only be attached to one instance at a time. + + volume type + Providers may support multiple types of volumes; at Rackspace, a volume + can either be ``SSD`` (solid state disk: expensive, high-performance) or + ``SATA`` (serial attached storage: regular disks, less expensive). + + snapshot + A snapshot is a point-in-time copy of the data contained in a volume. diff --git a/doc/services/volume/snapshots.rst b/doc/services/volume/snapshots.rst new file mode 100644 index 000000000..c2d8ef6cc --- /dev/null +++ b/doc/services/volume/snapshots.rst @@ -0,0 +1,48 @@ +Snapshots +========= + +Create a snapshot +----------------- + +A ``Snapshot`` object is created from the Cloud Block Storage service. +However, it is associated with a volume, and you must specify a volume +to create one: + +.. code-block:: php + + // New instance of OpenCloud\Volume\Resource\Snapshot + $snapshot = $service->snapshot(); + + // Send to API + $snapshot->create(array( + 'display_name' => 'Name that snapshot', + 'volume_id' => $volume->id + )); + + +List snapshots +-------------- + +.. code-block:: php + + $snapshots = $service->snapshotList(); + + foreach ($snapshots as $snapshot) { + /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ + } + + +To get details on a single snapshot +----------------------------------- + +.. code-block:: php + + $snapshot = $dallas->snapshot('{snapshotId}'); + + +To delete a snapshot +-------------------- + +.. code-block:: php + + $snapshot->delete(); diff --git a/doc/services/volume/volume-types.rst b/doc/services/volume/volume-types.rst new file mode 100644 index 000000000..ba3176df9 --- /dev/null +++ b/doc/services/volume/volume-types.rst @@ -0,0 +1,30 @@ +Volume Types +============ + +List volume types +----------------- + +.. code-block:: php + + $volumeTypes = $service->volumeTypeList(); + + foreach ($volumeTypes as $volumeType) { + /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ + } + + +Describe a volume type +---------------------- + +If you know the ID of a volume type, use the ``volumeType`` method to +retrieve information on it: + +.. code-block:: php + + $volumeType = $service->volumeType(1); + +A volume type has three attributes: + +- ``id`` the volume type identifier +- ``name`` its name +- ``extra_specs`` additional information for the provider diff --git a/doc/services/volume/volumes.rst b/doc/services/volume/volumes.rst new file mode 100644 index 000000000..6a35bb135 --- /dev/null +++ b/doc/services/volume/volumes.rst @@ -0,0 +1,79 @@ +Volumes +======= + +Create a volume +--------------- + +To create a volume, you must specify its size (in gigabytes). All other +parameters are optional: + +.. code-block:: php + + // Create instance of OpenCloud\Volume\Resource\Volume + $volume = $service->volume(); + + $volume->create(array( + 'size' => 200, + 'volume_type' => $service->volumeType(''), + 'display_name' => 'My Volume', + 'display_description' => 'Used for large object storage' + )); + +List volumes +------------ + +.. code-block:: php + + $volumes = $service->volumeList(); + + foreach ($volumes as $volume) { + /** @param $volumeType OpenCloud\Volume\Resource\Volume */ + } + + +Get details on a single volume +------------------------------ + +If you specify an ID on the ``volume()`` method, it retrieves +information on the specified volume: + +.. code-block:: php + + $volume = $dallas->volume(''); + echo $volume->size; + + +To delete a volume +------------------ + +.. code-block:: php + + $volume->delete(); + + +Attach a volume to a server +--------------------------- + +.. code-block:: php + + // retrieve server + $computeService = $client->computeService('{catalogName}', '{region}'); + $server = $computeService->server('{serverId}'); + + // attach volume + $server->attachVolume($volume, '{mountPoint}') + +The ``{mountPoint}`` is the location on the server on which to mount +the volume (usually ``/dev/xvhdd`` or similar). You can also supply +``'auto'`` as the mount point, in which case the mount point will be +automatically selected for you. ``auto`` is the default value for +``{mountPoint}``, so you do not actually need to supply anything for +that parameter. + + +Detach a volume from a server +----------------------------- + +.. code-block:: php + + $server->detachVolume($volume); diff --git a/doc/url-types.rst b/doc/url-types.rst new file mode 100644 index 000000000..b0e2cc0ea --- /dev/null +++ b/doc/url-types.rst @@ -0,0 +1,2 @@ +URL types +========= From 90d9207de1153c4e9f736ffbae4914f082fff908 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 11:43:03 +0100 Subject: [PATCH 445/835] Refactor root pages and index page --- doc/caching-creds.rst | 36 ++++++++++----------- doc/debugging.rst | 60 +++++++++++++++++++---------------- doc/index.rst | 74 ++++++++++++++++++++++++++++++++++++------- doc/iterators.rst | 18 ++++------- doc/regions.rst | 7 +++- doc/url-types.rst | 13 ++++++++ 6 files changed, 138 insertions(+), 70 deletions(-) diff --git a/doc/caching-creds.rst b/doc/caching-creds.rst index 3c3e8df23..4eb5be5c8 100644 --- a/doc/caching-creds.rst +++ b/doc/caching-creds.rst @@ -16,29 +16,29 @@ Filesystem example In this example, credentials will be saved to a file in the local filesystem. Be sure to exclude it from your VCS. -.. code:: php +.. code-block:: php - use OpenCloud\Rackspace; + use OpenCloud\Rackspace; - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' - )); + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); - $cacheFile = __DIR__ . '/.opencloud_token'; + $cacheFile = __DIR__ . '/.opencloud_token'; - // If the cache file exists, try importing it into the client - if (file_exists($cacheFile)) { - $data = unserialize(file_get_contents($cacheFile)); - $client->importCredentials($data); - } + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } - $token = $client->getTokenObject(); + $token = $client->getTokenObject(); - // If no token exists, or the current token is expired, re-authenticate and save the new token to disk - if (!$token || ($token && $token->hasExpired())) { - $client->authenticate(); - file_put_contents($cacheFile, serialize($client->exportCredentials())); - } + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/debugging.rst b/doc/debugging.rst index 445915284..5d35d5bc1 100644 --- a/doc/debugging.rst +++ b/doc/debugging.rst @@ -36,38 +36,40 @@ If you're trying to retrieve a Swift resource, such as a Data Object, and you're not completely certain that it exists, it makes sense to wrap your call in a try/catch block: -.. code:: php +.. code-block:: php - use Guzzle\Http\Exception\ClientErrorResponseException; + use Guzzle\Http\Exception\ClientErrorResponseException; - try { - return $service->getObject('foo.jpg'); - } catch (ClientErrorResponseException $e) { - // Okay, the resource probably does not exist + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + // Okay, the resource does not exist return false; - } catch (\Exception $e) { - // Some other exception was thrown, probably critical - $this->logException($e); - $this->alertDevs(); - } + } + } catch (\Exception $e) { + // Some other exception was thrown... + } + Both ``ClientErrorResponseException`` and ``ServerErrorResponseException`` have two methods that allow you to access the HTTP transaction: -.. code:: php +.. code-block:: php + + // Find out the faulty request + $request = $e->getRequest(); - // Find out the faulty request - $request = $e->getRequest(); + // Display everything by casting as string + echo (string) $request; - // Display everything by casting as string - echo (string) $request; + // Find out the HTTP response + $response = $e->getResponse(); - // Find out the HTTP response - $response = $e->getResponse(); + // Output that too + echo (string) $response; - // Output that too - echo (string) $response; Strategy 2: Wire logging ------------------------ @@ -80,20 +82,22 @@ don't know what's going on. Here's how you enable it: Install the plugin -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ -.. code:: bash +.. code-block:: bash + + composer require guzzle/guzzle - php composer.phar require guzzle/plugin-log:~3.8 Add to your client -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php -.. code:: php + use Guzzle\Plugin\Log\LogPlugin; - use Guzzle\Plugin\Log\LogPlugin; + $client->addSubscriber(LogPlugin::getDebugPlugin()); - $client->addSubscriber(LogPlugin::getDebugPlugin()); The above will add a generic logging subscriber to your client, which -will be notified every time a relevant HTTP event is fired off. +will output every HTTP transaction to `STDOUT`. diff --git a/doc/index.rst b/doc/index.rst index 9c06b86e8..8a211dab4 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,12 +1,37 @@ -.. php-opencloud documentation master file, created by - sphinx-quickstart on Tue Mar 3 12:28:19 2015. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +Welcome to php-opencloud! +========================= -Welcome to php-opencloud's documentation! -========================================= +Installation +------------ -Contents: +You must install this library through Composer: + +.. code-block:: bash + + composer require rackspace/php-opencloud + + +If you do not have Composer installed, please consult the `official docs +`_. + +Once you have installed the library, you will need to load Composer's autoloader +(which registers all the required namespaces). To do this, place the following +line of PHP code at the top of your application's PHP files: + +.. code-block:: php + + require 'vendor/autoload.php'; + +This assumes your application's PHP files are located in the same folder as +``vendor/``. If your files are located elsewhere, please supply the path to +``vendor/autoload.php`` in the require statement above. + +Read the `OpenStack Getting Started guide `_ +or `Rackspace Getting Started guide `_ to help +you get started with basic Compute operations. + +Services +-------- .. toctree:: :glob: @@ -14,10 +39,35 @@ Contents: services/**/index +Usage tips +---------- + +.. toctree:: + :maxdepth: 1 + + debugging + caching-creds + iterators + regions + url-types + +Help and support +---------------- + +If you have specific problems or bugs with this SDK, please file an issue on +our official `Github `_. We also +have a `mailing list `_, +so feel free to join to keep up to date with all the latest changes and +announcements to the library. + +For general feedback and support requests, send an email to +sdk-support@rackspace.com. + +You can also find assistance via IRC on #rackspace at freenode.net. -Indices and tables -================== +Contributing +------------ -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +If you'd like to contribute to the project, or require help running the +unit/acceptance tests, please view the `contributing guidelines +`_. diff --git a/doc/iterators.rst b/doc/iterators.rst index e5d3c198d..bef59e339 100644 --- a/doc/iterators.rst +++ b/doc/iterators.rst @@ -1,9 +1,6 @@ Iterators ========= -Intro ------ - Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: @@ -27,27 +24,27 @@ the SDK: Common behaviour ---------------- -.. code:: php +.. code-block:: php $iterator = $computeService->flavorList(); There are two ways to traverse an iterator. The first is the longer, more traditional way: -.. code:: php +.. code-block:: php while ($iterator->valid()) { $flavor = $iterator->current(); - + // do stuff.. echo $flavor->id; - + $iterator->next(); } There is also a shorter and more intuitive version: -.. code:: php +.. code-block:: php foreach ($iterator as $flavor) { // do stuff... @@ -63,7 +60,7 @@ Very important note Until now, users have been expected to do this: -.. code:: php +.. code-block:: php while ($flavor = $iterator->next()) { // ... @@ -119,7 +116,7 @@ precedence. Setting up a PaginatedIterator ------------------------------ -.. code:: php +.. code-block:: php use OpenCloud\Common\Collection\PaginatedIterator; @@ -175,4 +172,3 @@ needs to work. These are: +-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ | request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | +-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ - diff --git a/doc/regions.rst b/doc/regions.rst index 82edf28ec..42c14595c 100644 --- a/doc/regions.rst +++ b/doc/regions.rst @@ -3,13 +3,18 @@ Rackspace regions Below are the supported regions on the Rackspace network: -+======+===========+ ++------+-----------+ | code | location | +======+===========+ | IAD | Virginia | ++------+-----------+ | ORD | Chicago | ++------+-----------+ | DFW | Dallas | ++------+-----------+ | LON | London | ++------+-----------+ | SYD | Sydney | ++------+-----------+ | HKG | Hong Kong | +------+-----------+ diff --git a/doc/url-types.rst b/doc/url-types.rst index b0e2cc0ea..26a0410bd 100644 --- a/doc/url-types.rst +++ b/doc/url-types.rst @@ -1,2 +1,15 @@ URL types ========= + +internalURL +----------- + +An internal URL is a URL that is accessible only from within the Rackspace +Cloud network. Access to an internal URL is always free of charge. + + +publicURL +--------- + +A public URL is a URL that is accessible from anywhere. Access to a public URL +usually incurs traffic charges. From a80c96590913f514da09f86ec5b2681bb32121e3 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 12:29:29 +0100 Subject: [PATCH 446/835] Add final links --- doc/services/compute/index.rst | 8 +++- doc/services/database/index.rst | 8 ++++ doc/services/dns/index.rst | 8 ++++ doc/services/identity/index.rst | 7 +++ doc/services/image/index.rst | 7 +++ doc/services/load-balancer/index.rst | 9 +++- doc/services/monitoring/index.rst | 65 +++++++++++++++++++++++++++- doc/services/networking/index.rst | 8 ++++ doc/services/object-store/index.rst | 8 ++++ doc/services/orchestration/index.rst | 9 ++++ doc/services/queues/index.rst | 8 ++++ doc/services/volume/index.rst | 8 ++++ 12 files changed, 150 insertions(+), 3 deletions(-) diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 234517661..ee27bce8e 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -46,4 +46,10 @@ Glossary server A server is a virtual machine instance in the Cloud Servers environment. - keypair + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index c330d4ac8..32888e27d 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -67,3 +67,11 @@ Glossary for increased performance, scalability, availability and manageability. Applications with high I/O demands are performance optimized and data is protected through both local and network RAID-10. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index 99d19c5e8..39db3d675 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -49,3 +49,11 @@ Glossary DNS usually determines an IP address associated with a domain name. Reverse DNS is the opposite process: resolving a domain name from an IP address. This is usually achieved with a domain name pointer. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index 3eaa08de6..6cb4dfdf4 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -46,3 +46,10 @@ Glossary access the requested resources. Users may be directly assigned to a particular tenant and behave as if they are contained within that tenant. + + +Further Links +------------- + +- `Quickstart for the API `_ +- `API Developer Guide `_ diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index c6451554b..a7331b430 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -41,3 +41,10 @@ Glossary tag An image tag is a string of characters used to identify a specific image or images. + + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index e2e4ca973..9ea7614ad 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -21,7 +21,7 @@ Operations .. toctree:: - load-balancers + load-balancer nodes virtual-ips access @@ -87,3 +87,10 @@ Glossary (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP address (``SERVICENET``), routable only within the region in which the load balancer resides. + + +Further Links +------------- + +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index be38d1067..0a7225e36 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -29,6 +29,69 @@ Operations views zones - Glossary -------- + +.. glossary:: + + agent + A monitoring daemon that resides on the server being monitored. The agent + gathers metrics based on agent checks and pushes them to Cloud Monitoring. + The agent provides insight into your servers with checks for information + such as load average and network usage. The agent acts as a single small + service that runs scheduled checks and pushes metrics to the rest of Cloud + Monitoring so the metrics can be analyzed, trigger alerts, and be archived. + These metrics are gathered via checks using agent check types, and can be + used with the other Cloud Monitoring primitives such as alarms. + + agent token + An authentication token used to identify the agent when it communicates + with Cloud Monitoring. + + alarm + An alarm contains a set of rules that determine when the monitoring system + sends a notification. You can create multiple alarms for the different + checks types associated with an entity. For example, if your entity is a + web server that hosts your company's website, you can create one alarm to + monitor the server itself, and another alarm to monitor the website. + + check + Checks explicitly specify how you want to monitor an entity. Once you've + created an entity, you can configure one or more checks for it. A check is + the foundational building block of the monitoring system, and is always + associated with an entity. The check specifies the parts or pieces of the + entity that you want to monitor, the monitoring frequency, how many + monitoring zones are launching the check, and so on. It contains the + specific details of how you are monitoring the entity. + + entity + The object or resource that you want to monitor. It can be any object or + device that you want to monitor. It's commonly a web server, but it might + also be a website, a web page or a web service. + + monitoring zone + A monitoring zone is the "launch point" of a check. When you create a + check, you specify which monitoring zone(s) you want to launch the check + from. This concept of a monitoring zone is similar to that of a datacenter, + however in the monitoring system, you can think of it more as a geographical + region. + + notification + A notification is an informational message sent to one or more addresses + by the monitoring system when an alarm is triggered. You can set up + notifications to alert a single individual or an entire team. Rackspace + Cloud Monitoring currently supports webhooks and email for sending + notifications. + + notification plan + A notification plan contains a set of notification rules to execute when an + alarm is triggered. A notification plan can contain multiple notifications + for each of the following states: + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 89a159d4f..515f9ddce 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -48,3 +48,11 @@ Glossary When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index 8c50d7de0..fcdc5aff2 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -55,3 +55,11 @@ Glossary object An object (sometimes referred to as a file) is the unit of storage in an Object Store. An object is a combination of content (data) and metadata. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index fcb5e46b9..bae5ec6aa 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -25,6 +25,7 @@ Operations resources resource-types build-info + events Glossary @@ -48,3 +49,11 @@ Glossary stack A stack is a running instance of a template. When a stack is created, the resources specified in the template are created. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index 317257d65..4175df760 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -46,3 +46,11 @@ Glossary producer or publisher sends to the queue. A message exists until it is deleted by a recipient or automatically by the system based on a TTL (time-to-live) value. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst index 7e318a5cc..bee589086 100644 --- a/doc/services/volume/index.rst +++ b/doc/services/volume/index.rst @@ -41,3 +41,11 @@ Glossary snapshot A snapshot is a point-in-time copy of the data contained in a volume. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ From 0ad4ceae7fc8cd8864b7a10119ebd4547176629c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 13:05:09 +0100 Subject: [PATCH 447/835] Add GS guides --- doc/index.rst | 4 ++-- doc/services/load-balancer/lb-setup.sample.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 8a211dab4..297a64c6c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,8 +26,8 @@ This assumes your application's PHP files are located in the same folder as ``vendor/``. If your files are located elsewhere, please supply the path to ``vendor/autoload.php`` in the require statement above. -Read the `OpenStack Getting Started guide `_ -or `Rackspace Getting Started guide `_ to help +Read the `OpenStack Getting Started guide `_ +or `Rackspace Getting Started guide `_ to help you get started with basic Compute operations. Services diff --git a/doc/services/load-balancer/lb-setup.sample.rst b/doc/services/load-balancer/lb-setup.sample.rst index 96e228b8d..d82c80a1f 100644 --- a/doc/services/load-balancer/lb-setup.sample.rst +++ b/doc/services/load-balancer/lb-setup.sample.rst @@ -6,4 +6,4 @@ load balancer, like so: .. code-block:: php - $loadBalancer = $service->loadBalancer('{id}'); + $loadBalancer = $service->loadBalancer('{id}'); From 3fff2691b4e1ed1aac3128edc581a4654da7cf8a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 13:22:42 +0100 Subject: [PATCH 448/835] Tweak GS guides --- doc/getting-started-with-openstack.rst | 233 +++++++++++++++++++++++++ doc/getting-started-with-rackspace.rst | 185 ++++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 doc/getting-started-with-openstack.rst create mode 100644 doc/getting-started-with-rackspace.rst diff --git a/doc/getting-started-with-openstack.rst b/doc/getting-started-with-openstack.rst new file mode 100644 index 000000000..da6c6ee24 --- /dev/null +++ b/doc/getting-started-with-openstack.rst @@ -0,0 +1,233 @@ +Getting Started with OpenStack +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Nova instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against Keystone: + +.. code-block:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( + 'username' => 'foo', + 'password' => 'bar', + 'tenantName' => 'baz' + )); + +You will need to substitute in the public URL endpoint for your Keystone +service, as well as your ``username``, ``password`` and ``tenantName``. +You can also specify your ``tenantId`` instead of ``tenantName`` if you +prefer. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Nova service: + +.. code-block:: php + + $compute = $client->computeService('nova', 'regionOne'); + + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. For OpenStack users, this must be retrieved +and entered in your code. If you are unsure how to retrieve the service +name, follow these steps: + +1. Setup the ``$client`` object, as above +2. Copy and run this code: + +.. code-block:: php + + $client->authenticate(); + print_r($client->getCatalog()->getItems()); + + +3. This will output all the items in your service catalog. Go through + the outputted list and find your service, making note of the "name" + field. This is the name you will need to enter as the first argument. + You will also be able to see the available regions. + +The second argument is the region. The third and last argument is the +type of URL; you may use either ``publicURL`` or ``internalURL``. + + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instances are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of an instance is its +hardware configuration. So if you want a 2GB instance but don't know the +ID, you have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + + use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +As you can see, you're creating a server called "My lovely server" - +this will take a few minutes for the build to complete. You can always +check the progress by logging into your Controller node and running: + +.. code-block:: bash + + nova list + +You can also execute a polling function immediately after the ``create`` +method that checks the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +More fun with Nova +------------------ + +Once you've booted up your instance, you can use other API operations to +monitor your Compute nodes. To list every node on record, you can +execute: + +.. code-block:: php + + $servers = $compute->serverList(); + + foreach ($servers as $server) { + // do something with each server... + echo $server->name, PHP_EOL; + } + +or, if you know a particular instance ID you can retrieve its details: + +.. code-block:: php + + $server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); + +allowing you to update its properties: + +.. code-block:: php + + $server->update(array( + 'name' => 'New server name' + )); + +or delete it entirely: + +.. code-block:: php + + $server->delete(); + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. diff --git a/doc/getting-started-with-rackspace.rst b/doc/getting-started-with-rackspace.rst new file mode 100644 index 000000000..35317a3e6 --- /dev/null +++ b/doc/getting-started-with-rackspace.rst @@ -0,0 +1,185 @@ +Getting Started with Rackspace +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Cloud Servers instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against the Rackspace API and use its services: + +.. code-block:: php + + 'foo', + 'apiKey' => 'bar' + )); + +You can see in the first example that the constant +``Rackspace::US_IDENTITY_ENDPOINT`` is just a string representation of +Rackspace's identity endpoint +(``https://identity.api.rackspacecloud.com/v2.0/``). Another difference +is that Rackspace uses API key for authentication, whereas OpenStack +uses a generic password. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Compute (Nova) service: + +.. code-block:: php + + $compute = $client->computeService(null, 'ORD'); + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. If in doubt, you can leave blank and it will +revert to the default name for the service. The second argument is the +region; you may use: + +- **DFW** (Dallas) +- **ORD** (Chicago) +- **IAD** (Virginia) +- **LON** (London) +- **HKG** (Hong Kong) +- **SYD** (Sydney) + +The third and last argument is the type of URL; you may use either +``publicURL`` or ``internalURL``. If you select ``internalUrl`` all API +traffic will use ServiceNet (internal IPs) and will receive a +performance boost. + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Servers are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of a server is its hardware +configuration. So if you want a 2GB instance but don't know the ID, you +have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + +use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +You can also call a polling function that checks on the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. From 6146127b04b4d77d6d2cc450f18dc24392a342ef Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:15:29 -0800 Subject: [PATCH 449/835] Adding descriptions to docblocks. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 ++ lib/OpenCloud/Networking/Resource/SecurityGroupRule.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index 2892f5471..b4a8e08d2 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,6 +20,8 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * A security group is a named container for security group rules, each of which is + * represented by {@see \OpenCloud\Networking\Resource\SecurityGroupRule}. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php index 5527ce272..06ad028b2 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -20,6 +20,10 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * + * Security group rules provide users the ability to specify the types of traffic that are allowed + * to pass through to and from ports (represented by {@see \OpenCloud\Networking\Resource\Port}) + * on a virtual server instance. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * From d44ff031a856f94964b51637cb49d1c13dc4d3bf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:32:30 -0800 Subject: [PATCH 450/835] Fixing typo. --- docs/userguide/Networking/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index bfa1a217c..13342977f 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -65,9 +65,9 @@ be assigned to the interfaces plugged into them. When IP addresses are associated with a port, this also implies the port is associated with a subnet because the IP address is taken from the allocation pool for a specific subnet. -* **Security Group**: TODO +* **Security Group**: A named container for security group rules. -* **Security Group Rule**: TODO +* **Security Group Rule**: Provide users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. ## Prerequisites From 1de047e73283cf695340af43008c33520803c3e6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:32:38 -0800 Subject: [PATCH 451/835] Adding descriptions for Security Groups and Security Group Rules sections. --- docs/userguide/Networking/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 13342977f..5a7beb07f 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -485,7 +485,7 @@ $port->delete(); ## Security Groups -TODO: Add description +A security group is a named container for [security group rules](#security-group-rules). ### Create a security group @@ -546,7 +546,7 @@ $securityGroup->delete(); ## Security Group Rules -TODO: Add description +A security group rule provides users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. ### Create a security group rule From d2d400fe21444cdf44f0aa7badc53576d1c07532 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:51:36 -0800 Subject: [PATCH 452/835] Moving @see annotations to their own lines. --- lib/OpenCloud/Networking/Service.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 12bb7477f..e3fcc879e 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -51,8 +51,10 @@ public function network($id = null) /** * Creates a new Network and returns it. * - * @param array $params Network creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network + * @param array $params Network creation parameters. * @return \OpenCloud\Networking\Resource\Network Object representing created network + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network */ public function createNetwork(array $params = array()) { @@ -136,8 +138,10 @@ public function subnet($id = null) /** * Creates a new Subnet and returns it. * - * @param array $params Subnet creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet + * @param array $params Subnet creation parameters. * @return \OpenCloud\Networking\Resource\Subnet Object representing created subnet + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet */ public function createSubnet(array $params = array()) { @@ -221,8 +225,10 @@ public function port($id = null) /** * Creates a new Port and returns it. * - * @param array $params Port creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port + * @param array $params Port creation parameters. * @return \OpenCloud\Networking\Resource\Port Object representing created port + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port */ public function createPort(array $params = array()) { @@ -306,8 +312,10 @@ public function securityGroup($id = null) /** * Creates a new SecurityGroup and returns it. * - * @param array $params SecurityGroup creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group + * @param array $params SecurityGroup creation parameters. * @return \OpenCloud\Networking\Resource\SecurityGroup Object representing created security group + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group */ public function createSecurityGroup(array $params = array()) { @@ -355,8 +363,10 @@ public function securityGroupRule($id = null) /** * Creates a new SecurityGroupRule and returns it. * - * @param array $params SecurityGroupRule creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule + * @param array $params SecurityGroupRule creation parameters. * @return \OpenCloud\Networking\Resource\SecurityGroupRule Object representing created security group rule + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule */ public function createSecurityGroupRule(array $params = array()) { From fbf4074e37ef5e19869659487a56805d9e4912d8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:53:56 -0800 Subject: [PATCH 453/835] Appeasing the PSR-2 linter. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index b4a8e08d2..4b2f85efd 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * A security group is a named container for security group rules, each of which is + * A security group is a named container for security group rules, each of which is * represented by {@see \OpenCloud\Networking\Resource\SecurityGroupRule}. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups From e75cb13976637d01eac70c29508444636451cf05 Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Fri, 6 Mar 2015 17:29:07 -0600 Subject: [PATCH 454/835] Swap support email address for support URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42948e8c3..6031cf4ac 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Your feedback is appreciated! If you have specific problems or bugs with this SD also have a [mailing list](https://groups.google.com/forum/#!forum/php-opencloud), so feel free to join to keep up to date with all the latest changes and announcements to the library. -For general feedback and support requests, send an email to sdk-support@rackspace.com. +For general feedback and support requests, contact us at https://developer.rackspace.com/support/ You can also find assistance via IRC on #rackspace at freenode.net. From 3b288617f125a451e3313e2c48d73fb15f999d08 Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Fri, 6 Mar 2015 17:30:33 -0600 Subject: [PATCH 455/835] Swap support email address for support URL --- docs/getting-started-openstack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index b00e58693..6bb1ae260 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -198,4 +198,4 @@ $server->delete(); Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or [Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to e-mail sdk-support@rackspace.com or open a Github issue with details. +troubles, feel free to contact us at https://developer.rackspace.com/support/ or open a Github issue with details. From ee8b251a987205b39fdf7496f8c99611fb42f5ee Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:34:55 +0100 Subject: [PATCH 456/835] Use full paths for downloadable samples --- doc/services/database/configurations.rst | 14 +- doc/services/database/datastores.rst | 8 +- doc/services/database/instances.rst | 4 +- doc/services/networking/networks.rst | 12 +- doc/services/networking/ports.rst | 12 +- doc/services/networking/subnets.rst | 12 +- doc/services/orchestration/build-info.rst | 2 +- doc/services/orchestration/events.rst | 6 +- doc/services/orchestration/resource-types.rst | 6 +- doc/services/orchestration/resources.rst | 6 +- doc/services/orchestration/stacks.rst | 24 +- doc/services/orchestration/stacks.rst-e | 299 ++++++++++++++++++ doc/services/orchestration/templates.rst | 4 +- 13 files changed, 354 insertions(+), 55 deletions(-) create mode 100644 doc/services/orchestration/stacks.rst-e diff --git a/doc/services/database/configurations.rst b/doc/services/database/configurations.rst index edfb9dc89..f50619914 100644 --- a/doc/services/database/configurations.rst +++ b/doc/services/database/configurations.rst @@ -22,7 +22,7 @@ Creating a configuration ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Listing configurations @@ -37,7 +37,7 @@ You can list out all the configurations you have created as shown below: /** @var $configuration OpenCloud\Database\Resource\Configuration **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a configuration @@ -50,7 +50,7 @@ You can retrieve a specific configuration, using its ID, as shown below: $configuration = $service->configuration('{configId}'); /** @var OpenCloud\Database\Resource\Configuration **/ -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Updating a configuration @@ -77,7 +77,7 @@ You can patch a configuration as shown below: ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Replacing a configuration @@ -94,7 +94,7 @@ You can replace a configuration as shown below: ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Deleting a configuration @@ -104,7 +104,7 @@ Deleting a configuration $configuration->delete(); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ .. note:: @@ -124,4 +124,4 @@ as shown below: /** @var $instance OpenCloud\Database\Resource\Instance **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/datastores.rst b/doc/services/database/datastores.rst index 6ca45daed..ad6d90e85 100644 --- a/doc/services/database/datastores.rst +++ b/doc/services/database/datastores.rst @@ -13,7 +13,7 @@ You can list out all the datastores available as shown below: /** @var $datastore OpenCloud\Database\Resource\Datastore **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a datastore @@ -27,7 +27,7 @@ shown below: /** @var OpenCloud\Database\Resource\Datastore **/ $datastore = $service->datastore('{datastoreId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Listing datastore versions @@ -43,7 +43,7 @@ shown below: /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a datastore version @@ -56,4 +56,4 @@ below: $datastoreVersion = $datastore->version('{versionId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/instances.rst b/doc/services/database/instances.rst index 082e56c05..6f3afc1e0 100644 --- a/doc/services/database/instances.rst +++ b/doc/services/database/instances.rst @@ -16,7 +16,7 @@ Create a new instance 'volume' => array('size' => 4) // 4GB of volume disk )); -`Get the executable PHP script for this sample `__ +`Get the executable PHP script for this sample `__ Waiting for the instance to build @@ -96,7 +96,7 @@ Retrieving an instance $instance = $service->instance('{instanceId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Updating an instance diff --git a/doc/services/networking/networks.rst b/doc/services/networking/networks.rst index a290a34e7..b95d44eba 100644 --- a/doc/services/networking/networks.rst +++ b/doc/services/networking/networks.rst @@ -28,7 +28,7 @@ You can create a network as shown in the following example: 'name' => 'My private backend network' )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Create multiple networks @@ -55,7 +55,7 @@ You can create multiple networks as shown in the following example: /** @var $network OpenCloud\Networking\Resource\Network **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List networks ------------- @@ -72,7 +72,7 @@ following example: } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a network @@ -86,7 +86,7 @@ in the following example: /** @var $network OpenCloud\Networking\Resource\Network **/ $network = $networkingService->getNetwork('{networkId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a network @@ -113,7 +113,7 @@ You can update a network as shown in the following example: 'name' => 'My updated private backend network' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a network @@ -125,4 +125,4 @@ You can delete a network as shown in the following example: $network->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst index 06d2ec2cf..8eeb07bf0 100644 --- a/doc/services/networking/ports.rst +++ b/doc/services/networking/ports.rst @@ -39,7 +39,7 @@ You can create a port as shown in the following example: )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create multiple ports @@ -68,7 +68,7 @@ You can create multiple ports as shown in the following example: /** @var $port OpenCloud\Networking\Resource\Port **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List ports @@ -84,7 +84,7 @@ You can list all the ports to which you have access as shown in the following ex /** @var $port OpenCloud\Networking\Resource\Port **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a port @@ -98,7 +98,7 @@ the following example: /** @var $port OpenCloud\Networking\Resource\Port **/ $port = $networkingService->getPort('{portId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a port @@ -135,7 +135,7 @@ You can update a port as shown in the following example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a port @@ -147,4 +147,4 @@ You can delete a port as shown in the following example: $port->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/subnets.rst b/doc/services/networking/subnets.rst index e8d6951a0..9a6c2a7b4 100644 --- a/doc/services/networking/subnets.rst +++ b/doc/services/networking/subnets.rst @@ -42,7 +42,7 @@ You can create a subnet as shown in the following example: 'cidr' => '192.168.199.0/25' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create multiple subnets @@ -69,7 +69,7 @@ You can create multiple subnets as shown in the following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List subnets @@ -85,7 +85,7 @@ following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a subnet @@ -99,7 +99,7 @@ in the following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ $subnet = $networkingService->getSubnet('{subnetId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a subnet @@ -137,7 +137,7 @@ You can update a subnet as shown in the following example: 'gatewayIp' => '192.168.62.155' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a subnet @@ -149,4 +149,4 @@ You can delete a subnet as shown in the following example: $subnet->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/build-info.rst b/doc/services/orchestration/build-info.rst index c30ce5cfa..b4016bab2 100644 --- a/doc/services/orchestration/build-info.rst +++ b/doc/services/orchestration/build-info.rst @@ -12,4 +12,4 @@ build as shown in the following example: /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ $buildInfo = $orchestrationService->getBuildInfo(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/events.rst b/doc/services/orchestration/events.rst index 68dbf611b..bceec0f5a 100644 --- a/doc/services/orchestration/events.rst +++ b/doc/services/orchestration/events.rst @@ -19,7 +19,7 @@ shown in the following example: /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List stack resource events @@ -36,7 +36,7 @@ shown in the following example: /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource event @@ -50,4 +50,4 @@ using the resource event's ID, as shown in the following example: /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resource-types.rst b/doc/services/orchestration/resource-types.rst index cd229d660..caa58b013 100644 --- a/doc/services/orchestration/resource-types.rst +++ b/doc/services/orchestration/resource-types.rst @@ -17,7 +17,7 @@ example: /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get resource type @@ -31,7 +31,7 @@ following example: /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get resource type template @@ -45,4 +45,4 @@ appear in a template, as shown in the following example: /** @var $resourceTypeTemplate string **/ $resourceTypeTemplate = $resourceType->getTemplate(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resources.rst b/doc/services/orchestration/resources.rst index 049150f58..b04115122 100644 --- a/doc/services/orchestration/resources.rst +++ b/doc/services/orchestration/resources.rst @@ -19,7 +19,7 @@ example: /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource @@ -33,7 +33,7 @@ name, as shown in the following example: /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ $resource = $stack->getResource('load-balancer'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource metadata @@ -47,4 +47,4 @@ shown in the following example: /** @var $resourceMetadata \stdClass **/ $resourceMetadata = $resource->getMetadata(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst b/doc/services/orchestration/stacks.rst index 75a14cd4c..29b6c4f7f 100644 --- a/doc/services/orchestration/stacks.rst +++ b/doc/services/orchestration/stacks.rst @@ -46,7 +46,7 @@ example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Preview a stack from a template URL @@ -68,7 +68,7 @@ in the following example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create stack @@ -111,7 +111,7 @@ example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create a stack from a template URL @@ -134,7 +134,7 @@ in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List stacks ----------- @@ -149,7 +149,7 @@ following example: /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack @@ -163,7 +163,7 @@ following example: /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->getStack('simple-lamp-setup'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack template @@ -178,7 +178,7 @@ used to create the stack. /** @var $stackTemplate string **/ $stackTemplate = $stack->getTemplate(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update stack @@ -221,7 +221,7 @@ example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update Stack from Template URL @@ -243,7 +243,7 @@ in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete stack @@ -256,7 +256,7 @@ stack *and* the resources as shown in the following example: $stack->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Abandon Stack @@ -277,7 +277,7 @@ abandon the stack as shown in the following example: $abandonStackData = $stack->abandon(); file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Adopt stack @@ -296,4 +296,4 @@ shown in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst-e b/doc/services/orchestration/stacks.rst-e new file mode 100644 index 000000000..29b6c4f7f --- /dev/null +++ b/doc/services/orchestration/stacks.rst-e @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/templates.rst b/doc/services/orchestration/templates.rst index 5eca2519d..24bc896bc 100644 --- a/doc/services/orchestration/templates.rst +++ b/doc/services/orchestration/templates.rst @@ -31,7 +31,7 @@ file, you can validate it as shown in the following example: // Use $e->getMessage() for explanation of why template is invalid } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Validate Template from URL ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,4 +52,4 @@ following example: // Use $e->getMessage() for explanation of why template is invalid } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ From 5b1c7b0684fcce77f3a000a27f9556359e97d210 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:37:13 +0100 Subject: [PATCH 457/835] Remove MD links --- doc/services/identity/roles.rst | 57 ++++++++++++--------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/doc/services/identity/roles.rst b/doc/services/identity/roles.rst index 96a56488f..09f0600fa 100644 --- a/doc/services/identity/roles.rst +++ b/doc/services/identity/roles.rst @@ -1,9 +1,6 @@ Roles ===== -Intro ------ - A role is a personality that a user assumes when performing a specific set of operations. A role includes a set of rights and privileges. A user assuming a role inherits the rights and privileges associated with @@ -13,12 +10,6 @@ determines how to interpret a user's roles. A role that grants access to a list of operations or resources within one service may grant access to a completely different list when interpreted by a different service. -Setup ------ - -Role objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - Useful object properties/methods -------------------------------- @@ -37,16 +28,13 @@ List roles This call lists the global roles available within a specified service. -.. code:: php - - $roles = $service->getRoles(); +.. code-block:: php - foreach ($roles as $role) { - // ... - } + $roles = $service->getRoles(); -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. + foreach ($roles as $role) { + // ... + } Get role -------- @@ -54,39 +42,36 @@ Get role This call lists detailed information (id, name, description) for a specified role. -.. code:: php +.. code-block:: php + + $role = $service->getRole('{roleId}'); - $roleId = '123abc'; - $role = $service->getRole($roleId); Add/delete user roles --------------------- -To add/remove user roles, you must first instantiate a -`user `__ object: +.. code-block:: php + + $user = $service->getUser('{userId}'); -.. code:: php + $roleId = '{roleId}'; - $roleId = '123abc'; + // add role to user + $user->addRole($roleId); - // add role to user - $user->addRole($roleId); + // remove role from user + $user->removeRole($roleId); - // remove role from user - $user->removeRole($roleId); List user global roles ---------------------- This call returns a list of global roles associated with a user: -.. code:: php - - $roles = $user->getRoles(); +.. code-block:: php - foreach ($roles as $role) { - // ... - } + $roles = $user->getRoles(); -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. + foreach ($roles as $role) { + // ... + } From fa11f19aed4c95442bb0eee88fba3c1d775d216b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:43:57 +0100 Subject: [PATCH 458/835] Add Security Groups docs --- doc/services/networking/index.rst | 10 +++ .../networking/security-group-rules.rst | 61 +++++++++++++++++ doc/services/networking/security-groups.rst | 67 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 doc/services/networking/security-group-rules.rst create mode 100644 doc/services/networking/security-groups.rst diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 515f9ddce..335f6a10c 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -23,6 +23,8 @@ Operations networks subnets ports + security-groups + security-group-rules Glossary -------- @@ -49,6 +51,14 @@ Glossary associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + security group + A security group is a named container for security group rules. + + security group rule + A security group rule provides users the ability to specify the types of + traffic that are allowed to pass through to and from ports on a virtual + server instance. + Further links ------------- diff --git a/doc/services/networking/security-group-rules.rst b/doc/services/networking/security-group-rules.rst new file mode 100644 index 000000000..1ca6041d1 --- /dev/null +++ b/doc/services/networking/security-group-rules.rst @@ -0,0 +1,61 @@ +Security Group Rules +==================== + +Create a security group rule +---------------------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================================================================================================================================================================+=======================================+=============+=================+============================================+ +| ``securityGroupId`` | The security group ID to associate with this security group rule. | String | Yes | - | ``2076db17-a522-4506-91de-c6dd8e837028`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``direction`` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (``ingress`` or ``egress``) | Yes | - | ``ingress`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``ethertype`` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (``IPv4`` or ``IPv6``) | No | ``IPv4`` | ``IPv6`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMin`` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the ``portRangeMax`` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMax`` | The maximum port number in the range that is matched by the security group rule. The port\_range\_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``protocol`` | The protocol that is matched by the security group rule. | String (``tcp``, ``udp``, ``icmp``) | No | ``null`` | ``tcp`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteGroupId`` | The remote group ID to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``85cc3048-abc3-43cc-89b3-377341426ac5`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteIpPrefix`` | The remote IP prefix to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``192.168.5.0`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ + +You can create a security group rule as shown in the following example: + +.. code:: php + + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + $securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + +`Get the executable PHP script for this example `_ + + +List security group rules +------------------------- + +You can list all the security group rules to which you have access as +shown in the following example: + +.. code:: php + + $securityGroupRules = $networkingService->listSecurityGroupRules(); + foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + } + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/security-groups.rst b/doc/services/networking/security-groups.rst new file mode 100644 index 000000000..2ec8b384d --- /dev/null +++ b/doc/services/networking/security-groups.rst @@ -0,0 +1,67 @@ +Security Groups +=============== + +Create a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+================================================================================+=============+=============+=================+=====================================+ +| ``name`` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | ``new-webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| ``description`` | Description of the security group. | String | No | ``null`` | ``security group for webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ + +You can create a security group as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + +`Get the executable PHP script for this example `_ + +List security groups +~~~~~~~~~~~~~~~~~~~~ + +You can list all the security groups to which you have access as shown +in the following example: + +.. code-block:: php + + $securityGroups = $networkingService->listSecurityGroups(); + foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + } + +`Get the executable PHP script for this example `_ + +Get a security group +~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific security group by using that security +group’s ID, as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); + +`Get the executable PHP script for this example `_ + +Delete a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete a security group as shown in the following example: + +.. code-block:: php + + $securityGroup->delete(); + +`Get the executable PHP script for this example `_ From 48a9d50cb0f85e86631d72fbcbb5eb8efda2a082 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 16:54:07 +0100 Subject: [PATCH 459/835] Fixing relative links --- doc/services/networking/security-groups.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/services/networking/security-groups.rst b/doc/services/networking/security-groups.rst index 2ec8b384d..cde5c9a2d 100644 --- a/doc/services/networking/security-groups.rst +++ b/doc/services/networking/security-groups.rst @@ -40,7 +40,7 @@ in the following example: /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a security group ~~~~~~~~~~~~~~~~~~~~ @@ -53,7 +53,7 @@ group’s ID, as shown in the following example: /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a security group ~~~~~~~~~~~~~~~~~~~~~~~ @@ -64,4 +64,4 @@ You can delete a security group as shown in the following example: $securityGroup->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ From 0d0824764545255a0b097a8863a1d72975979ee3 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 17:05:37 +0100 Subject: [PATCH 460/835] Use :doc: to link to inline documents --- doc/index.rst | 6 +++--- doc/services/identity/roles.rst | 1 + doc/services/identity/tokens.rst | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 297a64c6c..fc7c280c3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,9 +26,9 @@ This assumes your application's PHP files are located in the same folder as ``vendor/``. If your files are located elsewhere, please supply the path to ``vendor/autoload.php`` in the require statement above. -Read the `OpenStack Getting Started guide `_ -or `Rackspace Getting Started guide `_ to help -you get started with basic Compute operations. +Read the :doc:`getting-started-with-openstack` or +:doc:`getting-started-with-rackspace` to help you get started with basic +Compute operations. Services -------- diff --git a/doc/services/identity/roles.rst b/doc/services/identity/roles.rst index 09f0600fa..614518b9b 100644 --- a/doc/services/identity/roles.rst +++ b/doc/services/identity/roles.rst @@ -36,6 +36,7 @@ This call lists the global roles available within a specified service. // ... } + Get role -------- diff --git a/doc/services/identity/tokens.rst b/doc/services/identity/tokens.rst index f49484105..e4601c7c4 100644 --- a/doc/services/identity/tokens.rst +++ b/doc/services/identity/tokens.rst @@ -76,7 +76,7 @@ Interacting with users $user = $service->resource('User', $data); To see which methods you can call on ``$user`` (which implements -``OpenCloud\Identity\Resource\User``), see our `user documentation `_ +``OpenCloud\Identity\Resource\User``), see our :doc:`user documentation ` which accompanies this guide. @@ -89,7 +89,7 @@ Interacting with tenants $tenant = $service->resource('Tenant', $data); To see which methods you can call on ``$tenant`` (which implements -``OpenCloud\Identity\Resource\Tenant``), see our `user documentation `_ +``OpenCloud\Identity\Resource\Tenant``), see our :doc:`user documentation ` which accompanies this guide. From 96778b306f44272097f672cb70e66f0ee686989e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 18:23:21 +0100 Subject: [PATCH 461/835] Adding sphinx extension --- doc/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/requirements.txt diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000..9873100ea --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1 @@ +sphinxcontrib.phpdomain From 34c5b3f7f2eef197033aa4816b001c4f75e57c74 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 18:31:28 +0100 Subject: [PATCH 462/835] Use correct ext name --- doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 9873100ea..3f092f372 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1 +1 @@ -sphinxcontrib.phpdomain +sphinxcontrib-phpdomain From 5824ee08f7f497034be6f1c134c1b4896566fc0f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 11:44:00 +0100 Subject: [PATCH 463/835] Adding sample links --- doc/services/compute/flavors.rst | 2 + doc/services/compute/images.rst | 3 + doc/services/compute/keypairs.rst | 5 + doc/services/compute/servers.rst | 29 +- doc/services/identity/users.rst | 14 + doc/services/load-balancer/access.rst | 7 +- doc/services/load-balancer/load-balancer.rst | 14 +- doc/services/load-balancer/ssl.rst | 2 + doc/services/object-store/access.rst | 11 +- doc/services/object-store/account.rst | 6 + doc/services/object-store/cdn.rst | 6 + doc/services/object-store/containers.rst | 22 ++ doc/services/object-store/objects.rst | 22 ++ doc/services/orchestration/stacks.rst-e | 299 ------------------- doc/services/queues/claims.rst | 2 + doc/services/queues/messages.rst | 2 + doc/services/queues/queues.rst | 11 + doc/services/volume/snapshots.rst | 8 + doc/services/volume/volumes.rst | 9 + 19 files changed, 162 insertions(+), 312 deletions(-) delete mode 100644 doc/services/orchestration/stacks.rst-e diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst index eeb447d3c..470ce8368 100644 --- a/doc/services/compute/flavors.rst +++ b/doc/services/compute/flavors.rst @@ -20,6 +20,8 @@ List flavors /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ } +`Get the executable PHP script for this example `_ + Detailed results ~~~~~~~~~~~~~~~~ diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst index fb68af7f7..570790c04 100644 --- a/doc/services/compute/images.rst +++ b/doc/services/compute/images.rst @@ -23,6 +23,9 @@ Below is the simplest usage for retrieving a list of images: } +`Get the executable PHP script for this example `_ + + Detailed results ~~~~~~~~~~~~~~~~ diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst index f7e67b113..823fcf5a2 100644 --- a/doc/services/compute/keypairs.rst +++ b/doc/services/compute/keypairs.rst @@ -21,6 +21,8 @@ value is automatically generated for you. $pubKey = $keypair->getPublicKey(); $priKey = $keypair->getPrivateKey(); +`Get the executable PHP script for this example `_ + Upload existing keypair ----------------------- @@ -41,6 +43,9 @@ filesystem. 'publicKey' => $content )); +`Get the executable PHP script for this example `_ + + List keypairs ------------- diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 229e8e4ff..8a55ce57f 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -49,6 +49,9 @@ URL parameters for filtering servers | RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | +--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +`Get the executable PHP script for this example `_ + + Create server ------------- @@ -59,19 +62,22 @@ Now we're ready to create our instance: .. code-block:: php - $server = $compute->server(); + $server = $compute->server(); - $server->create(array( - 'name' => 'My lovely server', - 'imageId' => '{imageId}', - 'flavorId' => '{flavorId}', - )); + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. +`Get the executable PHP script for this example `_ + + Using a bootable volume ~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,6 +104,9 @@ you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. +`Get the executable PHP script for this example `_ + + Create parameters ~~~~~~~~~~~~~~~~~ @@ -146,6 +155,8 @@ as usual, with one extra parameter: So, as you can see, you specify the **name** of an existing keypair that you previously created on the API. +`Get the executable PHP script for this example `_ + Creating a server with personality files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,6 +187,10 @@ attributes are detailed in the next section. 'name' => 'NEW SERVER NAME' )); + +`Get the executable PHP script for this example `_ + + Updatable attributes ~~~~~~~~~~~~~~~~~~~~ @@ -195,3 +210,5 @@ Delete server .. code-block:: php $server->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/identity/users.rst b/doc/services/identity/users.rst index b55236a74..944888902 100644 --- a/doc/services/identity/users.rst +++ b/doc/services/identity/users.rst @@ -34,6 +34,8 @@ List users // ... } +`Get the executable PHP script for this example `_ + Retrieve a user by username --------------------------- @@ -42,6 +44,8 @@ Retrieve a user by username $user = $service->getUser('jamie'); +`Get the executable PHP script for this example `_ + Retrieve a user by user ID -------------------------- @@ -52,6 +56,8 @@ Retrieve a user by user ID $user = $service->getUser('{userId}', UserConst::MODE_ID); +`Get the executable PHP script for this example `_ + Retrieve a user by email address -------------------------------- @@ -62,6 +68,8 @@ Retrieve a user by email address $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); +`Get the executable PHP script for this example `_ + Create user ----------- @@ -98,6 +106,8 @@ automatically generated and provided in the response. // show generated password echo $user->getPassword(); +`Get the executable PHP script for this example `_ + Update user ----------- @@ -129,6 +139,8 @@ Delete user $user->delete(); +`Get the executable PHP script for this example `_ + List credentials ---------------- @@ -159,3 +171,5 @@ you: $user->resetApiKey(); echo $user->getApiKey(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/load-balancer/access.rst b/doc/services/load-balancer/access.rst index 4387dd8de..42cd68315 100644 --- a/doc/services/load-balancer/access.rst +++ b/doc/services/load-balancer/access.rst @@ -68,6 +68,11 @@ You can add network items to a load balancer's access list very easily: In the above example, we allowed access for 1 IP address, and used the "0.0.0.0" wildcard to blacklist all other traffic. +Get the executable PHP scripts for this example: + +* `Blacklist IP range `_ +* `Limit access to 1 IP `_ + Remove Network Item From Access List ------------------------------------ @@ -76,4 +81,4 @@ You an remove a network item from a load balancer's access list: .. code-block:: php - $networkItem->delete(); + $networkItem->delete(); diff --git a/doc/services/load-balancer/load-balancer.rst b/doc/services/load-balancer/load-balancer.rst index 7071794df..d7867755d 100644 --- a/doc/services/load-balancer/load-balancer.rst +++ b/doc/services/load-balancer/load-balancer.rst @@ -51,9 +51,11 @@ port number, before submitting to the API: For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ please see the sections below. +`Get the executable PHP script for this example `_ -List Load Balancer Details --------------------------- + +Get Load Balancer Details +------------------------- You can retrieve a single load balancer's details by using its ID: @@ -64,7 +66,7 @@ You can retrieve a single load balancer's details by using its ID: List Load Balancers -~~~~~~~~~~~~~~~~~~~ +------------------- You can retrieve a list of all your load balancers: @@ -76,6 +78,8 @@ You can retrieve a list of all your load balancers: /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ } +`Get the executable PHP script for this example `_ + Update a Load Balancer ---------------------- @@ -121,6 +125,8 @@ When you no longer have a need for the load balancer, you can remove it: $loadBalancer->delete(); +`Get the executable PHP script for this example `_ + Protocols --------- @@ -159,7 +165,7 @@ You can programmatically list all supported load balancing algorithms: .. code-block:: php $algorithms = $service->algorithmList(); - + foreach ($algorithms as $algorithm) { /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ } diff --git a/doc/services/load-balancer/ssl.rst b/doc/services/load-balancer/ssl.rst index 12b28a6af..413bdc612 100644 --- a/doc/services/load-balancer/ssl.rst +++ b/doc/services/load-balancer/ssl.rst @@ -43,6 +43,8 @@ For a full list, with explanations, of required and optional attributes, please consult the `official documentation `__ +`Get the executable PHP script for this example `_ + Delete configuration -------------------- diff --git a/doc/services/object-store/access.rst b/doc/services/object-store/access.rst index 62cb541ca..40fe61670 100644 --- a/doc/services/object-store/access.rst +++ b/doc/services/object-store/access.rst @@ -23,6 +23,11 @@ in a global state: The string argument of ``setTempUrlSecret()`` is optional - if left out, the SDK will generate a random hashed secret for you. +Get the executable PHP script for this example: + +* `Specify a URL secret `_ +* `Generate random URL secret `_ + Create a temporary URL ---------------------- @@ -32,14 +37,16 @@ your object. To allow GET access to your object for 1 minute: .. code-block:: php - $object->getTemporaryUrl(60, 'GET'); + $object->getTemporaryUrl(60, 'GET'); To allow PUT access for 1 hour: .. code-block:: php - $object->getTemporaryUrl(360, 'PUT'); + $object->getTemporaryUrl(360, 'PUT'); + +`Get the executable PHP script for this example `_ Hosting HTML sites on CDN diff --git a/doc/services/object-store/account.rst b/doc/services/object-store/account.rst index fb51c8855..50c17f1a8 100644 --- a/doc/services/object-store/account.rst +++ b/doc/services/object-store/account.rst @@ -29,6 +29,8 @@ Retrieve total container count $account->getContainerCount(); +`Get the executable PHP script for this example `_ + Retrieve total object count --------------------- @@ -37,6 +39,8 @@ Retrieve total object count $account->getObjectCount(); +`Get the executable PHP script for this example `_ + Retrieve total bytes used ------------------------- @@ -44,3 +48,5 @@ Retrieve total bytes used .. code-block:: php $account->getBytesUsed(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst index 5ad72b6cd..eb29bb27f 100644 --- a/doc/services/object-store/cdn.rst +++ b/doc/services/object-store/cdn.rst @@ -29,6 +29,8 @@ execute the method on: /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ } +`Get the executable PHP script for this example `_ + CDN-enable a container ---------------------- @@ -47,6 +49,8 @@ refetches and caches the object for the TTL period. $container->enableCdn(); +`Get the executable PHP script for this example `_ + CDN-disable a container ----------------------- @@ -55,6 +59,8 @@ CDN-disable a container $container->disableCdn(); +`Get the executable PHP script for this example `_ + Operations on CDN-enabled containers ------------------------------------ diff --git a/doc/services/object-store/containers.rst b/doc/services/object-store/containers.rst index 4b9c66ac3..a2f0485e8 100644 --- a/doc/services/object-store/containers.rst +++ b/doc/services/object-store/containers.rst @@ -22,6 +22,9 @@ Forward slashes are not currently permitted. (such as spaces or non-English characters), you must ensure they are encoded with `urlencode `_ before passing them in +`Get the executable PHP script for this example `_ + + List containers --------------- @@ -42,6 +45,8 @@ memcmp() function, regardless of text encoding. The list is limited to 10,000 containers at a time. To work with larger collections, please read the next section. +`Get the executable PHP script for this example `_ + Filtering large collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -82,6 +87,8 @@ To retrieve a certain container: /** @param $container OpenCloud\ObjectStore\Resource\Container */ $container = $service->getContainer('{containerName}'); +`Get the executable PHP script for this example `_ + Retrieve a container's name ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,6 +105,8 @@ Retrieve a container's object count $count = $container->getObjectCount(); +`Get the executable PHP script for this example `_ + Retrieve a container's total bytes used ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -106,6 +115,8 @@ Retrieve a container's total bytes used $bytes = $container->getBytesUsed(); +`Get the executable PHP script for this example `_ + Delete container ---------------- @@ -129,6 +140,8 @@ before deleting it. This is done for you if you set the You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ first, and then call ``delete``. +`Get the executable PHP script for this example `_ + Deleting all objects inside a container --------------------------------------- @@ -137,6 +150,8 @@ Deleting all objects inside a container $container->deleteAllObjects(); +`Get the executable PHP script for this example `_ + Create or update container metadata ----------------------------------- @@ -158,6 +173,8 @@ to the current metadata: 'Publisher' => 'Hogarth' )); +`Get the executable PHP script for this example `_ + Container quotas ---------------- @@ -184,6 +201,11 @@ And to retrieve them: echo $container->getCountQuota(); echo $container->getBytesQuota(); +Get the executable PHP scripts for this example: + +* `Set bytes quota `_ +* `Set count quota `_ + Access log delivery ------------------- diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index e38e0eb8b..0eb0ea836 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -43,6 +43,8 @@ its path: The resource handle will be automatically closed by Guzzle in its destructor, so there is no need to execute ``fclose``. +`Get the executable PHP script for this example `_ + Upload a single file (under 5GB) with metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -80,6 +82,8 @@ file handle resource, or a string representation of object content (a temporary resource will be created in memory), and the third is an array of additional headers. +`Get the executable PHP script for this example `_ + Batch upload multiple files (each under 5GB) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -108,6 +112,8 @@ also specify *either* a path key (to an existing file), or a ``body``. The ``body`` can either be a PHP resource or a string representation of the content you want to upload. +`Get the executable PHP script for this example `_ + Upload large files (over 5GB) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -142,6 +148,8 @@ In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. To find out more details, please consult the `official documentation `_. +`Get the executable PHP script for this example `_ + List objects in a container --------------------------- @@ -166,6 +174,8 @@ docs objectList(array('prefix' => 'logFile_')); +`Get the executable PHP script for this example `_ + Get object ---------- @@ -313,6 +323,8 @@ destination path: Where ``container_2`` is the name of the container, and ``new_object_name`` is the name of the object inside the container that does not exist yet. +`Get the executable PHP script for this example `_ + Get object metadata ------------------- @@ -346,6 +358,8 @@ You can also update to get the latest metadata: $object->retrieveMetadata(); +`Get the executable PHP script for this example `_ + Update object metadata ---------------------- @@ -386,6 +400,8 @@ you want to append values to your metadata, use the correct method: $object->saveMetadata($metadata); +`Get the executable PHP script for this example `_ + Extract archive --------------- @@ -406,6 +422,8 @@ the first argument). If you do this, the API will create the containers necessary to house the extracted files - this is done based on the filenames inside the archive. +`Get the executable PHP script for this example `_ + Delete object ------------- @@ -414,6 +432,8 @@ Delete object $object->delete(); +`Get the executable PHP script for this example `_ + Delete multiple objects ----------------------- @@ -425,3 +445,5 @@ Bulk delete a set of paths: $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); $service->bulkDelete($pathsToBeDeleted); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst-e b/doc/services/orchestration/stacks.rst-e deleted file mode 100644 index 29b6c4f7f..000000000 --- a/doc/services/orchestration/stacks.rst-e +++ /dev/null @@ -1,299 +0,0 @@ -Stacks -====== - -A stack is a running instance of a template. When a stack is created, -the `resources <#stack-resources>`__ specified in the template are -created. - - -Preview stack -------------- - -Before you create a stack from a template, you might want to see what -that stack will look like. This is called *previewing the stack*. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Preview a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to preview a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - -`Get the executable PHP script for this example `_ - - -Preview a stack from a template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to preview a stack as shown -in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - -`Get the executable PHP script for this example `_ - - -Create stack ------------- - -You can create a stack from a template. This operation takes one parameter, an -associative array, with the following keys: - -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Create a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to create a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Create a stack from a template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to create a stack as shown -in the following example: - -.. code-block:: php - - $stack = $orchestrationService->stack(); - $stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - -List stacks ------------ - -You can list all the stacks that you have created as shown in the -following example: - -.. code-block:: php - - $stacks = $orchestrationService->listStacks(); - foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - } - -`Get the executable PHP script for this example `_ - - -Get stack ---------- - -You can retrieve a specific stack using its name, as shown in the -following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->getStack('simple-lamp-setup'); - -`Get the executable PHP script for this example `_ - - -Get stack template ------------------- - -You can retrieve the template used to create a stack. Note that a JSON -string is returned, regardless of whether a JSON or YAML template was -used to create the stack. - -.. code-block:: php - - /** @var $stackTemplate string **/ - $stackTemplate = $stack->getTemplate(); - -`Get the executable PHP script for this example `_ - - -Update stack ------------- - -You can update a running stack. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ - - -Update a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to update a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Update Stack from Template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to update a stack as shown -in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Delete stack ------------- - -If you no longer need a stack and all its resources, you can delete the -stack *and* the resources as shown in the following example: - -.. code-block:: php - - $stack->delete(); - -`Get the executable PHP script for this example `_ - - -Abandon Stack -------------- - -.. note:: - - This operation returns data about the abandoned stack as a string. You can - use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ - operation. - -If you want to delete a stack but preserve all its resources, you can -abandon the stack as shown in the following example: - -.. code-block:: php - - /** @var $abandonStackData string **/ - $abandonStackData = $stack->abandon(); - file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); - -`Get the executable PHP script for this example `_ - - -Adopt stack ------------ - -If you have data from an abandoned stack, you can re-create the stack as -shown in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ diff --git a/doc/services/queues/claims.rst b/doc/services/queues/claims.rst index 7e375c8d3..1111cb9eb 100644 --- a/doc/services/queues/claims.rst +++ b/doc/services/queues/claims.rst @@ -67,6 +67,8 @@ limit parameter is optional. 'ttl' => 5 * Datetime::MINUTE )); +`Get the executable PHP script for this example `_ + Query claim ----------- diff --git a/doc/services/queues/messages.rst b/doc/services/queues/messages.rst index ea23caea3..3a5dee89d 100644 --- a/doc/services/queues/messages.rst +++ b/doc/services/queues/messages.rst @@ -50,6 +50,8 @@ Posting a single message 'ttl' => 2 * Datetime::DAY )); +`Get the executable PHP script for this example `_ + Post a batch of messages ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/services/queues/queues.rst b/doc/services/queues/queues.rst index 7958adfae..f324e3f3b 100644 --- a/doc/services/queues/queues.rst +++ b/doc/services/queues/queues.rst @@ -64,6 +64,8 @@ digits, underscores, and hyphens. $queue = $service->createQueue('new_queue'); +`Get the executable PHP script for this example `_ + Find queue details ------------------ @@ -122,3 +124,12 @@ in the queue, categorized by status. .. code-block:: php $queue->getStats(); + +Delete queue +------------ + +.. code-block:: php + + $queue->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/volume/snapshots.rst b/doc/services/volume/snapshots.rst index c2d8ef6cc..555824a9d 100644 --- a/doc/services/volume/snapshots.rst +++ b/doc/services/volume/snapshots.rst @@ -19,6 +19,8 @@ to create one: 'volume_id' => $volume->id )); +`Get the executable PHP script for this example `_ + List snapshots -------------- @@ -31,6 +33,8 @@ List snapshots /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ } +`Get the executable PHP script for this example `_ + To get details on a single snapshot ----------------------------------- @@ -39,6 +43,8 @@ To get details on a single snapshot $snapshot = $dallas->snapshot('{snapshotId}'); +`Get the executable PHP script for this example `_ + To delete a snapshot -------------------- @@ -46,3 +52,5 @@ To delete a snapshot .. code-block:: php $snapshot->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/volume/volumes.rst b/doc/services/volume/volumes.rst index 6a35bb135..a84ed0421 100644 --- a/doc/services/volume/volumes.rst +++ b/doc/services/volume/volumes.rst @@ -19,6 +19,9 @@ parameters are optional: 'display_description' => 'Used for large object storage' )); +`Get the executable PHP script for this example `_ + + List volumes ------------ @@ -30,6 +33,8 @@ List volumes /** @param $volumeType OpenCloud\Volume\Resource\Volume */ } +`Get the executable PHP script for this example `_ + Get details on a single volume ------------------------------ @@ -42,6 +47,8 @@ information on the specified volume: $volume = $dallas->volume(''); echo $volume->size; +`Get the executable PHP script for this example `_ + To delete a volume ------------------ @@ -50,6 +57,8 @@ To delete a volume $volume->delete(); +`Get the executable PHP script for this example `_ + Attach a volume to a server --------------------------- From 532b258e90c3e1ccd58ed12e90637cec080210c6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 11:45:55 +0100 Subject: [PATCH 464/835] Add list CDN containers sample file --- samples/ObjectStore/list-cdn-containers.php | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 samples/ObjectStore/list-cdn-containers.php diff --git a/samples/ObjectStore/list-cdn-containers.php b/samples/ObjectStore/list-cdn-containers.php new file mode 100644 index 000000000..a71a8bed9 --- /dev/null +++ b/samples/ObjectStore/list-cdn-containers.php @@ -0,0 +1,40 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Object a CDN service object +$cdnService = $objectStoreService->getCdnService(); + +// 4. Get container list. +$containers = $cdnService->listContainers(); + +foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\CDNContainer **/ +} From 49536c268839a5ae1d027c54d58740d5329517c7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Mar 2015 10:09:13 -0700 Subject: [PATCH 465/835] Adding smoke test to apply security group to port. --- tests/OpenCloud/Smoke/Unit/Networking.php | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 8ccd30a94..02ed838bb 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -272,6 +272,30 @@ protected function testSecurityGroupOperations() $securityGroup = $this->getService()->getSecurityGroup($securityGroup->getId()); $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + + $network1 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_port_sg' + )); + $this->cleanupNetworkIds[] = $network1->getId(); + + $subnet1 = $this->getService()->createSubnet(array( + 'cidr' => '192.165.66.0/25', + 'networkId' => $network1->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet_for_test_port_sg' + )); + $this->cleanupSubnetIds[] = $subnet1->getId(); + + $port1 = $this->getService()->createPort(array( + 'networkId' => $network1->getId(), + 'name' => 'test_port_for_test_port_sg' + )); + $this->cleanupPortIds[] = $port1->getId(); + + $this->step('Apply security group to port'); + $port1->update(array( + 'securityGroups' => array($securityGroup->getId()) + )); } protected function testSecurityGroupRuleOperations() From ce882043affae1086789f9d6466d297fcb761534 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Mar 2015 10:21:52 -0700 Subject: [PATCH 466/835] Minor formatting fix so code block renders correctly. --- doc/getting-started-with-rackspace.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/getting-started-with-rackspace.rst b/doc/getting-started-with-rackspace.rst index 35317a3e6..73aa3a8d4 100644 --- a/doc/getting-started-with-rackspace.rst +++ b/doc/getting-started-with-rackspace.rst @@ -138,7 +138,7 @@ Okay, you're ready to spin up a server: .. code-block:: php -use Guzzle\Http\Exception\BadResponseException; + use Guzzle\Http\Exception\BadResponseException; $server = $compute->server(); From 29a61e4cde170638c1669ca2bacade18d8f7b035 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 13:10:35 +0100 Subject: [PATCH 467/835] Add deprecation and redirect links to existing docs --- doc/services/compute/servers.rst | 36 + docs/getting-started-openstack.md | 200 +----- docs/getting-started.md | 182 +---- docs/userguide/Autoscale/Config.md | 46 +- docs/userguide/Autoscale/Groups.md | 95 +-- docs/userguide/Autoscale/Policies.md | 67 +- docs/userguide/Autoscale/Webhooks.md | 51 +- docs/userguide/Clients.md | 148 +---- docs/userguide/CloudMonitoring/Agents.md | 148 +---- docs/userguide/CloudMonitoring/Alarms.md | 55 +- docs/userguide/CloudMonitoring/Changelogs.md | 13 +- docs/userguide/CloudMonitoring/Checks.md | 140 +--- docs/userguide/CloudMonitoring/Entities.md | 54 +- docs/userguide/CloudMonitoring/Metrics.md | 78 +-- .../CloudMonitoring/Notifications.md | 202 +----- docs/userguide/CloudMonitoring/Service.md | 15 +- docs/userguide/CloudMonitoring/Views.md | 18 +- docs/userguide/CloudMonitoring/Zones.md | 45 +- docs/userguide/Compute/Images.md | 64 +- docs/userguide/Compute/Keypair.md | 90 +-- docs/userguide/Compute/Server.md | 179 +---- docs/userguide/Compute/Service.md | 17 +- docs/userguide/DNS/Domains.md | 229 +------ docs/userguide/DNS/Limits.md | 57 +- docs/userguide/DNS/Records.md | 86 +-- docs/userguide/DNS/Reverse-DNS.md | 70 +- docs/userguide/DNS/Service.md | 10 +- docs/userguide/Database/README.md | 115 +--- docs/userguide/Debugging.md | 88 +-- docs/userguide/Identity/Roles.md | 67 +- docs/userguide/Identity/Service.md | 23 +- docs/userguide/Identity/Tenants.md | 20 +- docs/userguide/Identity/Tokens.md | 83 +-- docs/userguide/Identity/Users.md | 127 +--- docs/userguide/Image/Images.md | 96 +-- docs/userguide/Image/Schemas.md | 158 +---- docs/userguide/Image/Sharing.md | 109 +-- docs/userguide/Image/Tags.md | 22 +- docs/userguide/Iterators.md | 117 +--- docs/userguide/LoadBalancer/README.md | 79 +-- docs/userguide/LoadBalancer/USERGUIDE.md | 625 +----------------- docs/userguide/Networking/README.md | 75 +-- docs/userguide/Networking/USERGUIDE.md | 616 +---------------- docs/userguide/ObjectStore/Access.md | 65 +- docs/userguide/ObjectStore/Account.md | 27 +- docs/userguide/ObjectStore/CDN/Container.md | 77 +-- docs/userguide/ObjectStore/CDN/Object.md | 19 +- docs/userguide/ObjectStore/README.md | 64 +- .../ObjectStore/Storage/Container.md | 181 +---- .../ObjectStore/Storage/Migrating.md | 82 +-- docs/userguide/ObjectStore/Storage/Object.md | 274 +------- docs/userguide/ObjectStore/USERGUIDE.md | 624 +---------------- docs/userguide/Orchestration/README.md | 80 +-- docs/userguide/Orchestration/USERGUIDE.md | 505 +------------- docs/userguide/Queues/Claim.md | 122 +--- docs/userguide/Queues/Message.md | 206 +----- docs/userguide/Queues/Queue.md | 155 +---- docs/userguide/README.md | 73 +- docs/userguide/Services.md | 46 +- docs/userguide/accessip.md | 49 +- docs/userguide/caching-credentials.md | 38 +- docs/userguide/dbaas.md | 317 +-------- docs/userguide/flavors.md | 58 +- docs/userguide/networks.md | 101 +-- docs/userguide/servers.md | 186 +----- docs/userguide/volumes.md | 171 +---- 66 files changed, 166 insertions(+), 8169 deletions(-) diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 8a55ce57f..0709bda9a 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -204,6 +204,42 @@ Updatable attributes | accessIPv6 | The IP version 6 address. | +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Updating the access IP address(es) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example, you may have a private cloud with internal addresses in the +10.1.x range. However, you can access a server via a firewall device at +address 50.57.94.244. In this case, you can change the ``accessIPv4`` +attribute to point to the firewall: + +.. code-block:: php + + $server->update(array('accessIPv4' => '50.57.94.244')); + +When a client application retrieves the server’s information, it will +know that it needs to use the ``accessIPv4`` address to connect to the +server, and *not* the IP address assigned to one of the network +interfaces. + + +Retrieving the server’s IP address +---------------------------------- + +The ``Server::ip()`` method is used to retrieve the server’s IP address. +It has one optional parameter: the format (either IPv4 or IPv6) of the +address to return (by default, it returns the IPv4 address): + +.. code-block:: php + + // IPv4 + echo $server->ip(); + echo $server->ip(4); + + // IPv6 + echo $server->ip(6); + + Delete server ------------- diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index 6bb1ae260..24eda2779 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -1,201 +1,5 @@ # Installing the SDK -You must install through Composer, because this library has a few dependencies: +Our docs have moved! Please visit the below link: -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master -``` - -Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): - -```php -require 'vendor/autoload.php'; -``` - -And you're good to go! - -# Quick deep-dive: building some Nova instances - -In this example, you will write code that will create a Nova instance running Ubuntu. - -### 1. Setup the client and pass in your credentials - -To authenticate against Keystone: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( - 'username' => 'foo', - 'password' => 'bar', - 'tenantName' => 'baz' -)); -``` - -You will need to substitute in the public URL endpoint for your Keystone service, as well as your `username`, `password` -and `tenantName`. You can also specify your `tenantId` instead of `tenantName` if you prefer. - -### 2. Pick what service you want to use - -In this case, we want to use the Nova service: - -```php -$compute = $client->computeService('nova', 'regionOne'); -``` - -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. For OpenStack users, this must be retrieved and entered in your code. If you are unsure how to retrieve the service name, follow these steps: - -1. Setup the `$client` object, as above -2. Copy and run this code: - - ```php - $client->authenticate(); - - print_r($client->getCatalog()->getItems()); - ``` - -3. This will output all the items in your service catalog. Go through the outputted list and find your service, making note of the "name" field. This is the name you will need to enter as the first argument. You will also be able to see the available regions. - -The second argument is the region. The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. - -### 3. Select your server image - -Instances are based on "images", which are effectively just the type of operating system you want. Let's go through the -list and find an Ubuntu one: - -```php -$images = $compute->imageList(); -foreach ($images as $image) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntu = $image; - break; - } -} -``` - -Alternatively, if you already know the image ID, you can do this much easier: - -```php -$ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -``` - -## 4. Select your flavor - -There are different server specs - some which offer 1GB RAM, others which offer a much higher spec. The 'flavor' of an -instance is its hardware configuration. So if you want a 2GB instance but don't know the ID, you have to traverse the list: - -```php -$flavors = $compute->flavorList(); -foreach ($flavors as $flavor) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} -``` - -Again, it's much easier if you know the ID: - -```php -$twoGbFlavor = $compute->flavor('4'); -``` - -## 5. Thunderbirds are go! - -Okay, you're ready to spin up a server: - -```php -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntu, - 'flavor' => $twoGbFlavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf("Status: %s\nBody: %s\nHeaders: %s", $statusCode, $responseBody, implode(', ', $headers)); -} -``` - -As you can see, you're creating a server called "My lovely server" - this will take a few minutes for the build to -complete. You can always check the progress by logging into your Controller node and running: - -`nova list` - -You can also execute a polling function immediately after the `create` method that checks the build process: - -```php -use OpenCloud\Compute\Constants\ServerState; - -$callback = function($server) { - if (!empty($server->error)) { - var_dump($server->error); - exit; - } else { - echo sprintf( - "Waiting on %s/%-12s %4s%%", - $server->name(), - $server->status(), - isset($server->progress) ? $server->progress : 0 - ); - } -}; - -$server->waitFor(ServerState::ACTIVE, 600, $callback); -``` -So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. - -# More fun with Nova - -Once you've booted up your instance, you can use other API operations to monitor your Compute nodes. To list every -node on record, you can execute: - -```php -$servers = $compute->serverList(); - -foreach ($servers as $server) { - // do something with each server... - echo $server->name, PHP_EOL; -} -``` - -or, if you know a particular instance ID you can retrieve its details: - -```php -$server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); -``` - -allowing you to update its properties: - -```php -$server->update(array( - 'name' => 'New server name' -)); -``` - -or delete it entirely: - -```php -$server->delete(); -``` - -# Next steps - -Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services -you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or -[Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to contact us at https://developer.rackspace.com/support/ or open a Github issue with details. +https://doc.php-opencloud.com/en/latest/getting-started-with-openstack.html diff --git a/docs/getting-started.md b/docs/getting-started.md index e661e0ce8..af65271fe 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,183 +1,5 @@ # Installing the SDK -You must install through Composer, because this library has a few dependencies: +Our docs have moved! Please visit the below link: -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master -``` - -Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): - -```php -require 'vendor/autoload.php'; -``` - -And you're good to go! - -# Quick deep-dive: building some cloud servers - -In this example, you will write code that will create a Cloud Server running Ubuntu. - -### 1. Setup the client and pass in your credentials - -To authenticate against the Rackspace API and use its services: - -```php - 'foo', - 'apiKey' => 'bar' -)); -``` - -Alternatively, if you would like to validate against your own API, or just want to access OpenStack services: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://identity.my-openstack.com/v2.0/', array( - 'username' => 'foo', - 'password' => 'bar' -)); -``` - -You can see in the first example that the constant `Rackspace::US_IDENTITY_ENDPOINT` is just a string representation of -Rackspace's identity endpoint (`https://identity.api.rackspacecloud.com/v2.0/`). Another difference is that Rackspace -uses API key for authentication, whereas OpenStack uses a generic password. - -#### 1.2 Logger injection -As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. - -Prerequisities and usage example can be found in [the Clients userguide](/docs/userguide/Clients.md#12-logger-injection) - -### 2. Pick what service you want to use - -In this case, we want to use the Compute (Nova) service: - -```php -$compute = $client->computeService('cloudServersOpenStack', 'ORD'); -``` - -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. If in doubt, you can -leave blank and it will revert to the default name for the service. The second argument is the region; you may use: - -- __DFW__ (Dallas) -- __ORD__ (Chicago) -- __IAD__ (Virginia) -- __LON__ (London) -- __HKG__ (Hong Kong) -- __SYD__ (Sydney) - -The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. If you select `internalUrl` -all API traffic will use ServiceNet (internal IPs) and will receive a performance boost. - -### 3. Select your server image - -Servers are based on "images", which are effectively just the type of operating system you want. Let's go through the list -and find an Ubuntu one: - -```php -$images = $compute->imageList(); -while ($image = $images->next()) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntu = $image; - break; - } -} -``` - -Alternatively, if you already know the image ID, you can do this much easier: - -```php -$ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -``` - -## 4. Select your flavor - -There are different server specs - some which offer 1GB RAM, others which offer a much higher spec. The 'flavor' of a -server is its hardware configuration. So if you want a 2GB instance but don't know the ID, you have to traverse the list: - -```php -$flavors = $compute->flavorList(); -while ($flavor = $flavors->next()) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} -``` - -Again, it's much easier if you know the ID: - -```php -$twoGbFlavor = $compute->flavor('4'); -``` - -## 5. Thunderbirds are go! - -Okay, you're ready to spin up a server: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntu, - 'flavor' => $twoGbFlavor, - 'networks' => array( - $compute->network(Network::RAX_PUBLIC), - $compute->network(Network::RAX_PRIVATE) - ) - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf("Status: %s\nBody: %s\nHeaders: %s", $statusCode, $responseBody, implode(', ', $headers)); -} -``` - -As you can see, you're creating a server called "My lovely server", and you've inserted it in two networks: the Rackspace -private network (ServiceNet), and the Rackspace public network (for Internet connectivity). This will take a few -minutes for the build to complete. - -You can also call a polling function that checks on the build process: - -```php -use OpenCloud\Compute\Constants\ServerState; - -$callback = function($server) { - if (!empty($server->error)) { - var_dump($server->error); - exit; - } else { - echo sprintf( - "Waiting on %s/%-12s %4s%%", - $server->name(), - $server->status(), - isset($server->progress) ? $server->progress : 0 - ); - } -}; - -$server->waitFor(ServerState::ACTIVE, 600, $callback); -``` -So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. +https://doc.php-opencloud.com/en/latest/getting-started-with-rackspace.html diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md index a619bf277..02f2e891d 100644 --- a/docs/userguide/Autoscale/Config.md +++ b/docs/userguide/Autoscale/Config.md @@ -1,47 +1,5 @@ # Group configurations -##Intro +Our docs have moved! Please visit the below link: -There are two types of configuration associated with an Auto Scale group: - -- **Group configuration**. Outlines the basic elements of the Auto Scale configuration. The group configuration manages how many servers can participate in the scaling group. It sets a minimum and maximum limit for the number of entities that can be used in the scaling process. It also specifies information related to load balancers. - -- **Launch configuration**. Creates a blueprint for how new servers will be created. The launch configuration specifies what type of server image will be started on launch, what flavor the new server is, and which load balancer the new server connects to. - -## Setup - -To interact with the configuration of a scaling group, you will need to setup the group object beforehand: - -```php -$groupId = 'e41380ae-173c-4b40-848a-25c16d7fa83d'; -$group = $service->getGroup($groupId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [groups](). - -## Get group/launch configuration - -```php -$groupConfig = $group->getGroupConfig(); -$launchConfig = $group->getLaunchConfig(); -``` - -## Edit group/launch configuration - -```php -$groupConfig->update(array( - 'name' => 'New name!' -)); - -$launchConfig = $group->getLaunchConfig(); - -$server = $launchConfig->args->server; -$server->name = "BRAND NEW SERVER NAME"; - -$launchConfig->update(array - 'args' => array( - 'server' => $server, - 'loadBalancers' => $launchConfig->args->loadBalancers - ) -)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/group-config.html diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md index 98dfb43dc..7ec7aa269 100644 --- a/docs/userguide/Autoscale/Groups.md +++ b/docs/userguide/Autoscale/Groups.md @@ -1,96 +1,5 @@ #Auto Scale groups -## Intro +Our docs have moved! Please visit the below link: -The scaling group is at the heart of an Auto Scale deployment. The scaling group specifies the basic elements of the Auto Scale configuration. It manages how many servers can participate in the scaling group. It also specifies information related to load balancers if your configuration uses a load balancer. - -## Service setup - -Nothing special here; you setup your client and service objects in the same way as every other resource: - -```php -$service = $client->autoscaleService(); -``` - -Please consult the [client doc](docs/userguide/Client.md) for more information about creating clients. - -## List all groups - -```php -$groups = $service->groupList(); -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -## Retrieve one group by ID - -```php -$group = $service->group('605e13f6-1452-4588-b5da-ac6bb468c5bf'); -``` - -## Create a new group - -You can create a new scaling group in two ways: either pass in a JSON string (easier option); or instantiate a new object and manually set its properties (a lot more verbose) - -```php - -// Easy way: - -$jsonString = <<create($jsonString); - -// More granular (and verbose) way: - -$object = new \stdClass; - -// Set the config object for this autoscale group; contains all of properties -// which determine its behaviour -$object->groupConfiguration = new \stdClass; -$object->groupConfiguration->name = 'New autoscale group'; -$object->groupConfiguration->minEntities = 5; -$object->groupConfiguration->maxEntities = 25; -$object->groupConfiguration->cooldown = 60; - -// We need specify what is going to be launched. For now, we'll launch a new server -$object->launchConfiguration = new \stdClass; -$object->launchConfiguration->type = 'launch_server'; - -// To launch a new server, we need two `args` properties: `server` and `loadBalancer` -$server = new \stdClass; -$server->flavorRef = 3; -$server->name = 'webhead'; -$server->imageRef = "0d589460-f177-4b0f-81c1-8ab8903ac7d8"; - -$loadBalancer = new \stdClass; -$loadBalancer->loadBalancerId = 2200; -$loadBalancer->port = 8081; - -$object->launchConfiguration->args = new \stdClass; -$object->launchConfiguration->args->server = $server; -$object->launchConfiguration->args->loadBalancers = array($loadBalancer); - -// Do we want particular scaling policies? -$policy = new \stdClass; -$policy->name = "scale up by 10"; -$policy->change = 10; -$policy->cooldown = 5; -$policy->type = "webhook"; - -$object->scalingPolicies = array($policy); - -$group->create($object); -``` - -## Delete an autoscale group -```php -$group->delete(); -``` - -## Get the current state of the scaling group - -```php -$group->getState(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/groups.html diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md index 6f3d1b282..7a312af50 100644 --- a/docs/userguide/Autoscale/Policies.md +++ b/docs/userguide/Autoscale/Policies.md @@ -1,68 +1,5 @@ #Policies -## Setup +Our docs have moved! Please visit the below link: -To interact with the policies of a scaling group, you will need to setup the group object beforehand. - -```php -$groupId = 'foobar'; -$group = $service->getGroup($groupId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [Autoscale groups](). - -## Get all policies - -```php -$policies = $group->getPolicies(); - -while ($policy = $policies->next()) { - // do something - echo "{$policy->name} {($policy->type)}" . PHP_EOL; -} -``` - -## Create a new scaling policy - -Creating policies is achieved through passing an array to the `create` method. - -```php -$policy = new \stdClass; -$policy->name = "NEW NAME"; -$policy->change = 1; -$policy->cooldown = 150; -$policy->type = "webhook"; - -$group->getPolicy()->create(array($policy)); - -// or even: - -$group->getPolicy()->create(array( - (object) array( - 'name' => 'NEW NAME', - 'change' => 1, - 'cooldown' => 150, - 'type' => 'webhook' - ), - // etc. -)); -``` - -## Get, update and delete a scaling policy - -```php -$policyId = 'foobar'; -$policy = $group->getPolicy($policyId); - -$policy->update(array( - 'name' => 'More relevant name' -)); - -$policy->delete(); -``` - -## Execute a scaling policy - -```php -$policy->execute(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/policies.html diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md index e634cc073..c5e6453ed 100644 --- a/docs/userguide/Autoscale/Webhooks.md +++ b/docs/userguide/Autoscale/Webhooks.md @@ -1,52 +1,5 @@ # Webhooks -## Setup +Our docs have moved! Please visit the below link: -To interact with the webhooks of a group's scaling policy, you will need to setup the group and policy objects beforehand. - -```php -$groupId = 'foo'; -$policyId = 'bar'; - -$group = $service->getGroup($groupId); -$policy = $group->getPolicy($policyId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [Autoscale groups](). - -## Get all webhooks - -```php -$webhooks = $policy->getWebookList(); -``` - -## Create a new webhook - -```php -$policy->getWebhook()->create(array( - (object) array( - 'name' => 'Alice', - 'metadata' => array( - 'firstKey' => 'foo', - 'secondKey' => 'bar' - ) - ) -)); -``` - -## Get, update and delete an individual webhook - -```php -$webhookId = 'baz'; -$webhook = $policy->getWebhook($webhookId); - -// Update the metadata -$metadata = $webhook->metadata; -$metadata->thirdKey = 'blah'; -$webhook->update(array( - 'metadata' => $metadata -)); - -// Delete it -$webhook->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/webhooks.html diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index c7270d69e..184b601f4 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -1,149 +1,5 @@ # Clients -## Overview +Our docs have moved! Please visit the below link: -A Client is the object responsible for issuing HTTP requests and receiving responses from the API. In short, it forms the core of the SDK because it controls how functionality is executed. All services depend on the client to work. - -Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\Rackspace`. The latter extends the former, meaning that much of the functionality is shared between them. The OpenStack client extends functionality from other base classes, that trace all the way back to Guzzle's root class: - -1. `Guzzle\Http\Client` -2. `OpenCloud\Common\Http\Client` -3. `OpenCloud\OpenStack` -4. `OpenCloud\Rackspace` - -## 1. Initializing a client - -### Rackspace - -First, you need to select the Identity endpoint you want to authenticate against. If you're using Rackspace, you can either use the UK or US endpoints. There are class constants defined for your convenience: - -- `OpenCloud\Rackspace::US_IDENTITY_ENDPOINT` (https://identity.api.rackspacecloud.com/v2.0) -- `OpenCloud\Rackspace::UK_IDENTITY_ENDPOINT` (https://lon.identity.api.rackspacecloud.com/v2.0) - -Then you need to find your username and apiKey. Your username will be visible at the top right of the Rackspace Control panel; and your API key can be retrieved by going to Account Settings. Once this is done: - -```php -use OpenCloud\OpenStack; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' -)); -``` - -### OpenStack - -To initialize an OpenStack client, the process is the same: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( - 'username' => 'foo', - 'password' => 'bar' -)); -``` - -#### 1.2 Logger injection -As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. - -Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. - -To inject a `LoggerInterface` compatible logger into a new `Client`: - -```php -use Monolog\Logger; -use OpenCloud\OpenStack; - -// create a log channel -$log = new Logger('name'); - -$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( - 'username' => 'foo', - 'password' => 'bar' -), array( - 'logger' => $log, -)); -``` - -## 2. Authentication - -The Client does not automatically authenticate against the API on object creation - it waits for an API call. When this happens, it checks whether the current "token" has expired, and (re-)authenticates if necessary. - -You can force authentication, by calling: - -```php -$client->authenticate(); -``` - -If the credentials are incorrect, a `401` error will be returned. If credentials are correct, a `200` status is returned with your Service Catalog. - -## 3. Service Catalog - -The Service Catalog is returned on successful authentication, and is composed of all the different API services available to the current tenant. All of this functionality is encapsulated in the `Catalog` object, which allows you greater control and interactivity. - -```php -/** @var OpenCloud\Common\Service\Catalog */ -$catalog = $client->getCatalog(); - -// Return a list of OpenCloud\Common\Service\CatalogItem objects -foreach ($catalog->getItems() as $catalogItem) { - - $name = $catalogItem->getName(); - $type = $catalogItem->getType(); - - if ($name == 'cloudServersOpenStack' && $type == 'compute') { - break; - } - - // Array of OpenCloud\Common\Service\Endpoint objects - $endpoints = $catalogItem->getEndpoints(); - foreach ($endpoints as $endpoint) { - if ($endpoint->getRegion() == 'DFW') { - echo $endpoint->getPublicUrl(); - } - } -} -``` - -As you can see, you have access to each Service's name, type and list of endpoints. Each endpoint provides access to the specific region, along with its public and private endpoint URLs. - -## 4. Default HTTP headers - -To set default HTTP headers: - -```php -$client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); -``` - -## User agents - -php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: - -> User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz - -where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: - -```php -$client->setUserAgent('MyCustomUserAgent'); -``` - -which will result in: - -> User-Agent: MyCustomUserAgent - -If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: - -```php -$client->setUserAgent('MyPrefix', true); -``` - -which will result in: - -> User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz - -where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. - -## 5. Other functionality - -For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md index 7e67e7d0b..b316d004c 100644 --- a/docs/userguide/CloudMonitoring/Agents.md +++ b/docs/userguide/CloudMonitoring/Agents.md @@ -1,149 +1,5 @@ # Agents -## Intro +Our docs have moved! Please visit the below link: -The Monitoring Agent resides on the host server being monitored. The agent allows you to gather on-host metrics based on agent checks and push them to Cloud Monitoring where you can analyze them, use them with the Cloud Monitoring infrastructure (such as alarms), and archive them. - -For more information about this feature, including a brief overview of its core design principles and security layers, see the [official API documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/service-agent.html). - -## Setup - -```php -$agentId = '00-agent.example.com'; -$agent = $service->getAgent($agentId); -``` - -You can view the [service page](Service.md) for more information about setting up the Cloud Monitoring service. - -## List agents - -```php -$agents = $service->getAgents(); - -foreach ($agents as $agent) { - echo $agent->getLastConnected(); -} -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## List connections - -```php -$connections = $agent->getConnections(); -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Get connection -```php -/** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ -$connection = $agent->getConnection('cntl4qsIbA'); -``` - -### Agent Connection properties - -Name|Description|Type|Method -----|-----------|----|------ -id|-|-|`getId()` -guid|-|-|`getGuid()` -agent_id|-|-|`getAgentId()` -endpoint|-|-|`getEndpoint()` -process_version|-|-|`getProcessVersion()` -bundle_version|-|-|`getBundleVersion()` -agent_ip|-|-|`getAgentIp()` - - -# Agent tokens - -## Intro - -Agent tokens are used to authenticate Monitoring agents to the Monitoring Service. Multiple agents can share a single token. - -## Setup -```php -$tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; -$agentToken = $service->getAgentToken($tokenId); -``` - -## Create agent token -```php -$newToken = $service->getAgentToken(); -$newToken->create(array('label' => 'Foobar')); -``` - -## List agent tokens -```php -$agentTokens = $service->getAgentTokens(); - -foreach ($agentTokens as $token) { - echo $token->getLabel(); -} -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Update and delete Agent Token -```php -// Update -$token->update(array( - 'label' => 'New label' -)); - -// Delete -$token->delete(); -``` - -# Agent Host Information - -## Info - -An agent can gather host information, such as process lists, network configuration, and memory usage, on demand. You can use the host-information API requests to gather this information for use in dashboards or other utilities. - -## Setup -```php -$host = $monitoringService->getAgentHost(); -``` - -## Get some metrics -```php -$cpuInfo = $host->info('cpus'); -$diskInfo = $host->info('disks'); -$filesystemInfo = $host->info('filesystems'); -$memoryInfo = $host->info('memory'); -$networkIntInfo = $host->info('network_interfaces'); -$processesInfo = $host->info('processes'); -$systemInfo = $host->info('system'); -$userInfo = $host->info('who'); - -// What CPU models do we have? -foreach ($cpuInfo as $cpuMetric) { - echo $cpuMetric->model, PHP_EOL; -} - -// How many disks do we have? -echo $diskInfo->count(); - -// What's the available space on our ext4 filesystem? -foreach ($filesystemInfo as $filesystemMetric) { - if ($filesystemMetric->sys_type_name == 'ext4') { - echo $filesystemMetric->avail; - } -} -``` - -# Agent targets - -## Info - -Each agent check type gathers data for a related set of target devices on the server where the agent is installed. For example, `agent.network` gathers data for network devices. The actual list of target devices is specific to the configuration of the host server. By focusing on specific targets, you can efficiently narrow the metric data that the agent gathers. - -### List agent targets -```php -$targets = $service->getAgentTargets(); - -foreach ($targets as $target) { - echo $target->getType(); -} - -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/agents.html diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md index bf4a75652..b08caf4cd 100644 --- a/docs/userguide/CloudMonitoring/Alarms.md +++ b/docs/userguide/CloudMonitoring/Alarms.md @@ -1,56 +1,5 @@ # Alarms -## Info +Our docs have moved! Please visit the below link: -Alarms bind alerting rules, entities, and notification plans into a logical unit. Alarms are responsible for determining a state (```OK```, ```WARNING``` or ```CRITICAL```) based on the result of a Check, and executing a notification plan whenever that state changes. You create alerting rules by using the alarm DSL. For information about using the alarm language, refer to the [reference documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/alerts-language.html). - -## Setup - -Alarms are sub-resources of Entities: - -```php -$alarmId = 'alAAAA'; -$alarm = $check->getAlarm(); -``` - -For more information about working with Checks, please see the [appropriate documentation](Checks.md). - -## Attributes - -Name|Description|Required?|Method ----|---|---|--- -check_id|The ID of the check to alert on.|Required|`getCheckId()` -notification_plan_id|The ID of the notification plan to execute when the state changes.|Optional|`getNotificationPlanId()` -criteria|The alarm DSL for describing alerting conditions and their output states.|Optional|`getCriteria()` -disabled|Disable processing and alerts on this alarm|Optional|`isDisabled()` <`bool`> -label|A friendly label for an alarm.|Optional|`getLabel()` -metadata|Arbitrary key/value pairs.|Optional|`getMetadata()` - -## Create Alarm -```php -$alarm->create(array( - 'check_id' => 'chAAAA', - 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', - 'notification_plan_id' => 'npAAAAA' -)); -``` - -## List Alarms -```php -$alarms = $entity->getAlarms(); - -foreach ($alarms as $alarm) { - echo $alarm->getId(); -} -``` - -## Update and delete Alarm -```php -// Update -$alarm->update(array( - 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' -)); - -// Delete -$alarm->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/alarms.html diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md index 0f8fe48d0..ff943e592 100644 --- a/docs/userguide/CloudMonitoring/Changelogs.md +++ b/docs/userguide/CloudMonitoring/Changelogs.md @@ -1,14 +1,5 @@ # Changelogs -## Info +Our docs have moved! Please visit the below link: -The monitoring service records changelogs for alarm statuses. Changelogs are accessible as a Time Series Collection. By default the API queries the last 7 days of changelog information. - -## Working with Changelogs -```php -$changelog = $service->getChangelog(); - -foreach ($changelog as $item) { - $entity = $item->getEntityId(); -} -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/changelogs.html diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md index 265c0675d..5710aa465 100644 --- a/docs/userguide/CloudMonitoring/Checks.md +++ b/docs/userguide/CloudMonitoring/Checks.md @@ -1,141 +1,5 @@ # Checks -## Info +Our docs have moved! Please visit the below link: -A check is one of the foundational building blocks of the monitoring system. The check determines the parts or pieces of the entity that you want to monitor, the monitoring frequency, how many monitoring zones are originating the check, and so on. When you create a new check in the monitoring system, you specify the following information: - -- A name for the check -- The check's parent entity -- The type of check you're creating -- Details of the check -- The monitoring zones that will launch the check - -The check, as created, will not trigger alert messages until you create an alarm to generate notifications, to enable the creation of a single alarm that acts upon multiple checks (e.g. alert if any of ten different servers stops responding) or multiple alarms off of a single check. (e.g. ensure both that a HTTPS server is responding and that it has a valid certificate). - -## Setup - -Checks are sub-resources of Entities: -```php -$checkId = 'chAAAA'; -$check = $entity->getCheck($checkId); -``` - -## Attributes - -Name|Description|Required?|Data type ----|---|---|--- -type|The type of check.|Required|Valid check type. String (1..25 chars) -details|Details specific to the check type.|Optional|Array -disabled|Disables the check.|Optional|Boolean -label|A friendly label for a check.|Optional|String (1..255 chars) -metadata|Arbitrary key/value pairs.|Optional|Array -period|The period in seconds for a check. The value must be greater than the minimum period set on your account.|Optional|Integer (30..1800) -timeout|The timeout in seconds for a check. This has to be less than the period.|Optional|Integer (2..1800) - -###Attributes used for remote Checks - -Name|Description|Required?|Data type ----|---|---|--- -monitoring_zones_poll|List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks|Optional|Array -target_alias|A key in the entity's `ip_addresses` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target_hostname.|Optional|String (1..64 chars) -target_hostname|The hostname this check should target. This parameter is mutually exclusive with `target_alias`.|Optional|Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). -target_resolver|Determines how to resolve the check target.|Optional|`IPv4` or `IPv6` - -## Test parameters (before create) -```php -$params = array( - 'type' => 'remote.http', - 'details' => array( - 'url' => 'http://example.com', - 'method' => 'GET' - ), - 'monitoring_zones_poll' => array('mzlon'), - 'period' => '100', - 'timeout' => '30', - 'target_alias' => 'default', - 'label' => 'Website check 1' -); - -// You can do a test to see what would happen -// if a Check is launched with these params -$response = $entity->testNewCheckParams($params); - -echo $response->timestamp; // When was it executed? -echo $response->available; // Was it available? -echo $response->status; // Status code -``` -## Create a Check -```php -$entity->createCheck($params); -``` - -## Test existing Check -```php -// Set arg to TRUE for debug information -$response = $check->test(true); - -echo $response->debug_info; -``` - -## List Checks -```php -$checks = $entity->getChecks(); - -foreach ($checks as $check) { - echo $check->getId(); -} -``` - -### Update and delete Check -```php -// Update -$check->update(array('period' => 500)); - -// Delete -$check->delete(); -``` - -# Check types - -## Info - -Each check within the Rackspace Cloud Monitoring has a designated check type. The check type instructs the monitoring system how to check the monitored resource. **Note:** Users cannot create, update or delete check types. - -Check types for commonly encountered web protocols, such as HTTP (```remote.http```), IMAP (```remote.imap-banner```) , SMTP (```remote.stmp```), and DNS (```remote.dns```) are provided. Monitoring commonly encountered infrastructure servers like MySQL (```remote.mysql-banner```) and PostgreSQL (```remote.postgresql-banner```) are also available. Monitoring custom server uptime can be accomplished with the remote.tcp banner check to check for a protocol-defined banner at the beginning of a connection. Gathering metrics from server software to create alerts against can be accomplished using the remote.http check type and the 'extract' attribute to define the format. - -In addition to the standard Cloud Monitoring check types, you can also use agent check types if the Monitoring Agent is installed on the server you are monitoring. For a list of available check types, see the [official API documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/appendix-check-types.html). - -Checks generate metrics that alarms will alert based upon. The metrics generated often times depend on the check's parameters. For example, using the 'extract' attribute on the remote.http check, however the default metrics will always be present. To determine the exact metrics available, the Test Check API is provided. - -## Setup - -If you want to see the type for an existing Check: - -```php -/** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ -$checkType = $check->getCheckType(); -``` - -Alternatively, you can retrieve a specific type based on its ID: - -```php -$checkTypeId = 'remote.dns'; -$checkType = $service->getCheckType($checkTypeId); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|--- -type|The name of the supported check type.|String|`getType()` -fields|Check type fields.|Array|`getFields()` -supported_platforms|Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime|Array|`getSupportedPlatforms()` - -## List all possible check types -```php -$checkTypes = $service->getCheckTypes(); - -foreach ($checkTypes as $checkType) { - echo $checkType->getId(); -} -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/checks.html diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md index c9885604b..beed807a2 100644 --- a/docs/userguide/CloudMonitoring/Entities.md +++ b/docs/userguide/CloudMonitoring/Entities.md @@ -1,55 +1,5 @@ # Entities -## Info +Our docs have moved! Please visit the below link: -An entity is the target of what you are monitoring. For example, you can create an entity to monitor your website, a particular web service, or your Rackspace server. Note that an entity represents only one item in the monitoring system -- if you wanted to monitor each server in a cluster, you would create an entity for each of the servers. You would not create a single entity to represent the entire cluster. - -An entity can have multiple checks associated with it. This allows you to check multiple services on the same host by creating multiple checks on the same entity, instead of multiple entities each with a single check. - -## Setup - -```php -$entity = $service->getEntity(); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [services](Service.md). - -## Attributes - -Name|Description|Required?|Data type|Method ----|---|---|---|--- -label|Defines a name for the entity.|Required|String (1..255 chars)|`getLabel()` -agent_id|Agent to which this entity is bound to.|Optional|String matching the regex: `/^[-\.\w]{1,255}$/`|`getAgentId()` -ip_addresses|Hash of IP addresses that can be referenced by checks on this entity.|Optional|Array|`getIpAddresses()` -metadata|Arbitrary key/value pairs that are passed during the alerting phase.|Optional|`OpenCloud\Common\Metadata`|`getMetadata()` - -## Create Entity -```php -$service->createEntity(array( - 'label' => 'Brand New Entity', - 'ip_addresses' => array( - 'default' => '127.0.0.4', - 'b' => '127.0.0.5', - 'c' => '127.0.0.6', - 'test' => '127.0.0.7' - ), - 'metadata' => array( - 'all' => 'kinds', - 'of' => 'stuff', - 'can' => 'go', - 'here' => 'null is not a valid value' - ) -)); -``` - - -## Update and delete Entity -```php -// Update -$entity->update(array( - 'label' => 'New label for my entity' -)); - -// Delete -$entity->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/entities.html diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md index 6799f8881..0b275c97e 100644 --- a/docs/userguide/CloudMonitoring/Metrics.md +++ b/docs/userguide/CloudMonitoring/Metrics.md @@ -1,79 +1,5 @@ # Metrics -## Info +Our docs have moved! Please visit the below link: -When Monitoring checks run, they generate metrics. These metrics are stored as full resolution data points in the Cloud Monitoring system. Full resolution data points are periodically rolled up (condensed) into coarser data points. - -Depending on your needs, you can use the metrics API to fetch individual data points (fine-grained) or rolled up data points (coarse-grained) over a period of time. - -## Data Granularity - -Cloud Monitoring supports several granularities of data: full resolution data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. - -When you fetch metrics data points, you specify several parameters to control the granularity of data returned: - -- A time range for the points -- Either the number of points you want returned OR the resolution of the data you want returned - -When you query by points, the API selects the resolution that will return you the number of points you requested. The API makes the assumption of a 30 second frequency, performs the calculation, and selects the appropriate resolution. - -**Note:** Because the API performs calculations to determine the points returned for a particular resolution, the number of points returned may differ from the specific number of points you request. - -Consider that you want to query data for a 48-hour time range between the timestamps `from=1354647221000` and `to=1358794421000` ( **specified in Unix time, based on the number of milliseconds that have elapsed since January 1, 1970** ). The following table shows the number of points that the API returns for a given resolution. - -#### Specifying resolution to retrieve data in 48 hour period - -You specify resolution...|API returns points... ----|--- -FULL|5760 -MIN5|576 -MIN20|144 -MIN60|48 -MIN240|12 -MIN1440|2 - -#### Specifying number of points to retrieve data in 48 hour period - -You specify points in the range...|API calculates resolution ----|--- -3168-∞|FULL -360-3167|MIN5 -96-359|MIN20 -30-95|MIN60 -7-29|MIN240 -0-6|MIN1440 - -#### Data Point Expiration - -Cloud Monitoring expires data points according to the following schedule: - -Resolution|Expiration ----|--- -FULL|2 days -MIN5|7 days -MIN20|15 days -MIN60|30 days -MIN240|60 days -MIN1440|365 days - -## Setup - -Metrics are sub-resources of Checks. For more information about working with Checks, please see the [relevant documentation](Checks.md). - -## List all metrics -```php -$metrics = $check->getMetrics(); - -foreach ($metrics as $metric) { - echo $metric->getName(); -} -``` - -## Fetch data points -```php -$data = $check->fetchDataPoints('mzdfw.available', array( - 'resolution' => 'FULL', - 'from' => 1369756378450, - 'to' => 1369760279018 -)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/metrics.html diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md index 81a890f56..129881fae 100644 --- a/docs/userguide/CloudMonitoring/Notifications.md +++ b/docs/userguide/CloudMonitoring/Notifications.md @@ -1,203 +1,5 @@ # Notifications -## Info +Our docs have moved! Please visit the below link: -A notification is a destination to send an alarm; it can be a variety of different types, and will evolve over time. - -For instance, with a webhook type notification, Cloud Monitoring posts JSON formatted data to a user-specified URL on an alert condition (Check goes from `OK` -> `CRITICAL` and so on). - -## Setup - -```php -$id = 'ntAAAA'; -$notification = $service->getNotification($id); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|---|--- -details|A hash of notification specific details based on the notification type.|Array|`getDetails()` -label|Friendly name for the notification.|String (1..255 chars)|`getLabel()` -type|The notification type to send.|String. Either `webhook`, `email`, or `pagerduty`|`getType()` - -## Test parameters -```php -$params = array( - 'label' => 'My webhook #1', - 'type' => 'webhook', - 'details' => array( - 'url' => 'http://example.com' - ) -); - -// Test it -$response = $notification->testParams($params); - -if ($response->status == 'Success') { - echo $response->message; -} -``` - -## Create Notification - -```php -$notification->create($params); -``` - -## Test existing notification -```php -$response = $notification->testExisting(true); -echo $response->debug_info; -``` - -## List Notifications -```php -$notifications = $service->getNotifications(); - -foreach ($notifications as $notification) { - echo $notification->getId(); -} -``` - -## Update and delete Notifications -```php -// Update -$notification->update(array( - 'label' => 'New notification label' -)); - -// Delete -$notification->delete(); -``` - -# Notification types - -## Info - -Pretty self-explanatory. Rackspace Cloud Monitoring currently supports the following notification types: - -#### Webhook - -Industry-standard web hooks, where JSON is posted to a configurable URL. It has these attributes: - -Name|Description|Data type ----|---|--- -address|Email address to send notifications to|Valid email - -#### Email - -Email alerts where the message is delivered to a specified address. It has these attributes: - -Name|Description|Data type ----|---|--- -url|An HTTP or HTTPS URL to POST to|Valid URL - -## Setup - -If you've already set up a main Notification object, and want to access functionality for this Notification's particular Notification Type, you can access its property: - -```php -$type = $notification->getNotificationType(); -``` - -Alternatively, you can retrieve an independent resource using the ID: - -```php -$typeId = 'pagerduty'; -$type = $service->getNotificationType($typeId); -``` - -## List all possible notification types -```php -$types = $service->getNotificationTypes(); - -foreach ($types as $type) { - echo sprintf('%s %s', $type->getName(), $type->getDescription()); -} -``` - -# Notification plans - -## Info - -A notification plan contains a set of notification actions that Rackspace Cloud Monitoring executes when triggered by an alarm. Rackspace Cloud Monitoring currently supports webhook and email notifications. - -Each notification state can contain multiple notification actions. For example, you can create a notification plan that hits a webhook/email to notify your operations team if a warning occurs. However, if the warning escalates to an Error, the notification plan could be configured to hit a different webhook/email that triggers both email and SMS messages to the operations team. The notification plan supports the following states: - -- Critical -- Warning -- OK - -A notification plan, `npTechnicalContactsEmail`, is provided by default which will email all of the technical contacts on file for an account whenever there is a state change. - -## Setup - -```php -$planId = 'npAAAA'; -$plan = $service->getNotificationPlan(); -``` - -## Attributes - -Name|Description|Required?|Data type|Method ----|---|---|---|--- -label|Friendly name for the notification plan.|Required|String (1..255 chars)|`getLabel()` -critical_state|The notification list to send to when the state is `CRITICAL`.|Optional|Array|`getCriticalState()` -ok_state|The notification list to send to when the state is `OK`.|Optional|Array|`getOkState()` -warning_state|The notification list to send to when the state is `WARNING`.|Optional|Array|`getWarningState()` - -## Create Notification Plan -```php -$plan->create(array( - 'label' => 'New Notification Plan', - 'critical_state' => array('ntAAAA'), - 'ok_state' => array('ntBBBB'), - 'warning_state' => array('ntCCCC') -)); -``` - -## Update and delete Notification Plan - -```php -// Update -$plan->update(array( - 'label' => 'New label for my plan' -)); - -// Delete -$plan->delete(); -``` - -# Alarm Notification History - -## Info - -The monitoring service keeps a record of notifications sent for each alarm. This history is further subdivided by the check on which the notification occurred. Every attempt to send a notification is recorded, making this history a valuable tool in diagnosing issues with unreceived notifications, in addition to offering a means of viewing the history of an alarm's statuses. - -Alarm notification history is accessible as a Time Series Collection. By default alarm notification history is stored for 30 days and the API queries the last 7 days of information. - -## Setup - -Notification History is a sub-resource of an Alarm. For more information about working with Alarms, please consult the relevant [documentation](Alarms.md). - -## Discover which Checks have a Notification History - -This operation list checks for which alarm notification history is available: - -```php -$checks = $alarm->getRecordedChecks(); -``` - -## List Alarm Notification History for a particular Check -```php -$checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); -``` - -## Get a particular Notification History item -```php -$checkId = 'chAAAA'; -$itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; - -$singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/notifications.html diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md index 5647eedc2..6445de039 100644 --- a/docs/userguide/CloudMonitoring/Service.md +++ b/docs/userguide/CloudMonitoring/Service.md @@ -1,16 +1,5 @@ # Cloud Monitoring Service -Initializing the Cloud Monitoring is easy - and can be done in a similar way to all other Rackspace services: +Our docs have moved! Please visit the below link: -1. Create client and pass in auth details. For more information about creating clients, please consult the [Client documentation](../Clients.md). -2. Use the factory method, specifying additional parameters where necessary: - -```php -$service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); -``` - -All three parameters are optional - if not specified, it will revert to the service's default values which are: - -- Name = `cloudMonitoring` -- Region = `DFW` -- URL type = `publicURL` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/index.html diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md index b6b505efc..ac0017db0 100644 --- a/docs/userguide/CloudMonitoring/Views.md +++ b/docs/userguide/CloudMonitoring/Views.md @@ -1,19 +1,5 @@ # Views -## Info +Our docs have moved! Please visit the below link: -Views contain a combination of data that usually includes multiple, different objects. The primary purpose of a view is to save API calls and make data retrieval more efficient. Instead of doing multiple API calls and then combining the result yourself, you can perform a single API call against the view endpoint. - -## List all Views - -```php -$views = $service->getViews(); - -foreach ($views as $view) { - $entity = $view->getEntity(); - - echo $view->getTimestamp(); -} - ``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/views.html diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md index 338bb1053..fcad4b900 100644 --- a/docs/userguide/CloudMonitoring/Zones.md +++ b/docs/userguide/CloudMonitoring/Zones.md @@ -1,46 +1,5 @@ # Zones -## Info +Our docs have moved! Please visit the below link: -A monitoring zone is a location that Rackspace Cloud Monitoring collects data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". It is an abstraction for a general location from which data is collected. - -An "endpoint," also known as a "collector," collects data from the monitoring zone. The endpoint is mapped directly to an individual machine or a virtual machine. A monitoring zone contains many endpoints, all of which will be within the IP address range listed in the response. The opposite is not true, however, as there may be unallocated IP addresses or unrelated machines within that IP address range. - -A check references a list of monitoring zones it should be run from. - -## Setup -```php -$zoneId = 'mzAAAAA'; -$zone = $monitoringService->getMonitoringZone($zoneId); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|---|--- -country_code|Country Code|String longer than 2 characters|`getCountryCode()` -label|Label|String|`getLabel()` -source_ips|Source IP list|Array|`getSourceIps()` - -## List all zones - -```php -$zones = $service->getMonitoringZones(); -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Perform a traceroute - -```php -$traceroute = $zone->traceroute(array( - 'target' => 'http://test.com', - 'target_resolver' => 'IPv4' -)); - -// How many hops? -echo count($traceroute); - -// What was the first hop's IP? -echo $traceroute[0]->ip; -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/zones.html diff --git a/docs/userguide/Compute/Images.md b/docs/userguide/Compute/Images.md index d09b0b0eb..6cc06d37f 100644 --- a/docs/userguide/Compute/Images.md +++ b/docs/userguide/Compute/Images.md @@ -1,65 +1,5 @@ # Compute Images -## Intro +Our docs have moved! Please visit the below link: -An image is a collection of files for a specific operating system that you use to create or rebuild a server. Rackspace provides prebuilt images. You can also create custom images from servers that you have launched. - -In addition to creating images manually, you can also schedule images of your server automatically. Please consult the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/scheduled_images.html) for more information about this extension, including enabling and disabling scheduled images and showing scheduled images. - -With standard servers, the entire disk (OS and data) is captured in the image. With Performance servers, only the system disk is captured in the image. The data disks should be backed up using Cloud Backup or Cloud Block Storage to ensure availability in case you need to rebuild or restore a server. - -## Setup - -You first need to setup a Compute service. For information, please consult the [Compute service](Service.md) documentation. - -## List images - -```php -$images = $service->imageList(); - -foreach ($images as $image) { - -} -``` - -For more information about [iterators](docs/userguide/Iterators.md), please consult the official documentation. - -### Query parameters - -You can also refine the list of images returned by providing specific URL parameters: - -|Field name|Description| -|---|---| -|server|Filters the list of images by server. Specify the server reference by ID or by full URL.| -|name|Filters the list of images by image name.| -|status|Filters the list of images by status. In-flight images have a status of `SAVING` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the `OpenCloud\Compute\Constants\ImageState` class. Images with an `ACTIVE` status are available for use.| -|changes-since|Filters the list of images to those that have changed since the changes-since time. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ChangesSince.html) for more information.| -|marker|The ID of the last item in the previous list. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Paginated_Collections-d1e664.html) for more information.| -|limit|Sets the page size. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Paginated_Collections-d1e664.html) for more information.| -|type|Filters base Rackspace images or any custom server images that you have created. Can either be `BASE` or `SNAPSHOT`.| - -### Example - -You can return more information about each image by setting the `$details` argument to `true`. The second argument can be an array of query parameters: - -```php -use OpenCloud\Compute\Constants\ImageState; - -$list = $service->imageList(true, array( - 'server' => 'fooBar', - 'status' => ImageState::ACTIVE -)); -``` - -## Get an image - -```php -$imageId = '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001'; -$image = $service->image($imageId); -``` - -## Delete an image - -```php -$image->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/images.html diff --git a/docs/userguide/Compute/Keypair.md b/docs/userguide/Compute/Keypair.md index e314db32c..37e5080fe 100644 --- a/docs/userguide/Compute/Keypair.md +++ b/docs/userguide/Compute/Keypair.md @@ -1,91 +1,5 @@ # Keypairs -## Generate new keypair +Our docs have moved! Please visit the below link: -This operation creates a new keypair under a provided name; the public key value is automatically generated for you. - -```php -$keypair = $service->keypair(); - -$keypair->create(array( - 'name' => 'jamie_keypair_1' -)); - -echo $keypair->getPublicKey(); - -// Save private key to a file so you can use it to SSH into -// your server later. -$sshPrivateKeyFilename = 'jamie_keypair_1_rsa'; -$privateKey = $keypair->getPrivateKey(); -file_put_contents($sshPrivateKeyFilename, $privateKey); -chmod($sshPrivateKeyFilename, 0600); -``` - -## Upload existing keypair - -This operation creates a new keypair under a provided name using a provided public key value. This public key will probably exist on your local filesystem, and so provide easy access to your server when uploaded. - -```php -$keypair = $service->keypair(); - -$key = <<create(array( - 'name' => 'jamie_macbook', - 'publicKey' => $key -)); - -``` - -## List keypairs - -To list all existing keypairs: - -```php -$keys = $service->listKeypairs(); - -foreach ($keys as $key) { - // ... -} -``` - -For more information about iterators, please see [the docs](../Iterators.md). - -## Delete keypairs - -To delete a specific keypair: - -```php -$keypair->delete(); -``` - -## Creating a server with a keypair - -In order to spawn an instance with a saved keypair (allowing you to SSH in without passwords), you create your server -using the same operation as usual, with one extra parameter: - -```php -use Guzzle\Http\Exception\BadResponseException; -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'New server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor, - 'networks' => array( - $compute->network(Network::RAX_PUBLIC), - $compute->network(Network::RAX_PRIVATE) - ), - 'keypair' => 'jamie_macbook' - )); -} catch (BadResponseException $e) { - // error... -} -``` - -So, as you can see, you specify the **name** of an existing keypair that you previously created on the API. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/keypairs.html diff --git a/docs/userguide/Compute/Server.md b/docs/userguide/Compute/Server.md index 8cc0d17e7..e43e1cc42 100644 --- a/docs/userguide/Compute/Server.md +++ b/docs/userguide/Compute/Server.md @@ -1,180 +1,5 @@ # Servers -## Intro +Our docs have moved! Please visit the below link: -A server is a virtual machine instance in the Cloud Servers environment. - -## Setup - -Server objects are instantiated from the Compute service. For more details, see the [Service](Service.md) docs. - -## Get server - -The easiest way to retrieve a specific server is by its unique ID: - -```php -$serverId = 'ef08aa7a-b5e4-4bb8-86df-5ac56230f841'; -$server = $service->server($serverId); -``` - -## List servers - -You can list servers in two different ways: - -* return an _overview_ of each server (ID, name and links) -* return _detailed information_ for each server - -Knowing which option to use might help save unnecessary bandwidth and reduce latency. - -```php -// overview -$servers = $service->serverList(); - -// detailed -$servers = $service->serverList(true); -``` - -### URL parameters for filtering servers - -Name|Description|Type ----|---|--- -image|The image ID|string -flavor|The flavor ID|string -name|The server name|string -status|The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult `OpenCloud\Compute\Constants\ServerState`|string -changes-since|Value for checking for changes since a previous request|A valid ISO 8601 dateTime (2011-01-24T17:08Z) -RAX-SI:image_schedule|If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule.|bool - -## Create server - -### Using an image - -There are a few parameter requirements when creating a server using an image: - -* **name** - needs to be a string; -- **flavor** - a `OpenCloud\Compute\Resource\Flavor` object, that is populated with the values of a real API flavor; -* **image** - a `OpenCloud\Compute\Resource\Image` object, that is populated with the values of a real API image; - -Firstly we need to find our flavor and image using their UUIDs. For more information about these concepts, including how to find flavor/image UUIDs, please consult §§ 3-4 in the [Getting Started guide](https://github.com/rackspace/php-opencloud/blob/master/docs/getting-started.md#3-select-your-server-image). - -```php -$ubuntuImage = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -$twoGbFlavor = $compute->flavor('4'); -``` - -Now we're ready to create our instance: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf('Status: %s\nBody: %s\nHeaders: %s', $statusCode, $responseBody, implode(', ', $headers); -} -``` - -It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. - -### Using a bootable volume - -There are a few parameter requirements when creating a server using a bootable volume: - -* **name** - needs to be a string; -* **flavor** - a `OpenCloud\Compute\Resource\Flavor` object, that is populated with the values of a real API flavor; -* **volume** - a `OpenCloud\Volume\Resource\Volume` object, that is populated with the values of a real API volume; - -Firstly we need to find our flavor and volume using their IDs. - -```php -$volumeService = $client->volumeService(); -$bootableVolume = $volumeService->volume(''); -$flavor = $compute->flavor(''); -``` - -Now we're ready to create our instance: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'volume' => $bootableVolume, - 'flavor' => $flavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - // No! Something failed. Let's find out: - echo $e->getRequest() . PHP_EOL . PHP_EOL; - echo $e->getResponse(); -} -``` - -It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. - -### Create parameters - -Name|Description|Type|Required ----|---|---|--- -name|The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync.|string|Yes -flavor|A populated `OpenCloud\Compute\Resource\Flavor` object representing your chosen flavor|object|Yes -image|A populated `OpenCloud\Compute\Resource\Image` object representing your chosen image|object|No, if volume is specified -volume|A populated `OpenCloud\Volume\Resource\Volume` object representing your chosen bootable volume|object|No, if image is specified -volumeDeleteOnTermination|`true` if the bootable volume should be deleted when the server is terminated; `false`, otherwise|boolean|No; default = `false` -OS-DCF:diskConfig|The disk configuration value. You can use two options: `AUTO` or `MANUAL`.

      `AUTO` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. `AUTO` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.

      `MANUAL` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration.|string|No -networks|An array of populated `OpenCloud\Compute\Resource\Network` objects that indicate which networks your instance resides in.|array|No -metadata|An array of arbitrary data (key-value pairs) that adds additional meaning to your server.|array|No -keypair|You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication.|array|No -personality|Files that you can upload to your newly created instance's filesystem.|array|No - -### Creating a server with keypairs - -Please see the [Keypair](Keypair.md) docs for more information. - -### Creating a server with personality files - -Before you execute the create operation, you can add "personality" files to your `OpenCloud\Compute\Resource\Server` object. These files are structured as a flat array. - -```php -$server->addFile('/var/test_file', 'FILE CONTENT'); -``` - -As you can see, the first parameter represents the filename, and the second is a string representation of its content. When the server is created these files will be created on its local filesystem. For more information about server personality files, please consult the [official documentation](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Server_Personality-d1e2543.html). - -## Update server - -You can update certain attributes of an existing server instance. These attributes are detailed in the next section. - -```php -$server->update(array( - 'name' => 'NEW SERVER NAME' -)); -``` - -### Updatable attributes - -name|description ----|--- -name|The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. -accessIPv4|The IP version 4 address. -accessIPv6|The IP version 6 address. - -## Delete server - -```php -$server->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/Compute/Service.md b/docs/userguide/Compute/Service.md index faff788bb..ba49ff4e4 100644 --- a/docs/userguide/Compute/Service.md +++ b/docs/userguide/Compute/Service.md @@ -1,18 +1,5 @@ # Compute service -## Setup +Our docs have moved! Please visit the below link: -To instantiate a Compute service object, you first need to setup a Rackspace/OpenStack client. To do this, or for more -information, please consult the [Clients documentation](../Clients.md). - -```php -$service = $client->computeService(); -``` - -If no arguments are provided to the above method, certain values are set to their default values: - -|Param|Default value| -|---|---| -|`$name`|cloudServersOpenStack| -|`$region`|DFW| -|`$urltype`|publicURL| \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index f0d94efee..d8d3a12d3 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -1,230 +1,5 @@ # Domains -A domain is an entity/container of all DNS-related information containing one or more records. +Our docs have moved! Please visit the below link: -## Setup - -Limit methods will be called on the DNS service, an instance of `OpenCloud\DNS\Service`. Please see the [DNS service](Service.md) documentation for setup instructions. - -## Get domain - -To retrieve a specific domain, you will need the domain's **id**, not its domain name. - -```php -$domain = $service->domain(12345); -``` - -If you are having trouble remembering or accessing the domain ID, you can do a domain list search for your domain and then access its ID. - -## List domains - -These calls provide a list of all DNS domains manageable by a given account. The resulting list is flat, and does not break the domains down hierarchically by subdomain. All representative domains are included in the list, even if a domain is conceptually a subdomain of another domain in the list. - -```php -$domains = $service->domainList(); - -# Return detailed information for each domain -$domains = $service->domainList(true); -``` - -Please consult the [iterator documentation](/docs/userguide/Iterators.md) for more information about iterators. - -### Filter parameters - -You can filter the aforementioned search by using the `name` parameter in a key/value array supplied as a method argument. For example, providing `array('name' => 'hoola.com')` will return hoola.com and similar names such as main.hoola.com and sub.hoola.com. - -```php -$hoolaDomains = $service->domainList(array( - 'name' => 'hoola.com' -)); -``` - -Filter criteria may consist of: - -- Any letter (A-Za-z) -- Numbers (0-9) -- Hyphen ("-") -- 1 to 63 characters - -Filter criteria should not include any of the following characters: - -> ' + , | ! " £ $ % & / ( ) = ? ^ * ç ° § ; : _ > ] [ @ à, é, ò - -### Finding a domain ID - -If you know a domain's name, but not its unique identifier, you can do this: - -```php -$domains = $service->domainList(array( - 'name' => 'foo.com' -)); - -foreach ($domains as $domain) { - $id = $domain->id; -} -``` - -## List domain changes - -This call shows all changes to the specified domain since the specified date/time. The since parameter is optional and defaults to midnight of the current day. - -```php -$changes = $domain->changes(); - -# Changes since last week -$since = date('c', strtotime('last week')); -$changes = $domain->changes($since); - -foreach ($changes->changes as $change) { - printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); - - foreach ($change->changeDetails as $detail) { - printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); - } -} -``` - -## Export domain - -This call provides the BIND (Berkeley Internet Name Domain) 9 formatted contents of the requested domain. This call is for a single domain only, and as such, does not traverse up or down the domain hierarchy for details (that is, no subdomain information is provided). - -```php -$asyncResponse = $domain->export(); -$body = $asyncResponse->waitFor('COMPLETED'); -echo $body['contents']; -``` - -## Create domain - -A domain is composed of DNS records (e.g. `A`, `CNAME` or `MX` records) and an optional list of sub-domains. You will need to specify these before creating the domain itself: - -```php -// get empty object -$domain = $service->domain(); - -// add A record -$aRecord = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 -)); -$domain->addRecord($aRecord); - -// add optional C record -$cRecord = $domain->record(array( - 'type' => 'CNAME', - 'name' => 'www.example.com', - 'data' => 'example.com', - 'ttl' => 3600 -)); -$domain->addRecord($cRecord); - -// add optional MX record -$mxRecord = $domain->record(array( - 'type' => 'MX', - 'data' => 'mail.example.com', - 'name' => 'example.com', - 'ttl' => 3600, - 'priority' => 5 -)); -$domain->addRecord($mxRecord); - -// add optional NS records -$nsRecord1 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns1.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 -)); -$domain->addRecord($nsRecord1); - -$nsRecord2 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns2.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 -)); -$domain->addRecord($nsRecord2); - -// add optional subdomains -$sub1 = $domain->subdomain(array( - 'emailAddress' => 'foo@example.com', - 'name' => 'dev.example.com', - 'comment' => 'Dev portal' -)); -$domain->addSubdomain($sub1); - -// send to API -$domain->create(array( - 'emailAddress' => 'webmaster@example.com', - 'ttl' => 3600, - 'name' => 'example.com', - 'comment' => 'Optional comment' -)); -``` - -## Clone domain - -This call will duplicate a single existing domain configuration with a new domain name for the specified Cloud account. By default, all records and, optionally, subdomain(s) are duplicated as well. - -The method signature you will need to use is: - -```php -cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) -``` - -Name|Data type|Default|Description ----|---|---|--- -`$newDomainName`|`string`|-|The new name for your cloned domain -`$subdomains`|`bool`|`true`|Set to `TRUE` to clone all the subdomains for this domain -`$comments`|`bool`|`true`|Set to `TRUE` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. -`$email`|`bool`|`true`|Set to `TRUE` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. - -For example: - -```php -$asyncResponse = $domain->cloneDomain('new-name.com', true); -``` - -## Import domain - -This call provisions a new DNS domain under the account specified by the BIND 9 formatted file configuration contents defined in the request object. - -You will need to ensure that the BIND 9 formatted file configuration contents are valid by adhering to the following rules: - -- Each record starts on a new line and on the first column. If a record will not fit on one line, use the BIND_9 line continuation convention where you put a left parenthesis and continue the one record on the next line and put a right parenthesis when the record ends. For example, - - > example2.net. 3600 IN SOA dns1.stabletransit.com. ( - sample@rackspace.com. 1308874739 3600 3600 3600 3600) - -- The attribute values of a record must be separated by a single blank or tab. No other white space characters. - -- If there are any NS records, the data field should not be dns1.stabletransit.com or dns2.stabletransit.com. They will result in "duplicate record" errors. - -For example: - -```php -$bind9Data = <<import($bind9Data); -``` - -## Modify domain - -This call modifies DNS domain(s) attributes only. Only the TTL, email address and comment attributes of a domain can be modified. Records cannot be added, modified, or removed through this API operation - you will need to use the [add records](/docs/userguide/DNS/Records.md#add-record), [modify records](/docs/userguide/DNS/Records.md#modify-record) or [remove records](/docs/userguide/DNS/Records.md#delete-record) operations respectively. - -```php -$domain->update(array( - 'ttl' => ($domain->ttl + 100), - 'emailAddress' => 'new_dev@foo.com' -)); -``` - -## Remove domain - -```php -$domain->delete(); -``` +https://doc.php-opencloud.com/en/latest/services/dns/domains.html diff --git a/docs/userguide/DNS/Limits.md b/docs/userguide/DNS/Limits.md index 6e7aeb042..70fac6504 100644 --- a/docs/userguide/DNS/Limits.md +++ b/docs/userguide/DNS/Limits.md @@ -1,58 +1,5 @@ # Limits -## Setup +Our docs have moved! Please visit the below link: -Limit methods will be called on the DNS service, an instance of `OpenCloud\DNS\Service`. Please see the [DNS service](Service.md) documentation for setup instructions. - -## List all limits - -This call provides a list of all applicable limits for the specified account. - -```php -$limits = $service->limits(); -``` - -### Absolute limits - -There are some absolute limits imposed on your account - such as how many domains you can create and how many records you can create for each domain: - -```php -$absoluteLimits = $limits->absolute; - -# Domain limit -echo $absoluteLimits->domains; - -# Record limit per domain -echo $absoluteLimits->{'records per domain'}; -``` - -## List limit types - -To find out the different limit types you can query, run: - -```php -$limitTypes = $service->limitTypes(); -``` - -will return: - -``` -array(3) { - [0] => - string(10) "RATE_LIMIT" - [1] => - string(12) "DOMAIN_LIMIT" - [2] => - string(19) "DOMAIN_RECORD_LIMIT" -} -``` - -## Query a specific limit - -```php -$limit = $service->limits('DOMAIN_LIMIT'); - -echo $limit->absolute->limits->value; - ->>> 500 -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/limits.html diff --git a/docs/userguide/DNS/Records.md b/docs/userguide/DNS/Records.md index b8f71bda2..52998b955 100644 --- a/docs/userguide/DNS/Records.md +++ b/docs/userguide/DNS/Records.md @@ -1,87 +1,5 @@ # Records -A DNS record belongs to a particular domain and is used to specify information about the domain. +Our docs have moved! Please visit the below link: -There are several types of DNS records. Examples include mail exchange (MX) records, which specify the mail server for a particular domain, and name server (NS) records, which specify the authoritative name servers for a domain. - -It is represented by the `OpenCloud\DNS\Resource\Record` class. Records belong to a [Domain](Domains.md). - -## Get record - -In order to retrieve details for a specific DNS record, you will need its **id**: - -```php -$record = $domain->record('NS-1234567'); -``` - -If you do not have this ID at your disposal, you can traverse the record collection and do a string comparison (detailed below). - -## List records - -This call lists all records configured for the specified domain. - -```php -$records = $domain->recordList(); - -foreach ($records as $record) { - printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -### Query parameters - -You can pass in an array of query parameters for greater control over your search: - -Name|Data type|Default|Description ----|---|---|--- -`type`|`string`|The record type -`name`|`string`|The record name -`data`|`string`|Data for this record - -### Find a record ID from its name - -For example: - -```php -$records = $domain->recordList(array( - 'name' => 'imap.example.com', - 'type' => 'MX' -)); - -foreach ($records as $record) { - $recordId = $record->id; -} -``` - -## Add record - -This call adds a new record to the specified domain: - -```php -$record = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 -)); - -$record->create(); -``` - -Please be aware that records that are added with a different hostname than the parent domain might fail silently. - -## Modify record - -```php -$record = $domain->record(123456); -$record->ttl -= 100; -$record->update(); -``` - -## Delete record - -```php -$record->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/records.html diff --git a/docs/userguide/DNS/Reverse-DNS.md b/docs/userguide/DNS/Reverse-DNS.md index a269a0057..e930431c1 100644 --- a/docs/userguide/DNS/Reverse-DNS.md +++ b/docs/userguide/DNS/Reverse-DNS.md @@ -1,71 +1,5 @@ # Reverse DNS -DNS usually determines an IP address associated with a domain name. Reverse DNS is the opposite process: resolving a domain name from an IP address. This is usually achieved with a domain name pointer. +Our docs have moved! Please visit the below link: -## Get PTR record - -PTR records refer to a parent device: either a Cloud Server or a Cloud Load Balancer with a public virtual IP address. You must supply a fully formed resource object in order to retrieve either one's PTR record: - -```php -/** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ - -$ptr = $service->ptrRecord(array( - 'parent' => $parent -)); -``` - -So, in the above example, a `$parent` could be an instance of `OpenCloud\Compute\Resource\Server` or `OpenCloud\LoadBalancer\Resource\LoadBalancer` - because they both implement `OpenCloud\DNS\Resource\HadPtrRecordsInterface`. Please consult the [server documentation](../Compute/Server.md) and [load balancer documentation](../LoadBalancer/USERGUIDE.md) for more detailed usage instructions. - -## List PTR records - -```php -/** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ - -$ptrRecords = $service->ptrRecordList($parent); - -foreach ($ptrRecords as $ptrRecord) { - -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -## Add PTR record - -```php -$parent = $computeService->server('foo-server-id'); - -$ptr = $dnsService->ptrRecord(array( - 'parent' => $parent, - 'ttl' => 3600, - 'name' => 'example.com', - 'type' => 'PTR', - 'data' => '192.0.2.7' -)); - -$ptr->create(); -``` - -Here is a table that explains the above attributes: - -Name|Description|Required ----|---|--- -type|Specifies the record type as "PTR".|Yes -name|Specifies the name for the domain or subdomain. Must be a valid domain name.|Yes -data|The data field for PTR records must be a valid IPv4 or IPv6 IP address.|Yes -ttl|If specified, must be greater than 300. Defaults to 3600 if no TTL is specified.|No -comment|If included, its length must be less than or equal to 160 characters.|No - -## Modify PTR record - -```php -$ptr->update(array( - 'ttl' => $ptr->ttl * 2 -)); -``` - -## Delete PTR record - -```php -$ptr->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/reverse-dns.html diff --git a/docs/userguide/DNS/Service.md b/docs/userguide/DNS/Service.md index ccabb1242..3404ed7c7 100644 --- a/docs/userguide/DNS/Service.md +++ b/docs/userguide/DNS/Service.md @@ -1,11 +1,5 @@ # DNS Service -To instantiate a Compute service object, you first need to setup a -Rackspace/OpenStack client. To do this, or for more information, please consult -the [Clients documentation](../Clients.md). +Our docs have moved! Please visit the below link: -You will then need to run: - -```php -$service = $client->dnsService(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/index.html diff --git a/docs/userguide/Database/README.md b/docs/userguide/Database/README.md index 9a5822e5b..199d049e4 100644 --- a/docs/userguide/Database/README.md +++ b/docs/userguide/Database/README.md @@ -1,116 +1,5 @@ # Databases -A **cloud database** is a MySQL relational database service that allows -customers to programatically provision database instances of varying virtual -resource sizes without the need to maintain and/or update MySQL. +Our docs have moved! Please visit the below link: -## Getting started - -### 1. Instantiate a Rackspace client. - -```php -use OpenCloud\Rackspace; -use OpenCloud\Common\Constants\State; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### 2. Create a database server instance. - -```php -$databaseService = $client->databaseService('cloudDatabases', 'DFW'); - -$twoGbFlavor = $databaseService->flavor(3); - -$dbInstance = $databaseService->instance(); -$dbInstance->name = 'Demo database instance'; -$dbInstance->volume = new stdClass(); -$dbInstance->volume->size = 20; // GB -$dbInstance->flavor = $twoGbFlavor; -$dbInstance->create(); - -$dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { - - printf("Database instance build status: %s\n", $dbInstance->status); - -}); -``` - -The example above creates a database server instance with 20GB of disk space and -2GB of memory, then waits for it to become ACTIVE. - -### 3. Create a database on the database server instance. - -```php -$db = $dbInstance->database(); -$db->name = 'demo_db'; - -$db->create(); -``` - -The example above creates a database named `demo_db` on the database server -instance created in the previous step. - -### 4. Create database user and give it access to database. - -```php -$user = $dbInstance->user(); -$user->name = 'demo_user'; -$user->password = 'h@X0r!'; -$user->databases = array('demo_db'); - -$user->create(); -``` - -The example above creates a database user named `demo_user`, sets its password -and gives it access to the `demo_db` database created in the previous step. - -### 5. Optional step: Create a load balancer to allow access to the database from the Internet. - -The database created in the previous step can only be accessed from the Rackspace -private network (aka `SERVICENET`). If you have a cloud server instance in the same -region as the database server instance, you will be able to connect to the database -from that cloud server instance. - -If, however, you would like to access the database from the Internet, you will -need to create a load balancer with an IP address that is routable from the -Internet and attach the database server instance as a back-end node of this load -balancer. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -$loadBalancer = $loadBalancerService->loadBalancer(); - -$loadBalancer->name = 'Load balancer - DB'; -$loadBalancer->addNode($dbInstance->hostname, 3306); -$loadBalancer->port = 3306; -$loadBalancer->protocol = 'MYSQL'; -$loadBalancer->addVirtualIp('PUBLIC'); - -$loadBalancer->create(); - -$loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { - printf("Load balancer build status: %s\n", $lb->status); -}); - -foreach ($loadBalancer->virtualIps as $vip) { - if ($vip->type == 'PUBLIC') { - printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); - } -} -``` - -In the example above, a load balancer is created with the database server -instance as its only back-end node. Further, this load balancer is configured -to listen for MySQL connections on port 3306. Finally a virtual IP address (VIP) -is configured in the `PUBLIC` network address space so that this load balancer -may receive connections from the Internet. - -Once the load balancer is created and becomes `ACTIVE`, it's Internet-accessible -IP addresses are printed out. If you connect to any of these IP addresses on port -3306 using the MySQL protocol, you will be connected to the database created in -step 3. +https://doc.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/Debugging.md b/docs/userguide/Debugging.md index 0fc970c0a..0c6afa4d6 100644 --- a/docs/userguide/Debugging.md +++ b/docs/userguide/Debugging.md @@ -1,89 +1,5 @@ # Debugging -There are two important debugging strategies to use when encountering problems -with HTTP transactions. +Our docs have moved! Please visit the below link: -## Strategy 1: Meaningful exception handling - -If the API returns a `4xx` or `5xx` status code, it indicates that there was an -error with the sent request, meaning that the transaction cannot be adequately -completed. - -The Guzzle HTTP component, which forms the basis of our SDK's transport layer, -utilizes [numerous exception classes](https://github.com/guzzle/guzzle/tree/master/src/Guzzle/Http/Exception) -to handle this error logic. - -The two most common exception classes are: - -* `Guzzle\Http\Exception\ClientErrorResponseException`, which is thrown when a -`4xx` response occurs - -* `Guzzle\Http\Exception\ServerErrorResponseException`, which is thrown when a -`5xx` response occurs - -Both of these classes extend the base `BadResponseException` class. - -This provides you with the granularity you need to debug and handle exceptions. - -### An example with Swift - -If you're trying to retrieve a Swift resource, such as a Data Object, and you're -not completely certain that it exists, it makes sense to wrap your call in a -try/catch block: - -```php -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - return $service->getObject('foo.jpg'); -} catch (ClientErrorResponseException $e) { - // Okay, the resource probably does not exist - return false; -} catch (\Exception $e) { - // Some other exception was thrown, probably critical - $this->logException($e); - $this->alertDevs(); -} -``` - -Both `ClientErrorResponseException` and `ServerErrorResponseException` have -two methods that allow you to access the HTTP transaction: - -```php -// Find out the faulty request -$request = $e->getRequest(); - -// Display everything by casting as string -echo (string) $request; - -// Find out the HTTP response -$response = $e->getResponse(); - -// Output that too -echo (string) $response; -``` - -## Strategy 2: Wire logging - -Guzzle provides a [Log plugin](http://docs.guzzlephp.org/en/latest/plugins/log-plugin.html) -that allows you to log everything over the wire, which is useful if you don't -know what's going on. - -Here's how you enable it: - -#### Install the plugin - -```bash -php composer.phar require guzzle/plugin-log:~3.8 -``` - -#### Add to your client - -```php -use Guzzle\Plugin\Log\LogPlugin; - -$client->addSubscriber(LogPlugin::getDebugPlugin()); -``` - -The above will add a generic logging subscriber to your client, which will be -notified every time a relevant HTTP event is fired off. +https://doc.php-opencloud.com/en/latest/debugging.html diff --git a/docs/userguide/Identity/Roles.md b/docs/userguide/Identity/Roles.md index e2d49e71d..333d53c81 100644 --- a/docs/userguide/Identity/Roles.md +++ b/docs/userguide/Identity/Roles.md @@ -1,68 +1,5 @@ # Roles -## Intro +Our docs have moved! Please visit the below link: -A role is a personality that a user assumes when performing a specific set of operations. A role includes a set of rights and privileges. A user assuming a role inherits the rights and privileges associated with the role. A token that is issued to a user includes the list of roles the user can assume. When a user calls a service, that service determines how to interpret a user's roles. A role that grants access to a list of operations or resources within one service may grant access to a completely different list when interpreted by a different service. - -## Setup - -Role objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Getter|Setter ----|---|--- -id|`getId()`|`setId()` -name|`getName()`|`setName()` -description|`getDescription()`|`setDescription()` - -## List roles - -This call lists the global roles available within a specified service. - -```php -$roles = $service->getRoles(); - -foreach ($roles as $role) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). - -## Get role - -This call lists detailed information (id, name, description) for a specified role. - -```php -$roleId = '123abc'; -$role = $service->getRole($roleId); -``` - -## Add/delete user roles - -To add/remove user roles, you must first instantiate a [user](Users.md) object: - -```php -$roleId = '123abc'; - -// add role to user -$user->addRole($roleId); - -// remove role from user -$user->removeRole($roleId); -``` - -## List user global roles - -This call returns a list of global roles associated with a user: - -```php -$roles = $user->getRoles(); - -foreach ($roles as $role) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/roles.html diff --git a/docs/userguide/Identity/Service.md b/docs/userguide/Identity/Service.md index 6cc80eb5a..579cf2a9f 100644 --- a/docs/userguide/Identity/Service.md +++ b/docs/userguide/Identity/Service.md @@ -1,24 +1,5 @@ # Identity service -## Intro +Our docs have moved! Please visit the below link: -The Identity service is regionless, so you do not need to specify a region when instantiating the service object. Although this was primarily based on Rackspace's implementation of Cloud Identity, it should also work for OpenStack Keystone. - -## A note on object creation - -Normally, when services are created the client handles authenticates automatically. But because Keystone/Identity is fundamental to the authentication process itself, it proves difficult to do this procedure as its normally done. For this reason, you have two options when creating the service object: - -1: Use the client's factory method - -```php -$identity = $client->identityService(); -``` - -2: Authenticate manually - -```php -use OpenCloud\Identity\Service as IdentityService; - -$identity = IdentityService::factory($client); -$identity->getClient()->authenticate(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/index.html diff --git a/docs/userguide/Identity/Tenants.md b/docs/userguide/Identity/Tenants.md index b30cb661d..ae9bcdcb0 100644 --- a/docs/userguide/Identity/Tenants.md +++ b/docs/userguide/Identity/Tenants.md @@ -1,21 +1,5 @@ # Tenants -## Intro +Our docs have moved! Please visit the below link: -A tenant is a container used to group or isolate resources and/or identity objects. Depending on the service operator, a tenant may map to a customer, account, organization, or project. - -## Setup - -Tenant objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## List tenants - -```php -$tenants = $service->getTenants(); - -foreach ($tenants as $tenant) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/tenants.html diff --git a/docs/userguide/Identity/Tokens.md b/docs/userguide/Identity/Tokens.md index 04a3ec327..a60c57ab5 100644 --- a/docs/userguide/Identity/Tokens.md +++ b/docs/userguide/Identity/Tokens.md @@ -1,84 +1,5 @@ # Tokens -## Intro +Our docs have moved! Please visit the below link: -A token is an opaque string that represents an authorization to access cloud resources. Tokens may be revoked at any time and are valid for a finite duration. - -## Setup - -Token objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Description|Getter|Setter ----|---|---|--- -id|The unique ID of the token|`getId()`|`setId()` -expires|Timestamp of when the token will expire|`getExpires()` or `hasExpired()`|`setExpires()` - -## Create token (authenticate) - -In order to generate a token, you must pass in the JSON template that is sent to the API. This is because Rackspace's operation expects a slightly different entity body than OpenStack Keystone. - -Request body for Rackspace's generate token operation: - -```json -{ - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "foo", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1100111" - } -} -``` - -Request body for Keystone's generate token operation: - -```json -{ - "auth": { - "passwordCredentials":{ - "username":"demoauthor", - "password":"theUsersPassword" - }, - "tenantId": "12345678" - } -} -``` - -The only real differences you'll notice is the name of the object key (`RAX-KSKEY:apiKeyCredentials`/`passwordCredentials`) and the secret (`apiKey`/`password`). The `tenantId` property in both templates are optional. You can also add `tenantName` too. - -```php -use OpenCloud\Common\Http\Message\Formatter; - -$template = sprintf( - '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', - 'my_username', - 'my_api_key' -); - -$response = $service->generateToken($template); - -$body = Formatter::decode($response); - -// service catalog -$catalog = $body->access->serviceCatalog; - -// token -$token = $body->access->token; - -// user -$user = $body->access->user; -``` - -As you will notice, these variables will be stdClass objects - for fully fledged functionality, let the client authenticate by itself because it ends up stocking the necessary models for you. - -To see the response body structure, consult the [official docs](http://docs.rackspace.com/auth/api/v2.0/auth-client-devguide/content/POST_authenticate_v2.0_tokens_Token_Calls.html). - -## Revoke token (destroy session) - -```php -$tokenId = '1234567'; -$service->revokeToken($tokenId); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/tokens.html diff --git a/docs/userguide/Identity/Users.md b/docs/userguide/Identity/Users.md index 286aa6007..e387cbee5 100644 --- a/docs/userguide/Identity/Users.md +++ b/docs/userguide/Identity/Users.md @@ -1,128 +1,5 @@ # Users -## Intro +Our docs have moved! Please visit the below link: -A user is a digital representation of a person, system, or service who consumes cloud services. Users have credentials and may be assigned tokens; based on these credentials and tokens, the authentication service validates that incoming requests are being made by the user who claims to be making the request, and that the user has the right to access the requested resources. Users may be directly assigned to a particular tenant and behave as if they are contained within that tenant. - -## Setup - -User objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Description|Getter|Setter ----|---|---|--- -id|The unique ID for this user|`getId()`|`setId()` -username|Username for this user|`getUsername()`|`setUsername()` -email|User's email address|`getEmail()`|`setEmail()` -enabled|Whether or not this user can consume API functionality|`getEnabled()` or `isEnabled()`|`setEnabled()` -password|Either a user-defined string, or an automatically generated one, that provides security when authenticating.|`getPassword()` only valid on creation|`setPassword()` to set local property only. To set password on API (retention), use `updatePassword()`. -defaultRegion|Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region.|`getDefaultRegion()`|`setDefaultRegion()` -domainId|Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account.|`getDomainId()`|`setDomainId()` - -## List users - -```php -$users = $service->getUsers(); - -foreach ($users as $user) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). - -## Get user - -There are various ways to get a specific user: by name, ID and email address. - -```php -use OpenCloud\Identity\Constants\User as UserConst; - -// Get user by name -$user1 = $service->getUser('jamie'); - -// Get user by ID -$user2 = $service->getUser(123456, UserConst::MODE_ID); - -// Get user by email -$user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); -``` - -## Create user - -There are a few things to remember when creating a user: - -* This operation is available only to users who hold the `identity:user-admin` role. This admin can create a user who holds the `identity:default` user role. - -* The created user **will** have access to APIs but **will not** have access to the Cloud Control Panel. - -* Within an account, a maximum of 100 account users can be added. - -* If you attempt to add a user who already exists, an HTTP error 409 results. - -The `username` and `email` properties are required for creating a user. Providing a `password` is optional; if omitted, one will be automatically generated and provided in the response. - -```php -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - // execute operation - $user = $service->createUser(array( - 'username' => 'newUser', - 'email' => 'foo@bar.com' - )); -} catch (ClientErrorResponseException $e) { - // catch 4xx HTTP errors - echo $e->getResponse()->toString(); -} - -// show generated password -echo $user->getPassword(); -``` - -## Update user - -When updating a user, specify which attribute/property you want to update: - -```php -$user->update(array( - 'email' => 'new_email@bar.com' -)); -``` - -### Updating a user password - -Updating a user password requires calling a distinct method: -```php -$user->updatePassword('password123'); -``` - -## Delete user - -```php -$user->delete(); -``` - -## List credentials - -This operation allows you to see your non-password credential types for all authentication methods available. - -```php -$creds = $user->getOtherCredentials(); -``` - -## Get user API key - -```php -echo $user->getApiKey(); -``` - -## Reset user API key - -When resetting an API key, a new one will be automatically generated for you: - -```php -$user->resetApiKey(); -echo $user->getApiKey(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/users.html diff --git a/docs/userguide/Image/Images.md b/docs/userguide/Image/Images.md index c0efee9a1..1f593f17a 100644 --- a/docs/userguide/Image/Images.md +++ b/docs/userguide/Image/Images.md @@ -1,97 +1,5 @@ # Images -A virtual machine image is a single file which contains a virtual disk that has -an installed bootable operating system. In the Cloud Images API, an image is -represented by a JSON-encoded data structure (the image schema) and its raw -binary data (the image file). +Our docs have moved! Please visit the below link: -An Image is represented by the `OpenCloud\Image\Resource\Image` class. - -## Setup - -You instantiate an Image object from its `OpenCloud\Image\Service` class, which -is available from the OpenStack/Rackspace client: - -```php -$service = $client->imageService('cloudImages', 'IAD'); -``` - -View the guides for more information about [clients](../Clients.md) or -[services](../Services.md). - -## List images - -```php -$images = $service->listImages(); - -foreach ($images as $image) { - /** @param $image OpenCloud\Image\Resource\Image */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](../Iterators.md). - -## Get image details - -```php -/** @param $image OpenCloud\Image\Resource\Image */ -$image = $service->getImage(''); -``` - -### A note on schema classes - -Both `OpenCloud\Image\Resource\Image` and `OpenCloud\Image\Resource\Member` -extend the `AbstractSchemaResource` abstract class, which offers some unique -functionality. - -Because these resources are inherently dynamic - i.e. they are modelled on -dynamic JSON schema - you need to access their state in a way different than -conventional getter/setter methods, and even class properties. For this reason, -they implement SPL's native -[`ArrayAccess`](http://www.php.net/manual/en/class.arrayaccess.php) -interface which allows you to access their state as a conventional array: - -```php -$image = $service->getImage(''); - -$id = $image['id']; -$tags = $image['tags']; -``` - -## Update image - -You can only update your own custom images - you cannot update or delete base -images. The way in which you may update your image is dictated by its -[schema](Schemas.md). - -Although you should be able to add new and replace existing properties, always -prepare yourself for a situation where it might be forbidden: - -```php -use OpenCloud\Common\Exceptions\ForbiddenOperationException; - -try { - $image->update(array( - 'name' => 'foo', - 'newProperty' => 'bar' - )); -} catch (ForbiddenOperationException $e) { - // A 403 Forbidden was returned -} -``` - -There are three operations that can take place for each Image property: - -* If a `false` or `null` value is provided, a `REMOVE` operation will occur, -removing the property from the JSON document -* If a non-false value is provided and the property does not exist, an `ADD` -operation will add it to the document -* If a non-false value is provided and the property does exist, a `REPLACE` -operation will modify the property in the document - -## Delete image - -```php -$image->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/images.html diff --git a/docs/userguide/Image/Schemas.md b/docs/userguide/Image/Schemas.md index b634b5705..b37222b84 100644 --- a/docs/userguide/Image/Schemas.md +++ b/docs/userguide/Image/Schemas.md @@ -1,159 +1,5 @@ # JSON schemas -The Cloud Images API supplies json documents describing the JSON-encoded data -structures that represent domain objects, so that a client knows exactly what -to expect in an API response. +Our docs have moved! Please visit the below link: -A JSON Schema is represented by the `OpenCloud\Image\Resource\Schema\Schema` -class. - -## Schema types - -There are currently four types of schema: Images schema, Image schema, Members -schema, and Member schema. - -## Example response from the API - -A sample response from the API, for an Images schema might be: - -```json -{ - "name": "images", - "properties": { - "images": { - "items": { - "type": "array", - "name": "image", - "properties": { - "id": {"type": "string"}, - "name": {"type": "string"}, - "visibility": {"enum": ["public", "private"]}, - "status": {"type": "string"}, - "protected": {"type": "boolean"}, - "tags": { - "type": "array", - "items": {"type": "string"} - }, - "checksum": {"type": "string"}, - "size": {"type": "integer"}, - "created_at": {"type": "string"}, - "updated_at": {"type": "string"}, - "file": {"type": "string"}, - "self": {"type": "string"}, - "schema": {"type": "string"} - }, - "additionalProperties": {"type": "string"}, - "links": [ - {"href": "{self}", "rel": "self"}, - {"href": "{file}", "rel": "enclosure"}, - {"href": "{schema}", "rel": "describedby"} - ] - } - }, - "schema": {"type": "string"}, - "next": {"type": "string"}, - "first": {"type": "string"} - }, - "links": [ - {"href": "{first}", "rel": "first"}, - {"href": "{next}", "rel": "next"}, - {"href": "{schema}", "rel": "describedby"} - ] -} -``` - -The top-level schema is called `images`, and contains an array of links and -a properties object. Inside this properties object we see the structure of this -top-level `images ` object. So we know that it will take this form: - -```json -{ - "images": [something...] -} -``` - -Within this object, we can see that it contains an array of anonymous objects, -each of which is called `image` and has its own set of nested properties: - -```json -{ - "images": [ - { - [object 1...] - }, - { - [object 2...] - }, - { - [object 3...] - } - ] -} -``` - -The structure of these nested objects are defined as another schema - i.e. a -*subschema*. We know that each object has an ID property (string), a name -property (string), a visibility property (can either be `private` or `public`), etc. - -```json -{ - "images": [ - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - }, - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - }, - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - } - ] -} -``` - -Each nested property of a schema is represented by the -`OpenCloud\Image\Resource\Schema\Property` class. - -If you would like to find out more about schemas, Guzzle has good documentation -about [service descriptions](http://docs.guzzlephp.org/en/latest/webservice-client/guzzle-service-descriptions.html), -which is fairly analogous. - -## JSON Patch - -The Glance API has a unique way of updating certain dynamic resources: they use -JSON Patch method, as outlined in [RFC 6902](http://tools.ietf.org/html/rfc6902). - -Requests need to use the `application/openstack-images-v2.1-json-patch` -content-type. - -In order for the operation to occur, the request entity body needs to contain a -very particular structure: - -``` -[ - {"op": "replace", "path": "/name", "value": "Fedora 17"}, - {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} -] -``` - -The `op` key refers to the type of Operation (see -`OpenCloud\Image\Enum\OperationType` for a full list). - -The `path` key is a JSON pointer to the document property you want to modify or -insert. JSON pointers are defined in [RFC 6901](http://tools.ietf.org/html/rfc6901). - -The `value` key is the value. - -Because this is all handled for you behind the scenes, we will not go into exhaustive depth -about how this operation is handled. You can browse the source code, consult -the various RFCs and the [official documentation](http://docs.rackspace.com/images/api/v2/ci-devguide/content/patch-method.html) -for additional information. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/schemas.html diff --git a/docs/userguide/Image/Sharing.md b/docs/userguide/Image/Sharing.md index e83470073..91515b9fa 100644 --- a/docs/userguide/Image/Sharing.md +++ b/docs/userguide/Image/Sharing.md @@ -1,110 +1,5 @@ # Sharing images -Images can be created and deleted by image producers, updated by image -consumers, and listed by both image producers and image consumers: +Our docs have moved! Please visit the below link: -Operation|Producer can?|Consumer can? ----|---|--- -Created|Yes|No -Deleted|Yes|No -Updated|No|Yes -Listed|Yes|Yes - -The producer shares an image with the consumer by making the consumer a *member* -of that image. The consumer then accepts or rejects the image by changing the -member status. Once accepted, the image appears in the consumer's image list. - -## Typical workflow - -1. The producer posts the availability of specific images on a public website. - -2. A potential consumer provides the producer with his/her tenant ID and email -address. - -3. The producer [creates a new Image Member]() with the consumer's details - -4. The producer notifies the consumer via email that the image has been shared -and provides the image's ID. - -5. If the consumer wishes the image to appear in his/her image list, the -consumer [updates their own Member status]() to `ACCEPTED`. - -### Additional notes - -* If the consumer subsequently wishes to hide the image, the consumer can change -their Member status to `REJECTED`. - -* If the consumer wishes to hide the image, but is open to the possibility of -being reminded by the producer that the image is available, the consumer can -change their Member status to `PENDING`. - -* Image producers add or remove image members, but may not modify the member -status of an image member. - -* Image consumers change their own member status, but may not add or remove -themselves as an image member. - -* Image consumers can boot from any image shared by the image producer, -regardless of the member status, as long as the image consumer knows the image ID. - -## Setup - -All member operations are executed against an [Image](Images.md), so you will -need to set this up first. - -## List image members - -This operation is available for both producers and consumers. - -```php -$members = $image->listMembers(); - -foreach ($members as $member) { - /** @param $member OpenCloud\Image\Resource\Member */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](../Iterators.md). - -## Create image member - -This operation is only available for producers. - -```php -$tenantId = 12345; - -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->createMember($tenantId); -``` - -## Delete image member - -This operation is only available for producers. - -```php -$tenantId = 12345; - -/** @param $member OpenCloud\Image\Resource\Member */ -$member = $image->getMember($tenantId); - -$member->delete(); -``` - -## Update image member status - -This operation is only available for consumers. - -```php -use OpenCloud\Images\Enum\MemberStatus; - -$tenantId = 12345; - -/** @param $member OpenCloud\Image\Resource\Member */ -$member = $image->getMember($tenantId); - -$member->updateStatus(MemberStatus::ACCEPTED); -``` - -The acceptable states you may pass in are made available to you through the -constants defined in the `OpenCloud\Images\Enum\MemberStatus` class. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/sharing.html diff --git a/docs/userguide/Image/Tags.md b/docs/userguide/Image/Tags.md index 019e336e5..fc8bcd8aa 100644 --- a/docs/userguide/Image/Tags.md +++ b/docs/userguide/Image/Tags.md @@ -1,23 +1,5 @@ # Image tags -An image tag is a string of characters used to identify a specific image or -images. +Our docs have moved! Please visit the below link: -## Setup - -All member operations are executed against an [Image](Images.md), so you will -need to set this up first. - -## Add image tag - -```php -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->addTag('jamie_dev'); -``` - -## Delete image tag - -```php -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->deleteTag('jamie_dev'); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/tags.html diff --git a/docs/userguide/Iterators.md b/docs/userguide/Iterators.md index 553507937..114982668 100644 --- a/docs/userguide/Iterators.md +++ b/docs/userguide/Iterators.md @@ -1,118 +1,5 @@ # Iterators -## Intro +Our docs have moved! Please visit the below link: -Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: - -- **ResourceIterator**. The standard iterator class that implements SPL's standard [Iterator](http://php.net/manual/en/class.iterator.php), [ArrayAccess](http://www.php.net/manual/en/class.arrayaccess.php) and [Countable](http://php.net/manual/en/class.countable.php) interfaces. In short, this allows you to traverse this object (using `foreach`), count its internal elements like an array (using `count` or `sizeof`), and access its internal elements like an array (using `$iterator[1]`). - - -- **PaginatedIterator**. This is a child of ResourceIterator, and as such inherits all of its functionality. The difference however is that when it reaches the end of the current collection, it attempts to construct a URL to access the API based on predictive paginated collection templates. - -## Common behaviour - -```php -$iterator = $computeService->flavorList(); -``` - -There are two ways to traverse an iterator. The first is the longer, more traditional way: - -```php -while ($iterator->valid()) { - $flavor = $iterator->current(); - - // do stuff.. - echo $flavor->id; - - $iterator->next(); -} -``` - -There is also a shorter and more intuitive version: - -```php -foreach ($iterator as $flavor) { - // do stuff... - echo $flavor->id; -} -``` - -Because the iterator implements PHP's native `Iterator` interface, it can inherit all the native functionality of traversible data structures with `foreach`. - -## Very important note - -Until now, users have been expected to do this: - -```php -while ($flavor = $iterator->next()) { - // ... -} -``` - -which is **incorrect**. The single responsibility of `next` is to move the internal pointer forward. It is the job of `current` to retrieve the current element. - -For your convenience, these two Iterator classes are fully backward compatible: they exhibit all the functionality you'd expect from a correctly implemented iterator, but they also allow previous behaviour. - -## Using paginated collections - -For large collections, such as retrieving DataObjects from CloudFiles/Swift, you need to use pagination. Each resource will have a different limit per page; so once that page is traversed, there needs to be another API call to retrieve to *next* page's resources. - -There are two key concepts: - -- **limit** is the amount of resources returned per page -- **marker** is the way you define a starting point. It is some form of identifier that allows the collection to begin from a specific resource - -### Resource classes - -When the iterator returns a current element in the internal list, it populates the relevant resource class with all the data returned to the API. In most cases, a `stdClass` object will become an instance of `OpenCloud\Common\PersistentObject`. - -In order for this instantiation to happen, the `resourceClass` option must correspond to some method in the parent class that creates the resource. For example, if we specify 'ScalingPolicy' as the `resourceClass`, the parent object (in this case `OpenCloud\Autoscale\Group`, needs to have some method will allows the iterator to instantiate the child resource class. These are all valid: - -1. `Group::scalingGroup($data);` - -2. `Group::getScalingGroup($data);` - -3. `Group::resource('ScalingGroup', $data);` - -where `$data` is the standard object. This list runs in order of precedence. - -## Setting up a PaginatedIterator - -```php -use OpenCloud\Common\Collection\PaginatedIterator; - -$service = $client->computeService(); - -$flavors = PaginatedIterator::factory($service, array( - 'resourceClass' => 'Flavor', - 'baseUrl' => $service->getUrl('flavors') - 'limit.total' => 350, - 'limit.page' => 100, - 'key.collection' => 'flavors' -)); - -foreach ($flavors as $flavor) { - echo $flavor->getId(); -} -``` - -As you can see, there are a lot of configuration parameters to pass in - and getting it right can be quite fiddly, involving a lot of API research. For this reason, using the convenience methods like `flavorList` is recommended because it hides the complexity. - -### PaginatedIterator options - -There are certain configuration options that the paginated iterator needs to work. These are: - -Name|Description|Type|Required|Default| ----|---|---|---|---| -resourceClass|The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator.|string|Yes|- -baseUrl|The base URL that is used for making new calls to the API for new pages|`Guzzle\Http\Url`|Yes|- -limit.total|The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list|int|No|10000 -limit.page|The amount of resources each page contains|int|No|100 -key.links|Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is `images_links`.|string|No|links -key.collection|The top-level key for the array of resources. For example, servers are returned with this data structure: `{"servers": [...]}`. The **key.collection** value in this case would be `servers`.|string|No|`null` -key.collectionElement|Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: `{"keypairs": [ {"keypair": {...}} ] }`. So in this case the collectionElement key would be `keypair`.|string|No|`null` -key.marker|The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is `id` or `name`.|string|No|name -request.method|The HTTP method used when making API calls for new pages|string|No|GET -request.headers|The HTTP headers to send when making API calls for new pages|array|No|`array()` -request.body|The HTTP entity body to send when making API calls for new pages|`Guzzle\Http\EntityBody`|No|`null` -request.curlOptions|Additional cURL options to use when making API calls for new pages|array|No|`array()` +https://doc.php-opencloud.com/en/latest/iterators.html diff --git a/docs/userguide/LoadBalancer/README.md b/docs/userguide/LoadBalancer/README.md index 03364f103..34008ab6c 100644 --- a/docs/userguide/LoadBalancer/README.md +++ b/docs/userguide/LoadBalancer/README.md @@ -1,80 +1,5 @@ # Load Balancers -A **load balancer** is a device that distributes incoming network traffic amongst -multiple back-end systems. These back-end systems are called the **nodes** of -the load balancer. +Our docs have moved! Please visit the below link: -## Getting started - -### 1. Instantiate a Rackspace client. - -```php - -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### 2. Retrieve the server instances you want to add as nodes of the load balancer. - -```php -$computeService = $client->computeService('cloudServersOpenStack', 'DFW'); - -$serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); -$serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); -``` - -The example above uses two server instances that have already been created. It -retrieves the server instances using their IDs. See also: [creating server instances](). - -### 3. Obtain a Load Balancer service object from the client. - -This object will be used to first define the load balancer nodes and later create the load balancer itself. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); -``` - -### 4. Define a load balancer node for each server. - -```php -$loadBalancer = $loadBalancerService->loadBalancer(); - -$serverOneNode = $loadBalancer->node(); -$serverOneNode->address = $serverOne->addresses->private[0]->addr; -$serverOneNode->port = 8080; -$serverOneNode->condition = 'ENABLED'; - -$serverTwoNode = $loadBalancer->node(); -$serverTwoNode->address = $serverTwo->addresses->private[0]->addr; -$serverTwoNode->port = 8080; -$serverTwoNode->condition = 'ENABLED'; -``` - -In the example above, each node runs a service that listens on port 8080. Further, -each node will start out as `ENABLED`, which means it will be ready to receive -network traffic from the load balancer as soon as it is created. - -### 5. Create the load balancer with the two nodes. - -```php -$loadBalancer->addVirtualIp('PUBLIC'); -$loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) -)); -``` - -In the example above, the load balancer will have a virtual IP address accessible -from the public Internet. Also notice that the port the load balancer listens -on (80) does not need to match the ports of its nodes (8080). - -## Next steps - -Once you have created a load balancer, there is a lot you can do with it. See -the [complete user guide for load balancers](USERGUIDE.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 0c15f0b29..0a5c3b15d 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -1,626 +1,5 @@ # The Complete User Guide to Load Balancers -## Prerequisites +Our docs have moved! Please visit the below link: -### Client -To use the load balancers service, you must first instantiate a `Rackspace` -client object. - -```php -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### Load Balancer Service -All operations on load balancers are done via a load balancer service object. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); -``` - -### Cloud Servers -Many of the examples in this document use two cloud servers as nodes for -the load balancer. The variables `$serverOne` and `$serverTwo` refer to these -two cloud servers. - -## Load Balancers - -A **load balancer** is a device that distributes incoming network traffic amongst -multiple back-end systems. These back-end systems are called the **nodes** of -the load balancer. - -### Create Load Balancer - -```php -$loadBalancer = $loadBalancerService->loadBalancer(); - -$serverOneNode = $loadBalancer->node(); -$serverOneNode->address = $serverOne->addresses->private[0]->addr; -$serverOneNode->port = 8080; -$serverOneNode->condition = 'ENABLED'; - -$serverTwoNode = $loadBalancer->node(); -$serverTwoNode->address = $serverTwo->addresses->private[0]->addr; -$serverTwoNode->port = 8080; -$serverTwoNode->condition = 'ENABLED'; - -$loadBalancer->addVirtualIp('PUBLIC'); -$loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) -)); -``` - -### List Load Balancer Details - -You can retrieve a single load balancer's details by using its ID. - -```php -$loadBalancer = $loadBalancerService->loadBalancer('254889'); - -/** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ -``` - -### List Load Balancers - -You can retrieve a list of all your load balancers. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$loadBalancers = $loadBalancerService->loadBalancerList(); -foreach ($loadBalancers as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ -} -``` - -### Update Load Balancer Attributes - -You can update one or more of the following load balancer attributes: - -* `name`: The name of the load balancer -* `algorithm`: The algorithm used by the load balancer to distribute traffic amongst its nodes. See also: [Load balancing algorithms](#algorithms). -* `protocol`: The network protocol used by traffic coming in to the load balancer. See also: [Protocols](#protocols). -* `port`: The network port on which the load balancer listens for incoming traffic. -* `halfClosed`: Enable or Disable Half-Closed support for the load balancer. -* `timeout`: The timeout value for the load balancer to communicate with its nodes. -* `httpsRedirect`: Enable or disable HTTP to HTTPS redirection for the load balancer. When enabled, any HTTP request will return status code 301 (Moved Permanently), and the requestor will be redirected to the requested URL via the HTTPS protocol on port 443. For example, http://example.com/page.html would be redirected to https:// example.com/page.html. Only available for HTTPS protocol (`port` = 443), or HTTP Protocol with a properly configured SSL Termination (`secureTrafficOnly=true, securePort=443). See also: [SSL Termination](#ssl-termination). - -#### Updating a single attribute of a load balancer -```php -$loadBalancer->update(array( - 'name' => 'New name' -)); -``` - -#### Updating multiple attributes of a load balancer -```php -$loadBalancer->update(array( - 'name' => 'New name', - 'algorithm' => 'ROUND_ROBIN' -)); -``` - -### Remove Load Balancer - -When you no longer have a need for the load balancer, you can remove it. - -```php -$loadBalancer->delete(); -``` - -## Nodes - -A **node** is a backend device that provides a service on specified IP and port. An example of a load balancer node might be a web server serving HTTP traffic on port 8080. - -A load balancer typically has multiple nodes attached to it so it can distribute incoming network traffic amongst them. - -### List Nodes - -You can list the nodes attached to a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$nodes = $loadBalancer->nodeList(); -foreach ($nodes as $node) { - /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ -} -``` - -### Add Nodes - -You can attach additional nodes to a load balancer. Assume `$loadBalancer` already has two nodes attached to it - `$serverOne` and `$serverTwo` - and you want to attach a third node to it, say `$serverThree`, which provides a service on port 8080. - -**Important:** Remember to call `$loadBalancer->addNodes()` after all the calls to `$loadBalancer->addNode()` as shown below. - -```php -$address = $serverThree->addresses->private[0]->addr; -$loadBalancer->addNode($address, 8080); -$loadBalancer->addNodes(); -``` - -The `addNode` method accepts three more optional parameters, in addition to the two shown above: - -| Position | Description | Data type | Required? | Default value | -| ----------- | --------------- | --------------| -------------- | ----------------- | -| 1 | IP address of node | String | Yes | - | -| 2 | Port used by node's service | Integer | Yes | - | -| 3 | Starting condition of node:
      • `ENABLED` – Node is ready to receive traffic from the load balancer.
      • `DISABLED` – Node should not receive traffic from the load balancer.
      • `DRAINING` – Node should process any traffic it is already receiving but should not receive any further traffic from the load balancer.
      | String | No | `ENABLED` | -| 4 | Type of node to add:
      • `PRIMARY` – Nodes defined as PRIMARY are in the normal rotation to receive traffic from the load balancer.
      • `SECONDARY` – Nodes defined as SECONDARY are only in the rotation to receive traffic from the load balancer when all the primary nodes fail.
      | String | No | `PRIMARY` | -| 5 | Weight, between 1 and 100, given to node when distributing traffic using either the `WEIGHTED_ROUND_ROBIN` or the `WEIGHTED_LEAST_CONNECTIONS` load balancing algorithm. | Integer | No | 1 | - -### Modify Nodes -You can modify one or more of the following node attributes: - -* `condition`: The condition of the load balancer: - * `ENABLED` – Node is ready to receive traffic from the load balancer. - * `DISABLED` – Node should not receive traffic from the load balancer. - * `DRAINING` – Node should process any traffic it is already receiving but should not receive any further traffic from the load balancer. -* `type`: The type of the node: - * `PRIMARY` – Nodes defined as PRIMARY are in the normal rotation to receive traffic from the load balancer. - * `SECONDARY` – Nodes defined as SECONDARY are only in the rotation to receive traffic from the load balancer when all the primary nodes fail. -* `weight`: The weight, between 1 and 100, given to node when distributing traffic using either the `WEIGHTED_ROUND_ROBIN` or the `WEIGHTED_LEAST_CONNECTIONS` load balancing algorithm. - -#### Modifying a single attribute of a node -```php -use OpenCloud\LoadBalancer\Enum\NodeCondition; - -$node->update(array( - 'condition' => NodeCondition::DISABLED -)); -``` - -#### Modifying multiple attributes of a node -```php -use OpenCloud\LoadBalancer\Enum\NodeCondition; -use OpenCloud\LoadBalancer\Enum\NodeType; - -$node->update(array( - 'condition' => NodeCondition::DISABLED, - 'type' => NodeType::SECONDARY -)); -``` - -### Remove Nodes - -There are two ways to remove a node. - -#### Given an `OpenCloud\LoadBalancer\Resource\Node` instance -```php -$node->delete(); -``` - -#### Given an `OpenCloud\LoadBalancer\Resource\LoadBalancer` instance and a node ID -```php -$loadBalancer->removeNode(490639); -``` - -The `removeNode` method, as shown above, accepts the following arguments: - -|Position| Description | Data type | Required? | Default value | -|----------- | --------------- | -------------- |-------------- | ----------------- | -| 1 | ID of node | Integer | Yes | - | - -### View Node Service Events -You can view events associated with the activity between a node and a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$nodeEvents = $loadBalancer->nodeEventList(); -foreach ($nodeEvents as $nodeEvent) { - /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ -} -``` - -## Virtual IPs - -A **virtual IP (VIP)** makes a load balancer accessible by clients. The load balancing service supports either a public VIP address (`PUBLIC`), routable on the public Internet, or a ServiceNet VIP address (`SERVICENET`), routable only within the region in which the load balancer resides. - -### List Virtual IPs - -You can list the VIPs associated with a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$vips = $loadBalancer->virtualIpList(); -foreach ($vips as $vip) { - /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ -} -``` - -### Add Virtual IPv6 - -You can add additional IPv6 VIPs to a load balancer. - -```php -use OpenCloud\LoadBalancer\Enum\IpType; - -$loadBalancer->addVirtualIp(IpType::PUBLIC, 6); -``` - -The `addVirtualIp` method, as shown above, accepts the following arguments: - -| Position | Description | Data type | Required? | Default value | -| ----------- | --------------- | -------------- |-------------- | ----------------- | -| 1 | Type of VIP:
      • `PUBLIC` – An IP address that is routable on the public Internet.
      • `SERVICENET` – An IP address that is routable only on ServiceNet.
      | String | No | `PUBLIC` | -| 2 | IP version: Must be `6` | Integer | Yes | - | - - -### Remove Virtual IPs - -You can remove a VIP from a load balancer. - -```php -$vip->remove(); -``` - -Please note that a load balancer must have at least one VIP associated with it. If you try to remove a load balancer's last VIP, a `ClientErrorResponseException` will be thrown. - -## Algorithms - -Load balancers use an **algorithm** to determine how incoming traffic is distributed amongst the back-end nodes. - -### List Load Balancing Algorithms - -You can list all supported load balancing algorithms using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$algorithms = $loadBalancerService->algorithmList(); -foreach ($algorithms as $algorithm) { - /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ -} -``` - -## Protocols - -When a load balancer is created a network protocol must be specified. This network protocol should be based on the network protocol of the back-end service being load balanced. - -### List Load Balancing Protocols - -You can list all supported network protocols using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$protocols = $loadBalancerService->protocolList(); -foreach ($protocols as $protocol) { - /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ -} -``` - -## Session Persistence - -**Session persistence** is a feature of the load balancing service that forces multiple requests, of the same protocol, from clients to be directed to the same node. This is common with many web applications that do not inherently share application state between back-end servers. - -There are two types (or modes) of session persistence: - -| Name | Description | -| -------- | --------------- | -| `HTTP_COOKIE` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | -| `SOURCE_IP` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | - -### List Session Persistence Configuration - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistenceType = $sessionPersistence->persistenceType; - -/** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ -``` - -In the example above: - -* If session persistence is enabled, the value of `$sessionPersistenceType` is the type of session persistence: either `HTTP_COOKIE` or `SOURCE_IP`. -* If session persistence is disabled, the value of `$sessionPersistenceType` is `null`. - -### Enable Session Persistence - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistence->update(array( - 'persistenceType' => 'HTTP_COOKIE' -)); -``` - -### Disable Session Persistence - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistence->delete(); -``` - -## Connection Logging - -The **connection logging** feature allows logs to be delivered to a Cloud Files account every hour. For HTTP-based protocol traffic, these are Apache-style access logs. For all other traffic, this is connection and transfer logging. - -### Check Logging Configuration - -```php -/** @var $connectionLogging bool **/ - -$connectionLogging = $loadBalancer->hasConnectionLogging(); -``` -In the example above: - -* If connection logging is enabled, the value of `$connectionLogging` is `true`. -* If connection logging is disabled, the value of `$connectionLogging` is `false`. - -### Enable Connection Logging - -```php -$loadBalancer->enableConnectionLogging(true); -``` - -### Disable Connection Logging - -```php -$loadBalancer->enableConnectionLogging(false); -``` - -## Error Page - -An **error page** is the html file that is shown to the end user when an error in the service has been thrown. By default every virtual server is provided with the default error file. It is also possible to set a custom error page for a load balancer. - -### View Error Page Content - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPageContent = $errorPage->content; - -/** @var $errorPageContent string **/ -``` - -In the example above the value of `$errorPageContent` is the HTML for that page. This could either be the HTML of the default error page or of your custom error page. - -### Set Custom Error Page - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPage->update(array( - 'content' => '' -)); -``` - -### Delete Custom Error Page - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPage->delete(); -``` - -## Allowed Domains - -**Allowed domains** are a restricted set of domain names that are allowed to add load balancer nodes. - -### List Allowed Domains - -You can list all allowed domains using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$allowedDomains = $loadBalancerService->allowedDomainList(); -foreach ($allowedDomains as $allowedDomain) { - /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ -} -``` - -## Access Lists - -**Access Lists** allow fine-grained network access to a load balancer's VIP. Using access lists, network traffic to a load balancer's VIP can be allowed or denied from a single IP address, multiple IP addresses or entire network subnets. - -Note that `ALLOW` network items will take precedence over `DENY` network items in an access list. - -To reject traffic from all network items except those with the `ALLOW` type, add a `DENY` network item with the address of `0.0.0.0/0`. - -### View Access List - -You can view a load balancer's access list. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$accessList = $loadBalancer->accessList(); -foreach ($accessList as $networkItem) { - /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ -} -``` - -### Add Network Items To Access List - -You can add network items to a load balancer's access list very easily: - -```php -$loadBalancer->createAccessList(array( - (object) array( - 'type' => 'ALLOW', - 'address' => '206.160.165.1/24' - ), - (object) array( - 'type' => 'DENY', - 'address' => '0.0.0.0/0' - ) -)); -``` - -In the above example, we allowed access for 1 IP address, and used the "0.0.0.0" - wildcard to blacklist all other traffic. - -### Remove Network Item From Access List - -You an remove a network item from a load balancer's access list. - -```php -$networkItem->delete(); -``` - -## Content Caching - -When **content caching** is enabled on a load balancer, recently-accessed files are stored on the load balancer for easy retrieval by web clients. Requests to the load balancer for these files are serviced by the load balancer itself, which reduces load off its back-end nodes and improves response times as well. - -### Check Content Caching Configuration - -```php -/** @var $contentCaching bool **/ - -$contentCaching = $loadBalancer->hasContentCaching(); -``` -In the example above: - -* If content caching is enabled, the value of `$contentCaching` is `true`. -* If content caching is disabled, the value of `$contentCaching` is `false`. - -### Enable Content Caching - -```php -$loadBalancer->enableContentCaching(true); -``` - -### Disable Content Caching - -```php -$loadBalancer->enableContentCaching(false); -``` - -## SSL Termination - -The SSL Termination feature allows a load balancer user to terminate SSL traffic at the load balancer layer versus at the web server layer. A user may choose to configure SSL Termination using a key and an SSL certificate or an (Intermediate) SSL certificate. - -When SSL Termination is configured on a load balancer, a secure shadow server is created that listens only for secure traffic on a user-specified port. This shadow server is only visible to and manageable by the system. Existing or updated attributes on a load balancer with SSL Termination will also apply to its shadow server. For example, if Connection Logging is enabled on an SSL load balancer, it will also be enabled on the shadow server and Cloud Files logs will contain log files for both. - -### View current SSL termination config - -```php -/** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ - -$sslConfig = $loadBalancer->SSLTermination(); -``` - -### Update SSL termination config - -```php -$sslConfig->update(array( - 'enabled' => true, - 'securePort' => 443, - 'privateKey' => $key, - 'certificate' => $cert -)); -``` - -For a full list, with explanations, of required and optional attributes, please consult the [official documentation](http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/SSLTermination-d1e2479.html) - -### Delete SSL termination config - -```php -$sslConfig->delete(); -``` - -## Metadata - -Metadata can be associated with each load balancer and each node for the client's personal use. It is defined using key-value pairs where the key and value consist of alphanumeric characters. A key is unique per load balancer. - -### List metadata - -```php -$metadataList = $loadBalancer->metadataList(); - -foreach ($metadataList as $metadataItem) { - printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -### Add metadata - -```php -$metadataItem = $loadBalancer->metadata(); -$metadataItem->create(array( - 'key' => 'foo', - 'value' => 'bar' -)); -``` - -### Modify metadata - -```php -$metadataItem = $loadBalancer->metadata('foo'); -$metadataItem->update(array( - 'value' => 'baz' -)); -``` - -### Remove metadata - -```php -$metadataItem->delete(); -``` - -## Monitors - -The load balancing service includes a health monitoring operation which periodically checks your back-end nodes to ensure they are responding correctly. If a node is not responding, it is removed from rotation until the health monitor determines that the node is functional. In addition to being performed periodically, the health check also is performed against every node that is added to ensure that the node is operating properly before allowing it to service traffic. Only one health monitor is allowed to be enabled on a load balancer at a time. - -```php -/** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ - -$healthMonitor = $loadBalancer->healthMonitor(); - -printf( - "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", - $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout -); -``` - -For a full list, with explanations, of required and optional attributes, please consult the [official documentation](http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/Monitor_Connections-d1e3536.html) - -### Update or delete - -```php -// Update -$healthMonitor->update(array( - 'delay' => 120, - 'timeout' => 60, - 'type' => 'CONNECT' - 'attemptsBeforeDeactivation' => 3 -)); - -// Delete -$healthMonitor->delete(); -``` - -## Statistics - -You can retrieve detailed stats about your load balancer, including the following information: - -- `connectTimeOut` – Connections closed by this load balancer because the 'connect_timeout' interval was exceeded. -- `connectError` – Number of transaction or protocol errors in this load balancer. -- `connectFailure` – Number of connection failures in this load balancer. -- `dataTimedOut` – Connections closed by this load balancer because the 'timeout' interval was exceeded. -- `keepAliveTimedOut` – Connections closed by this load balancer because the 'keepalive_timeout' interval was exceeded. -- `maxConn` – Maximum number of simultaneous TCP connections this load balancer has processed at any one time. - -```php -/** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ - -$stats = $loadBalancer->stats(); -``` - -## Usage Reports - -The load balancer usage reports provide a view of all transfer activity, average number of connections, and number of virtual IPs associated with the load balancing service. Current usage represents all usage recorded within the preceding 24 hours. Values for both incomingTransfer and outgoingTransfer are expressed in bytes transferred. - -The optional startTime and endTime parameters can be used to filter all usage. If the startTime parameter is supplied but the endTime parameter is not, then all usage beginning with the startTime will be provided. Likewise, if the endTime parameter is supplied but the startTime parameter is not, then all usage will be returned up to the endTime specified. - -```php -# View billable LBs -$billable = $service->billableLoadBalancerList(); - -foreach ($billable as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - - # View usage - /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ - $usage = $loadBalancer->usage(); - - echo $usage->averageNumConnections, PHP_EOL; -} -``` +https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md index ee1de981e..1778646ad 100644 --- a/docs/userguide/Networking/README.md +++ b/docs/userguide/Networking/README.md @@ -1,76 +1,5 @@ # Networking -**Networking** is a service that you can use to create virtual networks and attach cloud devices such as servers to these networks. +Our docs have moved! Please visit the below link: -## Concepts - -## Concepts - -To use the Networking service effectively, you should understand the following key concepts: - -* **Network**: A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. - -* **Subnet**: A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. - -* **Port**: A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. - - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -To use the Networking service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Networking service object from the client. -All Networking operations are done via an _networking service object_. To -instantiate this object, call the `networkingService` method on the `$client` -object. This method takes two arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$networkingService = $client->networkingService(null, $region); -``` - -Any networks, subnets, and ports created with this `$networkingService` instance will -be stored in the cloud region specified by `$region`. - -### 3. Create a network. -```php -$network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] - -## Next steps - -Once you have created a network, there is more you can do with it. See [complete user guide for networking](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 5a7beb07f..376daa093 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -1,617 +1,5 @@ # Complete User Guide for the Networking Service -Networking is a service that you can use to create virtual networks and attach -cloud devices such as servers to these networks. +Our docs have moved! Please visit the below link: -This user guide introduces you the entities in the Networking service — -networks, subnets, and ports — and shows you how to create and manage -these entities. - -## Table of contents - * [Concepts](#concepts) - * [Prerequisites](#prerequisites) - * [Client](#client) - * [Networking service](#networking-service) - * [Networks](#networks) - * [Create a network](#create-a-network) - * [Create multiple networks](#create-multiple-networks) - * [List networks](#list-networks) - * [Get a network](#get-a-network) - * [Update a network](#update-a-network) - * [Delete a network](#delete-a-network) - * [Subnets](#subnets) - * [Create a subnet](#create-a-subnet) - * [Create multiple subnets](#create-multiple-subnets) - * [List subnets](#list-subnets) - * [Get a subnet](#get-a-subnet) - * [Update a subnet](#update-a-subnet) - * [Delete a subnet](#delete-a-subnet) - * [Ports](#ports) - * [Create a port](#create-a-port) - * [Create multiple ports](#create-multiple-ports) - * [List ports](#list-ports) - * [Get a port](#get-a-port) - * [Update a port](#update-a-port) - * [Delete a port](#delete-a-port) - * [Security Groups](#security-groups) - * [Create a security group](#create-a-security-group) - * [List security groups](#list-security-groups) - * [Get a security group](#get-a-security-group) - * [Delete a security group](#delete-a-security-group) - * [Security group rule Rules](#security-group-rules) - * [Create a security group rule](#create-a-security-group-rule) - * [List security group rules](#list-security-group-rules) - * [Get a security group rule](#get-a-security-group-rule) - * [Delete a security group rule](#delete-a-security-group-rule) - -## Concepts - -To use the Networking service effectively, you should understand the following -key concepts: - -* **Network**: An isolated virtual layer-2 broadcast domain that is typically -reserved for the tenant who created it unless it is configured to be shared. The -network is the main entity in the Networking service. Ports and subnets are -always associated with a network. - -* **Subnet**: An IP address block that can be used to assign IP addresses to -virtual instances (such as servers created using the Compute service). Each -subnet must have a CIDR and must be associated with a network. - -* **Port**: A virtual switch port on a logical network switch. Virtual instances -(such as servers created using the Compute service) attach their interfaces into -ports. The port also defines the MAC address and the IP address or addresses to -be assigned to the interfaces plugged into them. When IP addresses are -associated with a port, this also implies the port is associated with a subnet -because the IP address is taken from the allocation pool for a specific subnet. - -* **Security Group**: A named container for security group rules. - -* **Security Group Rule**: Provide users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. - -## Prerequisites - -### Client -To use the Networking service, you must first instantiate a `OpenStack` or -`Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an -`OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate an -`OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Networking service - -All Networking operations are done via a _networking service object_. To -instantiate this object, call the `networkingService` method on the `$client` -object. This method takes the following arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$networkingService = $client->networkingService(null, $region); -``` - -Any networks, subnets, and ports created with this `$networkingService` instance -are stored in the cloud region specified by `$region`. - -## Networks - -A network is an isolated virtual layer-2 broadcast domain that is typically -reserved for the tenant who created it unless it is configured to be shared. The -network is the main entity in the Networking service. Ports and subnets are -always associated with a network. - -### Create a network - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My private backend network` | -| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | -| `tenantId` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | `123456` | - -You can create a network as shown in the following example: - -```php -$network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' -)); -/** @var $network OpenCloud\Networking\Resource\Network **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] - -### Create multiple networks - -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the preceding table](#create-a-network). - -You can create multiple networks as shown in the following example: - -```php -$networks = $networkingService->createNetworks(array( - array( - 'name' => 'My private backend network #1' - ), - array( - 'name' => 'My private backend network #2' - ) -)); - -foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-networks.php) ] - -### List networks - -You can list all the networks to which you have access as shown in the following example: - -```php -$networks = $networkingService->listNetworks(); -foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-networks.php) ] - -### Get a network - -You can retrieve a specific network by using that network's ID, as shown in the following example: - -```php -$network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); -/** @var $network OpenCloud\Networking\Resource\Network **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-network.php) ] - -### Update a network - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My updated private backend network` | -| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | - -You can update a network as shown in the following example: - -```php -$network->update(array( - 'name' => 'My updated private backend network' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-network.php) ] - -### Delete a network - -You can delete a network as shown in the following example: - -```php -$network->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-network.php) ] - -## Subnets - -A subnet represents an IP address block that can be used to assign IP addresses -to virtual instances (such as servers created using the Compute service). Each -subnet must have a CIDR and must be associated with a network. - -### Create a subnet - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `networkId` | Network this subnet is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `ipVersion` | IP version | Integer (`4` or `6`) | Yes | - | `4` | -| `cidr` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | `192.168.199.0/25` | -| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | -| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `allocationPools` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | `array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))` | -| `hostRoutes` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | -| `tenantId` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | `123456` | - -You can create a subnet as shown in the following example: - -```php -$subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25' -)); -/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-subnet.php) ] - -### Create multiple subnets - -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the preceding table](#create-a-subnet). - -You can create multiple subnets as shown in the following example: - -```php -$subnets = $networkingService->createSubnets(array( - array( - 'name' => 'My subnet #1' - ), - array( - 'name' => 'My subnet #2' - ) -)); - -foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-subnets.php) ] - -### List subnets - -You can list all the subnets to which you have access as shown in the following -example: - -```php -$subnets = $networkingService->listSubnets(); -foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-subnets.php) ] - -### Get a subnet - -You can retrieve a specific subnet by using that subnet's ID, as shown in the -following example: - -```php -$subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); -/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-subnet.php) ] - -### Update a subnet - -This operation takes one parameter, an associative array, with the following -keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My updated subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.62.155` | -| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `hostRoutes` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | - -You can update a subnet as shown in the following example: - -```php -$subnet->update(array( - 'name' => 'My updated subnet', - 'hostRoutes' => array( - array( - 'destination' => '1.1.1.0/24', - 'nexthop' => '192.168.17.19' - ) - ), - 'gatewayIp' => '192.168.62.155' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-subnet.php) ] - -### Delete a subnet - -You can delete a subnet as shown in the following example: - -```php -$subnet->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-subnet.php) ] - -## Ports - -A port represents a virtual switch port on a logical network switch. Virtual -instances (such as servers created using the Compute service) attach their -interfaces into ports. The port also defines the MAC address and the IP address -or addresses to be assigned to the interfaces plugged into them. When IP -addresses are associated with a port, this also implies the port is associated -with a subnet because the IP address is taken from the allocation pool for a -specific subnet. - -### Create a port - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `networkId` | Network this port is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | -| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `macAddress` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | `0F:5A:6F:70:E9:5C` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))` | -| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | -| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | -| `tenantId` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | `123456` | - -You can create a port as shown in the following example: - -```php -$port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' -)); -/** @var $port OpenCloud\Networking\Resource\Port **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-port.php) ] - -### Create multiple ports - -This operation takes one parameter, an indexed array. Each element of this -array must be an associative array with the keys shown in -[the preceding table](#create-a-port). - -You can create multiple ports as shown in the following example: - -```php -$ports = $networkingService->createPorts(array( - array( - 'name' => 'My port #1', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ), - array( - 'name' => 'My port #2', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ) -)); - -foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-ports.php) ] - -### List ports - -You can list all the ports to which you have access as shown in the following -example: - -```php -$ports = $networkingService->listPorts(); -foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-ports.php) ] - -### Get a port - -You can retrieve a specific port by using that port's ID, as shown in the -following example: - -```php -$port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); -/** @var $port OpenCloud\Networking\Resource\Port **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-port.php) ] - -### Update a port - -This operation takes one parameter, an associative array, with the following -keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | -| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))` | -| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | -| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | - -You can update a port as shown in the following example: - -```php -$port->update(array( - 'fixedIps' => array( - array( - 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', - 'ipAddress' => '192.168.199.59' - ) - ) -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-port.php) ] - -### Delete a port - -You can delete a port as shown in the following example: - -```php -$port->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] - -## Security Groups - -A security group is a named container for [security group rules](#security-group-rules). - -### Create a security group - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | `new-webservers` | -| `description` | Description of the security group. | String | No | `null` | `security group for webservers` | - -You can create a security group as shown in the following example: - -```php -$securityGroup = $networkingService->createSecurityGroup(array( - 'name' => 'new-webservers', - 'description' => 'security group for webservers' -)); -/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-security-group.php) ] - -### List security groups - -You can list all the security groups to which you have access as shown in the following -example: - -```php -$securityGroups = $networkingService->listSecurityGroups(); -foreach ($securityGroups as $securityGroup) { - /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-security-groups.php) ] - -### Get a security group - -You can retrieve a specific security group by using that security group's ID, as shown in the -following example: - -```php -$securityGroup = $networkingService->getSecurityGroup('2076db17-a522-4506-91de-c6dd8e837028'); -/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-security-group.php) ] - -### Delete a security group - -You can delete a security group as shown in the following example: - -```php -$securityGroup->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group.php) ] - -## Security Group Rules - -A security group rule provides users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. - -### Create a security group rule - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `securityGroupId` | The security group ID to associate with this security group rule. | String | Yes | - | `2076db17-a522-4506-91de-c6dd8e837028` | -| `direction` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (`ingress` or `egress`) | Yes | - | `ingress` | -| `ethertype` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (`IPv4` or `IPv6`) | No | `IPv4` | `IPv6` | -| `portRangeMin` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the `portRangeMax` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | -| `portRangeMax` | The maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | -| `protocol` | The protocol that is matched by the security group rule. | String (`tcp`, `udp`, `icmp`) | No | `null` | `tcp` | -| `remoteGroupId` | The remote group ID to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `85cc3048-abc3-43cc-89b3-377341426ac5` | -| `remoteIpPrefix` | The remote IP prefix to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `192.168.5.0` | - -You can create a security group rule as shown in the following example: - -```php -$securityGroupRule = $networkingService->createSecurityGroupRule(array( - 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', - 'direction' => 'egress', - 'ethertype' => 'IPv4', - 'portRangeMin' => 80, - 'portRangeMax' => 80, - 'protocol' => 'tcp', - 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' -)); -/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-security-group-rule.php) ] - -### List security group rules - -You can list all the security group rules to which you have access as shown in the following -example: - -```php -$securityGroupRules = $networkingService->listSecurityGroupRules(); -foreach ($securityGroupRules as $securityGroupRule) { - /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-security-group-rules.php) ] - -### Get a security group rule - -You can retrieve a specific security group rule by using that security group rule's ID, as shown in the -following example: - -```php -$securityGroupRule = $networkingService->getSecurityGroupRule(''); -/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-security-group-rule.php) ] - -### Delete a security group rule - -You can delete a security group rule as shown in the following example: - -```php -$securityGroupRule->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group-rule.php) ] +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md index 0b15a2d19..8e224392a 100644 --- a/docs/userguide/ObjectStore/Access.md +++ b/docs/userguide/ObjectStore/Access.md @@ -1,66 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want -``` - -## Temporary URLs - -Temporary URLs allow you to create time-limited Internet addresses that allow you to grant access to your Cloud Files -account. Using Temporary URL, you may allow others to retrieve or place objects in your containers - regardless of -whether they're CDN-enabled. - -### Set "temporary URL" metadata key - -You must set this "secret" value on your account, where it can be used in a global state: - -```php -$account = $service->getAccount(); -$account->setTempUrlSecret('my_secret'); - -echo $account->getTempUrlSecret(); -``` - -The string argument of `setTempUrlSecret()` is optional - if left out, the SDK will generate a random hashed secret -for you. - -### Create a temporary URL - -Once you've set an account secret, you can create a temporary URL for your object. To allow GET access to your object -for 1 minute: - -```php -$object->getTemporaryUrl(60, 'GET'); -``` - -To allow PUT access for 1 hour: - -```php -$object->getTemporaryUrl(360, 'PUT'); -``` - -## Hosting websites on CloudFiles - -To host a static (i.e. HTML) website on CloudFiles, you must follow these steps: - -1. CDN-enable a container -2. Upload all HTML content. You can use nested directory structures. -3. Tell CloudFiles what to use for your default index page like this: - -```php -$container->setStaticIndexPage('index.html'); -``` - -4. (Optional) Tell CloudFiles which error page to use by default: - -```php -$container->setStaticErrorPage('error.html'); -``` - -Bear in mind that steps 3 & 4 do not upload content, but rather specify a reference to an existing page/CloudFiles object. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/access.html diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md index edd26c2c1..fc60751a8 100644 --- a/docs/userguide/ObjectStore/Account.md +++ b/docs/userguide/ObjectStore/Account.md @@ -1,28 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles'); -``` - -## View Account Details - -To see how many containers you have in your account (X-Account-Container-Count), how many objects are in your account -(X-Account-Object-Count), and how many total bytes your account uses (X-Account-Bytes-Used): - -```php -$account = $service->getAccount(); - -// Either return the full Metadata object -$details = $account->getDetails(); - -// or individual values -$account->getContainerCount(); -$account->getObjectCount(); -$account->getBytesUsed(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/account.html diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 642ee968d..9f3b4a5ca 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -1,78 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles'); -``` - -To access the CDN functionality of a particular container: - -```php -$container = $service->getContainer('foo_bar'); - -$cdn = $container->getCdn(); -``` - -## List CDN-enabled container - -To list CDN-only containers, follow the same operation for Storage which lists all containers. The only difference is -which service object you execute the method on: - -```php -$cdnService = $service->getCdnService(); -$cdnContainers = $cdnService->listContainers(); - -foreach ($cdnContainers as $cdnContainer) { - -} -``` - -## CDN-enable and -disable a container - -Before a container can be CDN-enabled, it must exist in the storage system. When a container is CDN-enabled, any objects -stored in it are publicly accessible over the Content Delivery Network by combining the container's CDN URL with the -object name. - -Any CDN-accessed objects are cached in the CDN for the specified amount of time called the TTL. The default TTL value is -259200 seconds, or 72 hours. Each time the object is accessed after the TTL expires, the CDN refetches and caches the -object for the TTL period. - -```php -$container->enableCdn(); -$container->disableCdn(); -``` - -## Serving containers through SSL - -```php -$cdn->getCdnSslUri(); -``` - -## Streaming CDN-enabled containers - -```php -$cdn->getCdnStreamingUri(); -``` - -## iOS streaming - -The Cloud Files CDN allows you to stream video to iOS devices without needing to convert your video. Once you -CDN-enable your container, you have the tools necessary for streaming media to multiple devices. - -```php -$cdn->getIosStreamingUri(); -``` - -## CDN logging - -To enable and disable logging for your CDN: - -```php -$cdn->enableCdnLogging(); -$cdn->disableCdnLogging(); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md index 79e3d4cdc..9f3b4a5ca 100644 --- a/docs/userguide/ObjectStore/CDN/Object.md +++ b/docs/userguide/ObjectStore/CDN/Object.md @@ -1,20 +1,5 @@ ## Setup -You will need to instantiate the container object and access its CDN functionality as -[documented here](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/CDN/Container.md). +Our docs have moved! Please visit the below link: -## Purge CDN-enabled objects - -To remove a CDN object from public access: - -```php -$object->purge(); -``` - -You can also provide an optional e-mail address (or comma-delimeted list of e-mails), which the API will send a -confirmation message to once the object has been completely purged: - -```php -$object->purge('jamie.hannaford@rackspace.com'); -$object->purge('hello@example.com,hallo@example.com'); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index 33ad706dd..ea43fcdf7 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -1,65 +1,5 @@ # Object Store -**Object Store** is an object-based storage system that stores content and metadata as objects in a cloud. +Our docs have moved! Please visit the below link: -Specifically, a cloud is made up of one or more regions. Each region can have several **containers**, created by a user. Each container can container several **objects** (sometimes referred to as files), uploaded by the user. - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -Choose one of the following two options: - -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Object Store service object from the client. -```php -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); -``` - -In the example above, you are connecting to the ``DFW`` region of the cloud. Any containers and objects created with this `$objectStoreService` instance will be stored in that cloud region. - -### 3. Create a container for your objects (also referred to as files). - -```php -$container = $objectStoreService->createContainer('logos'); -``` - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### 4. Upload an object to the container. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/quickstart.php) ] - -## Next steps - -There is a lot more you can do with containers and objects. See -the [complete user guide to the Object Store service](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index 5f75de9d1..e9fd5af14 100644 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -1,182 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -// Create a client object to communicate with various Rackspace Cloud services. -$client = new Rackspace(RACKSPACE_US, array( - 'username' => 'Replace this with your Rackspace Cloud user name', - 'apiKey' => 'Replace this with your Rackspace Cloud API key' -)); - -// Create a service object to use the object store service. The sample code -// creates the object store in the 'DFW' region. -$service = $client->objectStoreService('cloudFiles', 'DFW'); -``` - -## Create container - -To create a new container, you just need to define its name: - -```php -$container = $service->createContainer('my_amazing_container'); -``` - -If the response returned is `FALSE`, there was an API error - most likely due to the fact you have a naming collision. - -Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -## List containers - -### Return a list of containers - -```php -$containerList = $service->listContainers(); - -while ($container = $containerList->next()) { - // Do stuff; some examples below - printf("Container name: %s\n", $container->name); - printf("Number of objects within container: %d\n", $container->getObjectCount()); -} -``` - -Container names are sorted based on a binary comparison, a single built-in collating sequence that compares string -data using SQLite's memcmp() function, regardless of text encoding. - -The list is limited to 10,000 containers at a time. See 1.3 for ways to limit and navigate this list. - -### Return a formatted list of containers - -Currently, the SDK only supports JSON-formatted responses. - -### Controlling a large list of containers - -You may limit and control this list of results by using the `marker` and `end_marker` parameters. The former parameter -(`marker`) tells the API where to begin the list, and the latter (`end_marker`) tells it where to end the list. You may -use either of them independently or together. You may also use the `limit` parameter to fix the number of containers -returned. - -To list a set of containers between two fixed points: - -```php -$someContainers = $service->listContainers(array( - 'marker' => 'container_55', - 'end_marker' => 'container_2001' -)); -``` - -Or to return a limited set: - -```php -$someContainers = $service->listContainers(array('limit' => 560)); -``` - -## Get container - -To retrieve a certain container, either to access its object or metadata: - -```php -$container = $service->getContainer('container_name'); - -echo $container->getObjectCount(); -echo $container->getBytesUsed(); -``` - -## Delete container - -Deleting a container is easy: -```php -$container->delete(); -``` - -Please bear mind that you must delete all objects inside a container before deleting it. This is done for you if you -set the `$deleteObjects` parameter to `TRUE` like so: - -```php -$container->delete(TRUE); -``` - -You can also do it manually: - -```php -$container->deleteAllObjects(); -$container->delete(); -``` - -## Create or update container metadata - -```php -$container->saveMetadata(array( - 'Author' => 'Virginia Woolf', - 'Published' => '1931' -)); -``` - -Please bear in mind that this action will set metadata to this array - overriding existing values and wiping those left -out. To _append_ values to the current metadata: - -```php -$metadata = $container->appendToMetadata(array( - 'Publisher' => 'Hogarth' -)); -``` - -If you only want to set the metadata to the local object, and not immediately retain these values on the API, you can -use a standard setter method - which can contribute to eventual actions like an update: - -```php -$container->setMetadata(array('Foo' => 'Bar')); -``` - -## Container quotas - -The container_quotas middleware implements simple quotas that can be imposed on Cloud Files containers by a user. -Setting container quotas can be useful for limiting the scope of containers that are delegated to non-admin users, -exposed to formpost uploads, or just as a self-imposed sanity check. - -To set quotas for a container: - -```php -use OpenCloud\Common\Constants\Size; - -$container->setCountQuota(1000); -$container->setBytesQuota(2.5 * Size::GB); -``` - -And to retrieve them: - -```php -echo $container->getCountQuota(); -echo $container->getBytesQuota(); -``` - -## Access log delivery - -To view your object access, turn on Access Log Delivery. You can use access logs to analyze the number of people who -access your objects, where they come from, how many requests for each object you receive, and time-based usage patterns -(such as monthly or seasonal usage). - -```php -$container->enableLogging(); -$container->disableLogging(); -``` - -## Syncing containers - -You can synchronize local directories with your CloudFiles/Swift containers very easily. When you do this, the container -will mirror exactly the nested file structure within your local directory: - -```php -$container->uploadDirectory('/home/Jamie/blog'); -``` - -There are four scenarios you should be aware of: - -Local|Remote|Comparison|Action ----|---|---|--- -File exists|File exists|Identical checksum|No action -File exists|File exists|Different checksum|Local file overwrites remote -File exists|File does not exist|-|Local file created in Swift -Files does not exist|File exists|-|Remote file deleted \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/containers.html diff --git a/docs/userguide/ObjectStore/Storage/Migrating.md b/docs/userguide/ObjectStore/Storage/Migrating.md index d92528cbf..8d12c382a 100644 --- a/docs/userguide/ObjectStore/Storage/Migrating.md +++ b/docs/userguide/ObjectStore/Storage/Migrating.md @@ -1,83 +1,5 @@ # Migrating containers (across regions) -## Introduction +Our docs have moved! Please visit the below link: -Currently, there exists no single API operation to copy containers across geographic endpoints. Although the API offers -a `COPY` operation for individual files, this does not work for cross-region copying. The SDK, however, does offer this -functionality. - -You **will** be charged for bandwidth between regions, so it's advisable to use ServiceNet where possible (which is -free). - -## Requirements - -* You must install the full Guzzle package, so that the process can take advantage of Guzzle's batching functionality -(it allows parallel requests to be batched for greater efficiency). You can do this by running: - -```bash -php composer.phar install --dev -``` - -* Depending on the size and number of transfer items, you will need to raise PHP's memory limit: - -```php -ini_set('memory_limit', '512M'); -``` - -* You will need to enact some kind of backoff/retry strategy for rate limits. Guzzle comes with a convenient feature -that just needs to be added as a normal subscriber: - -```php -use Guzzle\Plugin\Backoff\BackoffPlugin; - -$client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); -``` - -This tells the client to retry up to `10` times for failed requests have resulted in these HTTP status codes: `500`, -`503` or `408`. - -## Setup - -You can access all this functionality by executing: - -```php -$ordService = $client->objectStoreService('cloudFiles', 'ORD'); -$iadService = $client->objectStoreService('cloudFiles', 'IAD'); - -$oldContainer = $ordService->getContainer('old_container'); -$newContainer = $iadService->getContainer('new_container'); - -$iadService->migrateContainer($oldContainer, $newContainer); -``` - -It's advisable to do this process in a Cloud Server in one of the two regions you're migrating to/from. This allows you -to use `privateURL` as the third argument in the `objectStoreService` methods like this: - -```php -$client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); -``` - -This will ensure that traffic between your server and your new IAD container will be held over the internal Rackspace -network which is free. - -## Options - -You can pass in an array of arguments to the method: - -```php -$options = array( - 'read.batchLimit' => 100, - 'read.pageLimit' => 100, - 'write.batchLimit' => 50 -); - -$iadService->migrateContainer($oldContainer, $newContainer, $options); -``` - -### Options explained - -Name|Description|Default ----|---|--- -`read.pageLimit`|When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional `objectList` method, which calls the `PaginatedIterator`. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API)|10,000 -`read.batchLimit`|After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together.|1,000 -`write.batchLimit`|Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together.|100 \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/migrating-containers.html diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 4127b2610..0c58ca9fb 100644 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -1,275 +1,5 @@ ## Setup -Conceptually, a container contains objects (also known as files). In order to work with -objects, you will need to instantiate a container object first as [documented here](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/Storage/Container.md). +Our docs have moved! Please visit the below link: -## Note on object properties - -Please be aware that you cannot directly access the properties of DataObject anymore, you __must__ use appropriate getter/ -setter methods: - -|Property|Method| -|--------|------| -|Parent container|`getContainer`| -|Name|`getName`| -|Body of file|`getContent`| -|Size of file|`getContentLength`| -|Type of file|`getContentType`| -|ETag checksum|`getEtag`| -|Last modified date|`getLastModified`| - -## Create an object - -There are three ways to upload a new file, each of which has different business needs. - -> **Note:** Unlike previous versions, you do not need to manually specify your object's content type. The API will do this -for you. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### To upload a single/basic file: - -```php -use OpenCloud\ObjectStore\Resource\DataObject; - -$data = fopen('/path/to/sample.mp3', 'r+'); - -// alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) - -$meta = array( - 'Author' => 'Camera Obscura', - 'Origin' => 'Glasgow' -); - -$metaHeaders = DataObject::stockHeaders($meta); -$customHeaders = array(); -$allHeaders = $metaHeaders + $customHeaders; - -$container->uploadObject('sample.mp3', $data, $allHeaders); -``` - -### To upload multiple small-to-mid sized files: - -```php -$files = array( - array( - 'name' => 'apache.log', - 'path' => '/etc/httpd/logs/error_log' - ), - array( - 'name' => 'mysql.log', - 'body' => fopen('/tmp/mysql.log', 'r+') - ), - array( - 'name' => 'to_do_list.txt', - 'body' => 'PHONE HOME' - ) -); - -$container->uploadObjects($files); -``` - -As you can see, the `name` key is required for every file. You must also specify _either_ a path key (to an existing -file), or a `body`. The `body` can either be a PHP resource or a string representation of the content you want to upload. - -### To upload large files - -For files over 5GB, you will need to use the `OpenCloud\ObjectStore\Upload\TransferBuilder` factory to build your transfer, -upon which you can execute your upload functionality. For your convenience, the Container resource object contains a -simple method to do this heavy lifting for you: - -```php -$transfer = $container->setupObjectTransfer(array( - 'name' => 'video.mov', - 'path' => '/home/jamie/video.mov', - 'metadata' => array( - 'Author' => 'Jamie' - ), - 'concurrency' => 4, - 'partSize' => 1.5 * Size::GB -)); - -$transfer->upload(); -``` - -You can specify how many concurrent cURL connections are used to upload parts of your file. The file is fragmented into -chunks, each of which is uploaded individually as a separate file (the filename of each part will indicate that it's a -segment rather than the full file). After all parts are uploaded, a manifest is uploaded. When the end-user accesses -the 5GB by its true filename, it actually references the manifest file which concatenates each segment into a streaming -download. - -## List objects in a container - -To return a list of objects: - -```php -$files = $container->objectList(); - -foreach ($files as $file) { - // ... do something -} -``` - -By default, 10,000 objects are returned as a maximum. To get around this, you can construct a query which refines your -result set. For a full specification of query parameters relating to collection filtering, see the [official docs](http://docs.openstack.org/api/openstack-object-storage/1.0/content/list-objects.html). - -```php -$container->objectList(array('prefix' => 'logFile_')); -``` - -## Get object - -To retrieve a specific file from Cloud Files: - -```php -$file = $container->getObject('summer_vacation.mp4'); -``` - -### Conditional requests - -You can also perform conditional requests according to [RFC 2616 specification](http://www.ietf.org/rfc/rfc2616.txt) -(§§ 14.24-26). Supported headers are `If-Match`, `If-None-Match`, `If-Modified-Since` and `If-Unmodified-Since`. - -So, to retrieve a file's contents only if it's been recently changed - -```php -$file = $container->getObject('error_log.txt', array( - 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' -)); - -if ($file->getContentLength()) { - echo 'Has been changed since the above date'; -} else { - echo 'Has not been changed'; -} -``` - -Retrieve a file only if it has NOT been modified (and expect a 412 on failure): - -``` -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( - 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' - )); -} catch (ClientErrorResponseException $e) { - echo 'This file has been modified...'; -} -``` - -Finally, you can specify a range - which will return a subset of bytes from the file specified. To return the last 20B -of a file: - -```php -$snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); -``` - -## Update an existing object - -Updating content is easy: - -```php -$file->setContent(fopen('/path/to/new/content', 'r+')); -$file->update(); -``` - -Bear in mind that updating a file name will result in a new file being generated (under the new name). You will need to -delete the old file. - -## Copy object - -To copy a file to another location, you need to specify a string-based destination path: - -```php -$object->copy('/container_2/new_object_name'); -``` - -## Delete object - -```php -$object->delete(); -``` - -## Get object metadata -You can fetch just the object metadata without fetching the full content: -```php -$container->getPartialObject('summer_vacation.mp4'); -``` - -In order to access the metadata on a partial or complete object, use: - -```php -$object->getMetadata(); -``` - -You can turn a partial object into a full object to get the content after looking at the metadata: -```php -$object->refresh(); -``` - -You can also update to get the latest metadata: - -```php -$object->retrieveMetadata(); -``` - -## Update object metadata - -Similarly, with setting metadata there are two options: you can update the metadata values of the local object (i.e. no -HTTP request) if you anticipate you'll be executing one soon (an update operation for example): - -```php -// There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation -$object->setMetadata(array( - 'Author' => 'Hemingway' -)); - -// ... code here - -$object->update(); -``` - -Alternatively, you can update the API straight away - so that everything is retained: - -```php -$object->saveMetadata(array( - 'Author' => 'Hemingway' -)); -``` - -Please be aware that these methods override and wipe existing values. If you want to append values to your metadata, use -the correct method: - -```php -$metadata = $object->appendToMetadata(array( - 'Author' => 'Hemingway' -)); - -$object->saveMetadata($metadata); -``` - -## Extract archive - -CloudFiles provides you the ability to extract uploaded archives to particular destinations. The archive will be extracted -and its contents will populate the particular area specified. To upload file (which might represent a directory structure) -into a particular container: - -```php -use OpenCloud\ObjectStore\Constants\UrlType; - -$service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); -``` - -You can also omit the container name (i.e. provide an empty string as the first argument). If you do this, the API will -create the containers necessary to house the extracted files - this is done based on the filenames inside the archive. - -## Bulk delete - -Bulk delete a set of paths: - -```php -$pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - -$service->bulkDelete($pathsToBeDeleted); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/objects.html diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index fa04bdeaf..6881ee3e6 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -1,625 +1,5 @@ # The Complete User Guide to the Object Store Service -**Object Store** is an object-based storage system that stores content and metadata as objects in a cloud. +Our docs have moved! Please visit the below link: -## Prerequisites - -### Client -To use the object store service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Object Store Service -All operations on the object store are done via an object store service object. - -```php -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); -``` - -In the example above, you are connecting to the ``DFW`` region of the cloud. Any containers and objects created with this `$objectStoreService` instance will be stored in that cloud region. - -## Containers -A **container** defines a namespace for **objects**. An object with the same name in two different containers represents two different objects. - -For example, you may create a container called `logos` to hold all the image files for your blog. - -A container may contain zero or more objects in it. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### Create Container - -```php -$container = $objectStoreService->createContainer('logos'); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/create-container.php) ] - -### Get Container Details -You can retrieve a single container's details by using its name. An instance of `OpenCloud\ObjectStore\Resource\Container` is returned. - -```php -$container = $objectStoreService->getContainer('logos'); - -/** @var $container OpenCloud\ObjectStore\Resource\Container **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container.php) ] - -### List Containers -You can retrieve a list of all your containers. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$containers = $objectStoreService->listContainers(); -foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-containers.php) ] - -### Set or Update Container Metadata -You can set metadata on a container. - -```php -$container->saveMetadata(array( - 'author' => 'John Doe' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-metadata.php) ] - -### Get Container Metadata -You can retrieve the metadata for a container. - -```php -$containerMetadata = $container->getMetadata(); - -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-metadata.php) ] - -### Delete Container -When you no longer have a need for the container, you can remove it. - -If the container is empty (that is, it has no objects in it), you can remove it as shown below: - -```php -$container->delete(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/delete-container.php) ] - -If the container is not empty (that is, it has objects in it), you have two choices in how to remove it: - -* [Individually remove each object](#delete-object) in the container, then remove the container itself as shown above, or - -* Remove the container and all the objects within it as shown below: - - ```php - $container->delete(true); - ``` - [ [Get the executable PHP script for this example](/samples/ObjectStore/delete-container-recursive.php) ] - -### Get Object Count - -You can quickly find out how many objects are in a container. - -```php -$containerObjectCount = $container->getObjectCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-object-count.php) ] - -In the example above, `$containerObjectCount` will contain the number of objects in the container represented by `$container`. - -### Get Bytes Used - -You can quickly find out the space used by a container, in bytes. - -```php -$containerSizeInBytes = $container->getBytesUsed(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-bytes-used.php) ] - -In the example above, `$containerSizeInBytes` will contain the space used, in bytes, by the container represented by `$container`. - -### Container Quotas - -#### Set Quota for Number of Objects - -You can set a quota for the maximum number of objects that may be stored in a container. - -```php -$maximumNumberOfObjectsAllowedInContainer = 25; -$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-count-quota.php) ] - -#### Set Quota for Total Size of Objects - -You can set a quota for the maximum total space (in bytes) used by objects in a container. - -```php -use OpenCloud\Common\Constants\Size; - -$maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; -$container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-bytes-quota.php) ] - -#### Get Quota for Number of Objects - -You can retrieve the quota for the maximum number of objects that may be stored in a container. - -```php -$maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-count-quota.php) ] - -#### Get Quota for Total Size of Objects - -You can retrieve the quota for the maximum total space (in bytes) used by objects in a container. - -```php -$maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-bytes-quota.php) ] - -## Objects - -An **object** (sometimes referred to as a file) is the unit of storage in an Object Store. An object is a combination of content (data) and metadata. - -For example, you may upload an object named `php-elephant.jpg`, a JPEG image file, to the `logos` container. Further, you may assign metadata to this object to indicate that the author of this object was someone named Jane Doe. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### Upload Object - -Once you have created a container, you can upload objects to it. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$object = $container->uploadObject($remoteFileName, $fileData); -/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object.php) ] - -In the example above, an image file from the local filesystem (`path/to/local/php-elephant.jpg`) is uploaded to a container in the Object Store. - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -It is also possible to upload an object and associate metadata with it. - -```php -use OpenCloud\ObjectStore\Resource\DataObject; - -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); -$metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData, $allHeaders); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object-with-metadata.php) ] - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -#### Pseudo-hierarchical Folders -Although you cannot nest directories in an Object Store, you can simulate a hierarchical structure within a single container by adding forward slash characters (`/`) in the object name. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'languages/php/elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/pseudo-hierarchical-folders.php) ] - -In the example above, an image file from the local filesystem (`/path/to/local/php-elephant.jpg`) is uploaded to a container in the Object Store. Within that container, the filename is `languages/php/elephant.jpg`, where `languages/php/` is a pseudo-hierarchical folder hierarchy. - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -#### Upload Multiple Objects -You can upload more than one object at a time to a container. - -```php -$objects = array( - array( - 'name' => 'php-elephant.jpg', - 'path' => '/path/to/local/php-elephant.jpg' - ), - array( - 'name' => 'python-snake.jpg', - 'path' => '/path/to/local/python-snake.jpg' - ), - a -); - -$responses = $container->uploadObjects($objects); -foreach ($responses as $response) { - /** @var $response \Guzzle\Http\Message\Response **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects.php) ] - -In the above example, the contents of two files present on the local filesystem are uploaded as objects to the container referenced by `$container`. - -Instead of specifying the `path` key in an element of the `$objects` array, you can specify a `body` key whose value is a string or a stream representation. - -You can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. - -``` -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); -$metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; - -$container->uploadObjects($objects, $allHeaders); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects-with-metadata.php) ] - -In the example above, every object referenced within the `$objects` array will be uploaded with the same metadata. - -Finally, if you want the `uploadObjects` method to return an array of `OpenCloud\ObjectStore\Resource\DataObject` objects instead of an array of `Guzzle\Http\Message\Response` objects, you can specify a third parameter to the `uploadObjects` method: - -``` -use OpenCloud\ObjectStore\Enum\ReturnType; - -$dataObjects = $container->uploadObjects($objects, $allHeaders, ReturnType::DATA_OBJECT_ARRAY); -foreach ($dataObjects as $dataObject) { - /** @var $dataObject OpenCloud\ObjectStore\Resource\DataObject **/ -} -``` - -### Large Objects - -If you want to upload objects larger than 5GB in size, you must use a different upload process. - -```php -$options = array( - 'name' => 'san_diego_vacation_video.mp4', - 'path' => '/path/to/local/videos/san_diego_vacation.mp4' -); -$objectTransfer = $container->setupObjectTransfer($options); -$objectTransfer->upload(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-large-object.php) ] - -The process shown above will automatically partition your large object into small chunks and upload them concurrently to the container represented by `$container`. - -You can tune the parameters of this process by specifying additional options in the `$options` array. Here is a complete listing of keys that can be specified in the `$options` array: - -| Key name | Description | Data Type | Required? | Default Value | Example | -| -------------- | --------------- | ------------- | -------------- | ------------------ | ----------- | -| `name` | Name of large object in container | String | Yes | - | `san_diego_vacation_video.mp4` | -| `path` | Path to file containing object data on local filesystem | String | One of `path` or `body` must be specified | - | `/path/to/local/videos/san_diego_vacation.mp4` | -| `body` | String or stream representation of object data | String \| Stream | One of `path` or `body` must be specified | - | `... lots of data ...` | -| `metadata` | Metadata for the object | Associative array of metadata key-value pairs | No | `array()` | `array( "Author" => "Jane Doe" )` | -| `partSize` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | `1073741824` (1GB) | `52428800` (50MB) | -| `concurrency` | The number of concurrent transfers to execute as part of the upload | Integer | No | `1` (no concurrency; upload chunks serially) | `10` | -| `progress` | A [callable function or method](http://us1.php.net/manual/en/language.types.callable.php) which is called to report progress of the the upload. See [`CURLOPT_PROGRESSFUNCTION` documentation](http://us2.php.net/curl_setopt) for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | `reportProgress` | - -### Auto-extract Archive Files - -You can upload a tar archive file and have the Object Store service automatically extract it into a container. - -```php -use OpenCloud\ObjectStore\Constants\UrlType; - -$localArchiveFileName = '/path/to/local/image_files.tar.gz'; -$remotePath = 'images/'; - -$fileData = fopen($localArchiveFileName, 'r'); -$objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/auto-extract-archive-files.php) ] - -In the above example, a local archive file named `image_files.tar.gz` is uploaded to an Object Store container named `images` (defined by the `$remotePath` variable). - -Note that while we call `fopen` to open a file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `bulkExtract` call. - -The third parameter to `bulkExtract` is the type of the archive file being uploaded. The acceptable values for this are: - -* `UrlType::TAR` for tar archive files, *or*, -* `UrlType:TAR_GZ` for tar archive files that are compressed with gzip, *or* -* `UrlType::TAR_BZ` for tar archive file that are compressed with bzip - -Note that the value of `$remotePath` could have been a (pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as `images/blog` as well. - -### List Objects in a Container -You can list all the objects stored in a container. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$objects = $container->objectList(); -foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects.php) ] - -You can list only those objects in the container whose names start with a certain prefix. - -```php -$options = array( - 'prefix' => 'php' -); - -$objects = $container->objectList($options); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects-with-prefix.php) ] - -In general, the `objectList()` method described above takes an optional parameter (`$options` in the example above). This parameter is an associative array of various options. Here is a complete listing of keys that can be specified in the `$options` array: - -| Key name | Description | Data Type | Required? | Default Value | Example | -| -------------- | --------------- | ------------- | -------------- | ------------------ | ----------- | -| `prefix` | Given a string x, limits the results to object names beginning with x. | String | No | | `php` | -| `limit` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | -| `marker` | Given a string x, returns object names greater than the specified marker. | String | No | | `php-elephant.jpg` | -| `end_marker` | Given a string x, returns object names less than the specified marker. | String | No | | `python-snakes.jpg` | - -### Retrieve Object -You can retrieve an object and its metadata, given the object's container and name. - -```php -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); - -/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - -$objectContent = $object->getContent(); - -/** @var $objectContent Guzzle\Http\EntityBody **/ - -// Write object content to file on local filesystem. -$objectContent->rewind(); -$stream = $objectContent->getStream(); -$localFilename = tempnam("/tmp", 'php-opencloud-'); -file_put_contents($localFilename, $stream); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object.php) ] - -In the example above, `$object` is the object named `php-elephant.jpg` in the container represented by `$container`. Further, `$objectContent` represents the contents of the object. It is of type [`Guzzle\Http\EntityBody`](http://api.guzzlephp.org/class-Guzzle.Http.EntityBody.html). - -### Retrieve Object Metadata -You can retrieve just an object's metadata without retrieving its contents. - -```php -$objectName = 'php-elephant.jpg'; -$object = $container->getPartialObject($objectName); -$objectMetadata = $object->getMetadata(); - -/** @var $objectMetadata \OpenCloud\Common\Metadata **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object-metadata.php) ] - -In the example above, while `$object` is an instance of `OpenCloud\ObjectStore\Resource\DataObject`, that instance is only partially populated. Specifically, only properties of the instance relating to object metadata are populated. - -### Temporary URLs - -The Temporary URL feature allows you to create limited-time Internet addresses that allow you to grant limited access to your Object Store account. Using this feature, you can allow others to retrieve or place objects in your Object Store account for a specified amount of time. Access to the temporary URL is independent of whether or not your account is [CDN-enabled](#cdn-containers). Even if you do not CDN-enable a container, you can still grant temporary public access through a temporary URL. - -First, you must set the temporary URL secret on your account. This is a one-time operation; you only need to perform it the very first time you wish to use the temporary URLs feature. - -```php -$account->setTempUrlSecret(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-account-temp-url-secret.php) ] - -Note that this operation is carried out on `$account`, which is an instance of `OpenCloud\ObjectStore\Resource\Account`, a class representing [your object store account](#accounts). - -The above operation will generate a random secret and set it on your account. Instead of a random secret, if you wish to provide a secret, you can supply it as a parameter to the `setTempUrlSecret` method. - -```php -$account->setTempUrlSecret(''); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-account-temp-url-secret-specified.php) ] - -Once a temporary URL secret has been set on your account, you can generate a temporary URL for any object in your Object Store. - -```php -$expirationTimeInSeconds = 3600; // one hour from now -$httpMethodAllowed = 'GET'; -$tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object-temporary-url.php) ] - -In the example above, a temporary URL for the object is generated. This temporary URL will provide public access to the object for an hour (3600 seconds), as specified by the `$expirationTimeInSeconds` variable. Further, only GET HTTP methods will be allowed on this URL, as specified by the `$httpMethodAllowed` variable. The other value allowed for the `$httpMethodAllowed` variable would be `PUT`. - -You can also retrieve the temporary URL secret that has been set on your account. - -```php -$tempUrlSecret = $account->getTempUrlSecret(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-temp-url-secret.php) ] - -### Update Object - -You can update an object's contents (as opposed to [updating its metadata](#update-object-metadata)) by simply re-[uploading the object](#upload-object) to its container using the same object name as before. - -### Update Object Metadata - -You can update an object's metadata after it has been uploaded to a container. - -```php -$object->saveMetadata(array( - 'author' => 'John Doe' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/update-object-metadata.php) ] - -### Copy Object - -You can copy an object from one container to another, provided the destination container already exists. - -```php -$object->copy('logos_copy/php.jpg'); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/copy-object.php) ] - -In the example above, both the name of the destination container (`logos_copy`)and the name of the destination object (`php.jpg`) have to be specified, separated by a `/`. - -### Delete Object - -When you no longer need an object, you can delete it. - -```php -$object->delete(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/delete-object.php) ] - -### Bulk Delete - -While you can delete individual objects as shown above, you can also delete objects and empty containers in bulk. - -```php -$objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/bulk-delete.php) ] - -In the example above, two objects (`some_container/object_a.png`, `some_other_container/object_z.png`) and one empty container (`some_empty_container`) are all being deleted in bulk via a single command. - -## CDN Containers - -Note: The functionality described in this section is available only on the Rackspace cloud. It will not work as described when working with a vanilla OpenStack cloud. - -Any container can be converted to a CDN-enabled container. When this is done, the objects within the container can be accessed from anywhere on the Internet via a URL. - -### Enable CDN Container - -To take advantage of CDN capabilities for a container and its objects, you must CDN-enable that container. - -```php -$container->enableCdn(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/enable-container-cdn.php) ] - -### Public URLs - -Once you have CDN-enabled a container, you can retrieve a publicly-accessible URL for any of its objects. There are four types of publicly-accessible URLs for each object. Each type of URL is meant for a different purpose. The sections below describe each of these URL types and how to retrieve them. - -#### HTTP URL -You can use this type of URL to access the object over HTTP. - -``` -$httpUrl = $object->getPublicUrl(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-http-url.php) ] - -#### Secure HTTP URL -You can use this type of URL to access the object over HTTP + TLS/SSL. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$httpsUrl = $object->getPublicUrl(UrlType::SSL); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-https-url.php) ] - -#### Streaming URL -You can use this type of URL to stream a video or audio object using Adobe's HTTP Dynamic Streaming. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$streamingUrl = $object->getPublicUrl(UrlType::STREAMING); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-streaming-url.php) ] - -#### IOS Streaming URL - -You can use this type of URL to stream an audio or video object to an iOS device. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-ios-streaming-url.php) ] - -### Update CDN Container TTL -You can update the TTL of a CDN-enabled container. - -```php -$cdnContainer = $container->getCdn(); -$cdnContainer->setTtl(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-cdn-container-ttl.php) ] - -### Disable CDN Container - -If you no longer need CDN capabilities for a container, you can disable them. - -```php -$container->disableCdn(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/disable-container-cdn.php) ] - -## Accounts -An **account** defines a namespace for **containers**. An account can have zero or more containers in it. - -### Retrieve Account -You must retrieve the account before performing any operations on it. - -```php -$account = $objectStoreService->getAccount(); -``` - -### Get Container Count - -You can quickly find out how many containers are in your account. - -```php -$accountContainerCount = $account->getContainerCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-container-count.php) ] - -### Get Object Count - -You can quickly find out how many objects are in your account. - -```php -$accountObjectCount = $account->getObjectCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-object-count.php) ] - -In the example above, `$accountObjectCount` will contain the number of objects in the account represented by `$account`. - -### Get Bytes Used - -You can quickly find out the space used by your account, in bytes. - -```php -$accountSizeInBytes = $account->getBytesUsed(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-bytes-used.php) ] - -In the example above, `$accountSizeInBytes` will contain the space used, in bytes, by the account represented by `$account`. +https://doc.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index cb3c6245a..862a61ed7 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -1,81 +1,5 @@ # Orchestration -**Orchestration** is a service that can be used to create and manage cloud -resources. Examples of such resources are databases, load balancers, -servers and software installed on them. +Our docs have moved! Please visit the below link: -## Concepts - -To use the Orchestration service effectively, you should understand several -key concepts: - -* **Template**: An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment. The template specifies what resources should be used, what -attributes of these resources are parameterized and what information is -output to the user when a template is instantiated. - -* **Resource**: A resource is a template artifact that represents some component of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load balancer, some configuration management system, and so forth). - -* **Stack**: A stack is a running instance of a template. When a stack is created, -the resources specified in the template are created. - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Orchestration service object from the client. -All Orchestration operations are done via an _orchestration service object_. To -instantiate this object, call the `orchestrationService` method on the `$client` -object as shown in the following example: - -```php -$region = ''; -$orchestrationService = $client->orchestrationService(null, $region); -``` - -Any stacks and resources created with this `$orchestrationService` instance will -be stored in the cloud region specified by `$region`. - -### 3. Create a stack from a template. -```php -$stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/quickstart.php) ] - -## Next steps - -Once you have created a stack, there is more you can do with it. See [complete user guide for orchestration](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index a6d8af0f9..902d7741b 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -1,506 +1,5 @@ # Complete User Guide for the Orchestration Service -Orchestration is a service that you can use to create and manage cloud resources -such as databases, load balancers, and servers, and the software installed on -servers. +Our docs have moved! Please visit the below link: -## Table of Contents - -* [Concepts](#concepts) -* [Prerequisites](#prerequisites) - * [Client](#client) - * [Orchestration service](#orchestration-service) -* [Templates](#templates) - * [Validate template](#validate-template) - * [Validate a template from a file](#validate-a-template-from-a-file) - * [Validate Template from URL](#validate-template-from-url) -* [Stacks](#stacks) - * [Preview stack](#preview-stack) - * [Preview a stack from a template file](#preview-a-stack-from-a-template-file) - * [Preview a stack from a template URL](#preview-a-stack-from-a-template-url) - * [Create stack](#create-stack) - * [Create a stack from a template file](#create-a-stack-from-a-template-file) - * [Create a stack from a template URL](#create-a-stack-from-a-template-url) - * [List stacks](#list-stacks) - * [Get stack](#get-stack) - * [Get stack template](#get-stack-template) - * [Update stack](#update-stack) - * [Update a stack from a template file](#update-a-stack-from-a-template-file) - * [Update Stack from Template URL](#update-stack-from-template-url) - * [Delete stack](#delete-stack) - * [Abandon Stack](#abandon-stack) - * [Adopt stack](#adopt-stack) -* [Stack resources](#stack-resources) - * [List stack resources](#list-stack-resources) - * [Get stack resource](#get-stack-resource) - * [Get stack resource metadata](#get-stack-resource-metadata) -* [Stack resource events](#stack-resource-events) - * [List stack events](#list-stack-events) - * [List stack resource events](#list-stack-resource-events) - * [Get stack resource event](#get-stack-resource-event) -* [Resource types](#resource-types) - * [List resource types](#list-resource-types) - * [Get resource type](#get-resource-type) - * [Get resource type template](#get-resource-type-template) -* [Build info](#build-info) - * [Get build info](#get-build-info) - -## Concepts - -To use the Orchestration service effectively, you should understand the following -key concepts: - -* **Template**: A JSON or YAML document that describes how a set of resources should -be assembled to produce a working deployment. The template specifies the resources -to use, the attributes of these resources that are parameterized and the information -that is sent to the user when a template is instantiated. - -* **Resource**: Some component of your architecture (a cloud server, a group of scaled -cloud servers, a load balancer, some configuration management system, and so on) that -is defined in a template. - -* **Stack**: A running instance of a template. When a stack is created, the resources -specified in the template are created. - -## Prerequisites - -### Client -To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Orchestration service -All Orchestration operations are done via an _orchestration service object_. To -instantiate this object, call the `orchestrationService` method on the `$client` -object. This method takes two arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudOrchestration` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$orchestrationService = $client->orchestrationService(null, $region); -``` - -Any stacks and resources created with this `$orchestrationService` instance will -be stored in the cloud region specified by `$region`. - -## Templates -An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment (known as a [stack](#stacks)). The template specifies the resources -to use, the attributes of these resources that are parameterized and the -information that is sent to the user when a template is instantiated. - -### Validate template -Before you use a template to create a stack, you might want to validate it. - -#### Validate a template from a file -If your template is stored on your local computer as a JSON or YAML file, you -can validate it as shown in the following example: - -```php -use OpenCloud\Common\Exceptions\InvalidTemplateError; - -try { - $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') - )); -} catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid -} -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] - -#### Validate Template from URL -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can validate it as shown in the following example: - -```php -use OpenCloud\Common\Exceptions\InvalidTemplateError; - -try { - $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' - )); -} catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid -} -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] - -## Stacks -A stack is a running instance of a template. When a stack is created, the -[resources](#stack-resources) specified in the template are created. - -### Preview stack -Before you create a stack from a template, you might want to see what that stack -will look like. This is called _previewing the stack_. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters | Yes | - | `simple-lamp-setup` | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of the template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters. For example, see the parameters in [this template section](https://github.com/rackspace-orchestration-templates/lamp/blob/master/lamp.yaml#L22) | Associative array | No | `null` | `array('flavor_id' => 'general1-1')` | - -#### Preview a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to preview a stack as shown in the following example: - -```php -$stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-file.php) ] - -#### Preview a stack from a template URL - -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to preview a stack as shown in the following -example: - -```php -$stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-url.php) ] - -### Create stack -You can create a stack from a template. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | - | `simple-lamp-setup` | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null` | `array('server_hostname' => 'web01')` | -| `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | - -#### Create a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to create a stack as shown in the following example: - -```php -$stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-file.php) ] - -#### Create a stack from a template URL -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to create a stack as shown in the following -example: - -```php -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-url.php) ] - -### List stacks -You can list all the stacks that you have created as shown in the following example: - -```php -$stacks = $orchestrationService->listStacks(); -foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stacks.php) ] - -### Get stack -You can retrieve a specific stack using its name, as shown in the following example: - -```php -$stack = $orchestrationService->getStack('simple-lamp-setup'); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack.php) ] - -### Get stack template -You can retrieve the template used to create a stack. Note that a JSON string is -returned, regardless of whether a JSON or YAML template was used to create the stack. - -```php -$stackTemplate = $stack->getTemplate(); -/** @var $stackTemplate string **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-template.php) ] - -### Update stack -You can update a running stack. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null`| `array('flavor_id' => 'general1-1')` | -| `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | - -#### Update a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to update a stack as shown in the following example: - -```php -$stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-file.php) ] - -#### Update Stack from Template URL - -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to update a stack as shown in the following -example: - -```php -$stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-url.php) ] - -### Delete stack - -If you no longer need a stack and all its resources, you can delete the stack -_and_ the resources as shown in the following example: - -```php -$stack->delete(); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/delete-stack.php) ] - -### Abandon Stack - -If you want to delete a stack but preserve all its resources, you can abandon -the stack as shown in the following example: - -```php -$abandonStackData = $stack->abandon(); -/** @var $abandonStackData string **/ - -file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/abandon-stack.php) ] - -Note that this operation returns data about the abandoned stack as a string. You -can use this data to recreate the stack by using the [adopt stack](#adopt-stack) -operation. - -### Adopt stack - -If you have data from an abandoned stack, you can re-create the stack as shown -in the following example: - -```php -$stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/adopt-stack.php) ] - -## Stack resources - -A stack is made up of zero or more resources such as databases, load balancers, -and servers, and the software installed on servers. - -### List stack resources - -You can list all the resources for a stack as shown in the following example: - -```php -$resources = $stack->listResources(); -foreach ($resources as $resource) { - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] - -### Get stack resource - -You can retrieve a specific resource in a stack bt using that resource's name, -as shown in the following example: - -```php -$resource = $stack->getResource('load-balancer'); -/** @var $resource OpenCloud\Orchestration\Resource\Resource **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] - -### Get stack resource metadata - -You can retrieve the metadata for a specific resource in a stack as shown in the -following example: - -```php -$resourceMetadata = $resource->getMetadata(); -/** @var $resourceMetadata \stdClass **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] - -## Stack resource events -Operations on resources within a stack (such as the creation of a resource) produce -events. - -### List stack events -You can list all of the events for all of the resources in a stack as shown in -the following example: - -```php -$stackEvents = $stack->listEvents(); -foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] - -### List stack resource events -You can list all of the events for a specific resource in a stack as shown in the -following example: - -```php -$resourceEvents = $resource->listEvents(); -foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] - -### Get stack resource event -You can retrieve a specific event for a specific resource in a stack, by using -the resource event's ID, as shown in the following example: - -```php -$resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -/** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] - -## Resource types -When you define a template, you must use resource types supported by your cloud. - -### List resource types -You can list all supported resource types as shown in the following example: - -```php -$resourceTypes = $orchestrationService->listResourceTypes(); -foreach ($resourceTypes as $resourceType) { - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-resource-types.php) ] - -### Get resource type -You can retrieve a specific resource type's schema as shown in the following example: - -```php -$resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); -/** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type.php) ] - -### Get resource type template -You can retrieve a specific resource type's representation as it would appear -in a template, as shown in the following example: - -```php -$resourceTypeTemplate = $resourceType->getTemplate(); -/** @var $resourceTypeTemplate string **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type-template.php) ] - -## Build info - -### Get build info -You can retrieve information about the current Orchestration service build as -shown in the following example: - -```php -$buildInfo = $orchestrationService->getBuildInfo(); -/** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-build-info.php) ] +https://doc.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index dbaa53ba9..0e338956c 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -1,123 +1,5 @@ ## 1. Introduction -A __Claim__ is the process of a worker checking out a message to perform a task. Claiming a message prevents other -workers from attempting to process the same messages. +Our docs have moved! Please visit the below link: -## 2. Setup - -A Claim is initialized on its parent object, a Queue: - -```php -// To initialize an empty object: -$claim = $queue->getClaim(); - -// or retrieve a specific claim: -$claim = $queue->getClaim('51db7067821e727dc24df754'); -``` - -## 3. Claim messages - -### 3.1 Description - -This operation claims a set of messages (up to the value of the limit parameter) from oldest to newest and skips any -messages that are already claimed. If no unclaimed messages are available, the API returns a `204 No Content` message. - -When a client (worker) finishes processing a message, it should delete the message before the claim expires to ensure -that the message is processed only once. As part of the delete operation, workers should specify the claim ID (which is -best done by simply using the provided href). If workers perform these actions, then if a claim simply expires, the -server can return an error and notify the worker of the race condition. This action gives the worker a chance to roll -back its own processing of the given message because another worker can claim the message and process it. - -The age given for a claim is relative to the server's clock. The claim's age is useful for determining how quickly -messages are getting processed and whether a given message's claim is about to expire. - -When a claim expires, it is released. If the original worker failed to process the message, another client worker can -then claim the message. - -### 3.2 Attributes - -The `ttl` attribute specifies how long the server waits before releasing the claim. The ttl value must be between 60 and -43200 seconds (12 hours). You must include a value for this attribute in your request. - -The `grace` attribute specifies the message grace period in seconds. The value of grace value must be between 60 and -43200 seconds (12 hours). You must include a value for this attribute in your request. To deal with workers that have -stopped responding (for up to 1209600 seconds or 14 days, including claim lifetime), the server extends the lifetime of -claimed messages to be at least as long as the lifetime of the claim itself, plus the specified grace period. If a -claimed message would normally live longer than the grace period, its expiration is not adjusted. - -The `limit` attribute specifies the number of messages to return, up to 20 messages. If limit is not specified, limit - defaults to 10. The limit parameter is optional. - -### 3.3 Code - -```php -use OpenCloud\Common\Constants\Datetime; - -$queue->claimMessages(array( - 'limit' => 15, - 'grace' => 5 * Datetime::MINUTE, - 'ttl' => 5 * Datetime::MINUTE -)); -``` - -## 4. Query claim - -### 4.1 Description - -This operation queries the specified claim for the specified queue. Claims with malformed IDs or claims that are not -found by ID are ignored. - -### 4.2 Attributes - -Claim ID. - -### 4.3 Code - -```php -$claim = $queue->getClaim('51db7067821e727dc24df754'); -``` - -## 5. Update claim - -### 5.1 Description - -This operation updates the specified claim for the specified queue. Claims with malformed IDs or claims that are not -found by ID are ignored. - -Clients should periodically renew claims during long-running batches of work to avoid losing a claim while processing a -message. The client can renew a claim by executing this method on a specific __Claim__ and including a new TTL. The API - will then reset the age of the claim and apply the new TTL. - -### 5.2 Attributes - -See section 4.2. - -### 5.3 Code - -```php -use OpenCloud\Common\Constants\Datetime; - -$claim->update(array( - 'ttl' => 10 * Datetime::MINUTE -)); -``` - -## 6. Release claim - -### 6.1 Description - -This operation immediately releases a claim, making any remaining undeleted messages that are associated with the -claim available to other workers. Claims with malformed IDs or claims that are not found by ID are ignored. - -This operation is useful when a worker is performing a graceful shutdown, fails to process one or more messages, or is -taking longer than expected to process messages, and wants to make the remainder of the messages available to other workers. - -### 6.2 Attributes - -See section 4.2. - -### 6.3 Code - -```php -$message->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/queues/claims.html diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md index d27f2fbbf..ce0f54b07 100644 --- a/docs/userguide/Queues/Message.md +++ b/docs/userguide/Queues/Message.md @@ -1,207 +1,5 @@ ## 1. Introduction -A __Message__ is a task, a notification, or any meaningful data that a producer or publisher sends to the queue. A -message exists until it is deleted by a recipient or automatically by the system based on a TTL (time-to-live) value. +Our docs have moved! Please visit the below link: -## 2. Setup - -A message is initialized from its parent object, a Queue: - -```php -// Setup an empty object -$message = $queue->getMessage(); - -// or retrieve an existing one -$message = $queue->getMessage(''); -``` - -## 3. Post message - -### 3.1 Description - -This operation posts the specified message or messages. You can submit up to 10 messages in a single request. - -When posting new messages, you specify only the `body` and `ttl` for the message. The API will insert metadata, such as -ID and age. - -### 3.2 Parameters - -How you pass through the array structure depends on whether you are executing multiple (3.3.2) or single (3.3.3) posts, -but the keys are the same. - -The `body` attribute specifies an arbitrary document that constitutes the body of the message being sent. The size of -this body is limited to 256 KB, excluding whitespace. - -The `ttl` attribute specifies how long the server waits before marking the message as expired and removing it from the -queue. The value of ttl must be between 60 and 1209600 seconds (14 days). Note that the server might not actually -delete the message until its age has reached up to (ttl + 60) seconds, to allow for flexibility in storage implementations. - -### 3.3 Code samples - -#### 3.3.1 Posting a single message - -``` -use OpenCloud\Common\Constants\Datetime; - -$queue->createMessage(array( - 'body' => (object) array( - 'event' => 'BackupStarted', - 'deadline' => '26.12.2013 - ), - 'ttl' => 2 * Datetime::DAY -)); -``` - -#### 3.3.2 Post a batch of messages - -Please note that the list of messages will be truncated at 10. For more, please execute another method call. - -```php -use OpenCloud\Common\Constants\Datetime; - -$messages = array( - array( - 'body' => (object) array( - 'play' => 'football' - ), - 'ttl' => 2 * Datetime::DAY - ), - array( - 'body' => (object) array( - 'play' => 'tennis' - ), - 'ttl' => 50 * Datetime::HOUR - ) -); - -$queue->createMessages($messages); -``` - -## 4. Get messages - -### 4.1 Description - -This operation gets the message or messages in the specified queue. - -Message IDs and markers are opaque strings. Clients should make no assumptions about their format or length. Furthermore, -clients should assume that there is no relationship between markers and message IDs (that is, one cannot be derived -from the other). This allows for a wide variety of storage driver implementations. - -Results are ordered by age, oldest message first. - -### 4.2 Parameters - -A hash of options. - -|Name|Style|Type|Description| -|----|----|----|----| -|marker|Query|String|Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional.| -|limit|Query|Integer|When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional.| -|echo|Query|Boolean|Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. -|include_claimed|Query|​Boolean|Determines whether the API returns claimed messages and unclaimed messages. The include_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include_claimed uses the default value of false (only unclaimed messages are returned). Optional. - -### 4.3 Code sample - -```php -$messages = $queue->listMessages(array( - 'marker' => '51db6f78c508f17ddc924357', - 'limit' => 20, - 'echo' => true -)); - -while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; -} -``` - -## 5. Get a set of messages by ID - -### 5.1 Description - -This operation provides a more efficient way to query multiple messages compared to using a series of individual GET. -Note that the list of IDs cannot exceed 20. If a malformed ID or a nonexistent message ID is provided, it is ignored, -and the remaining messages are returned. - -### 5.2 Parameters - -A hash of options. - -|Name|Style|Type|Description| -|----|-----|----|-----------| -|ids|Query|String|Specifies the IDs of the messages to get. Format multiple message ID values by separating them with -commas (comma-separated). Optional.| -|claim_id|Query|​String|Specifies the claim ID with which the message is associated. Optional.| -|----|-----|----|-----------| - -### 5.3 Code sample - -```php -$ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); - -$messages = $queue->listMessages(array('ids' => $ids)); - -while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; -} -``` - -## 6. Delete a set of messages by ID - -### 6.1 Description - -This operation immediately deletes the specified messages. If any of the message IDs are malformed or non-existent, -they are ignored. The remaining valid messages IDs are deleted. - -### 6.2 Parameters - -An array of IDs. - -### 6.3 Code sample - -```php -$ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); - -$response = $queue->deleteMessages($ids); -``` - -## 7. Get a specific message - -### 7.1 Description - -This operation gets the specified message from the specified queue. - -### 7.2 Parameters - -Message ID. - -### 7.3 Object properties - -`href` is an opaque relative URI that the client can use to uniquely identify a message resource and interact with it. - -`ttl` is the TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. - -`age` is the number of seconds relative to the server's clock. - -`body` is the arbitrary document that was submitted with the original request to post the message. - -### 7.4 Code sample - -```php -$message = $queue->getMessage('51db6f78c508f17ddc924357'); -``` - -## 8. Delete message - -### 8.1 Description - -This operation immediately deletes the specified message. - -### 8.2 Parameters - -None. - -### 8.3 Code sample - -```php -$message->delete(); -``` +https://doc.php-opencloud.com/en/latest/services/queues/messages.html diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md index 16290f445..01bb95e74 100644 --- a/docs/userguide/Queues/Queue.md +++ b/docs/userguide/Queues/Queue.md @@ -1,156 +1,5 @@ ## 1. Introduction -A Queue is an entity that holds messages. Ideally, a queue is created per work type. For example, if you want to -compress files, you would create a queue dedicated to this job. Any application that reads from this queue would only -compress files. +Our docs have moved! Please visit the below link: -## 2. Setup - -```php -$service = $client->queuesService('cloudQueues', 'ORD'); -``` - -## 3. Client IDs - -With most of Marconi's operation, you must specify a __Client ID__ which will be used as a unique identifier for the -process accessing this Queue. This is basically a UUID that must be unique to each client accessing the API - it can be -an arbitrary string. - -```php -$service->setClientId(); - -echo $service->getClientId(); -``` - -If you call `setClientId` without any parameters, a UUID is automatically generated for you. - -## 4. List queues - -### 4.1 Description - -This operation lists queues for the project. The queues are sorted alphabetically by name. - -### 4.2 Parameters - -|Name|Style|Type|Description| -|----|-----|----|-----------| -|marker|Query|​String|Specifies the name of the last queue received in a previous request, or none to get the first page - of results. Optional.| -|limit|Query|Integer|Specifies the number of queues to return. The default value for the number of queues returned is -10. If you do not specify this parameter, the default number of queues is returned. Optional.| -|detailed|Query|​Boolean|Determines whether queue metadata is included in the response. The default value for this - parameter is false, which excludes the metadata. Optional.| - |----|-----|----|-----------| - -### 4.3 Code sample - -```php -$queues = $service->listQueues(); - -while ($queue = $queues->next()) { - echo $queue->getName() . PHP_EOL; -} -``` - -## 5. Create queue - -### 5.1 Description - -This operation creates a new queue. - -### 5.2 Parameters - -A string representation of the name for your new Queue. The name must not exceed 64 bytes in length, and it is limited -to US-ASCII letters, digits, underscores, and hyphens. - -### 5.3 Code sample - -```php -$queue = $service->createQueue('new_queue'); -``` - -## 6. Retrieve queue - -### 6.1 Description - -Returns a `Queue` object for use. - -### 6.2 Parameters - -Queue name. - -### 6.3 Code sample - -```php -$queue = $service->getQueue('new_queue'); -``` - -## 7. Check queue existence - -### 7.1 Description - -This operation verifies whether the specified queue exists by returning `TRUE` or `FALSE`. - -### 7.2 Parameters - -### 7.3 Code sample - -```php -if ($service->hasQueue('new_queue')) { - // do something -} -``` - -## 8. Update queue metadata (permanently to the API) - -### 4.1 Description - -This operation replaces any existing metadata document in its entirety. Ensure that you do not accidentally overwrite -existing metadata that you want to retain. If you want to _append_ metadata, ensure you merge a new array to the -existing values. - -### 4.2 Parameters - -Hash of key pairs. - -### 4.3 Code sample - -```php -$queue->saveMetadata(array( - 'foo' => 'bar' -)); -``` - -## 9. Retrieve the queue metadata (fresh from the API) - -### 4.1 Description - -This operation returns metadata, such as message TTL, for the queue. - -### 4.2 Parameters - -None. - -### 4.3 Code sample - -```php -$metadata = $queue->retrieveMetadata(); - -print_r($metadata->toArray()); -``` - -## 10. Get queue stats - -### 4.1 Description - -This operation returns queue statistics, including how many messages are in the queue, categorized by status. - -### 4.2 Parameters - -None. - -### 4.3 Code sample - -```php -$queue->getStats(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/queues/queues.html diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 934951e24..0be239d94 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -1,74 +1,5 @@ # Table of contents -## Auto Scale -- [Groups](/docs/userguide/Autoscale/Groups.md) -- [Policies](/docs/userguide/Autoscale/Policies.md) -- [Webhooks](/docs/userguide/Autoscale/Webhooks.md) +Our docs have moved! Please visit the below link: -## Cloud Monitoring -- [Agents](/docs/userguide/CloudMonitoring/Agents.md) -- [Alarms](/docs/userguide/CloudMonitoring/Alarms.md) -- [Changelogs](/docs/userguide/CloudMonitoring/Changelogs.md) -- [Checks](/docs/userguide/CloudMonitoring/Checks.md) -- [Entities](/docs/userguide/CloudMonitoring/Entities.md) -- [Metrics](/docs/userguide/CloudMonitoring/Metrics.md) -- [Notifications](/docs/userguide/CloudMonitoring/Notifications.md) -- [Views](/docs/userguide/CloudMonitoring/Views.md) -- [Zones](/docs/userguide/CloudMonitoring/Zones.md) - -## Compute -- [Servers](/docs/userguide/Compute/Server.md) -- [Images](/docs/userguide/Compute/Images.md) -- [Flavors](/docs/userguide/flavors.md) -- [Keypairs](/docs/userguide/Compute/Keypairs.md) - -## DNS -- [Records](/docs/userguide/DNS/Records.md) -- [Domains](/docs/userguide/DNS/Domains.md) -- [Reverse DNS](/docs/userguide/DNS/Reverse-DNS.md) - -## Databases -- [Getting Started](/docs/userguide/Database/README.md) - -## Identity -- [Users](/docs/userguide/Identity/Users.md) -- [Roles](/docs/userguide/Identity/Roles.md) -- [Tokens](/docs/userguide/Identity/Tokens.md) -- [Tenants](/docs/userguide/Identity/Tenants.md) - -## Images -- [Images](/docs/userguide/Image/Images.md) -- [Sharing](/docs/userguide/Image/Sharing.md) -- [Tagging](/docs/userguide/Image/Tags.md) - -## Load Balancers -- [Getting Started](/docs/userguide/LoadBalancer/README.md) -- [Complete userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) - -## Networking -- [Getting Started](/docs/userguide/Networking/README.md) -- [Complete userguide](/docs/userguide/Networking/USERGUIDE.md) - -## Object Store -- [Objects](/docs/userguide/ObjectStore/Storage/Object.md) -- [Containers](/docs/userguide/ObjectStore/Storage/Container.md) -- [Migrating containers across regions](/docs/userguide/ObjectStore/Storage/Migrating.md) -- [Access control](/docs/userguide/ObjectStore/Access.md) -- [Accounts](/docs/userguide/ObjectStore/Account.md) -- [CDN-enabled containers](/docs/userguide/ObjectStore/CDN/Container.md) -- [Purging objects from the CDN](/docs/userguide/ObjectStore/CDN/Object.md) - -## Orchestration -- [Getting Started](/docs/userguide/Orchestration/README.md) -- [Complete userguide](/docs/userguide/Orchestration/USERGUIDE.md) - -## Queues -- [Queues](/docs/userguide/Queues/Queue.md) -- [Messages](/docs/userguide/Queues/Message.md) -- [Claims](/docs/userguide/Queues/Claim.md) - -## Miscelleneous -- [Client setup](/docs/userguide/Clients.md) -- [Debugging](/docs/userguide/Debugging.md) -- [Iterators](/docs/userguide/Iterators.md) -- [Caching credentials](/docs/userguide/caching-credentials.md) +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/Services.md b/docs/userguide/Services.md index 6f67222a2..9c9946e34 100644 --- a/docs/userguide/Services.md +++ b/docs/userguide/Services.md @@ -1,47 +1,5 @@ # Services -Both OpenStack and Rackspace are comprised of different services: Compute (Nova), -Object Storage (Swift), Queues (Marconi), etc. Each of these services is -encapsulated in its own class which implements the -`OpenCloud\Common\Service\ServiceInterface` interface. +Our docs have moved! Please visit the below link: -Each service class is nested in its own namespace: - -Service|OpenStack codename|FQCN ----|---|--- -Compute|Nova|`OpenCloud\Compute\Service` -Object Storage|Swift|`OpenCloud\ObjectStore\Service` -Identity|Keystone|`OpenCloud\Identity\Service` -Queues|Marconi|`OpenCloud\Queues\Service` - -## Instantiating a service - -The easiest way to instantiate a service, is using the convenient factory -methods of the client class. For more information about clients, see the -[client documentation](Clients.md). - -To create a Compute service, for example: - -```php -$service = $client->computeService('cloudServersOpenStack', 'DFW'); -``` - -### Method arguments - -As you will notice, each factory method accepts three arguments: - -Position|Name|Required?|Type|Description ----|---|---|---|--- -1|Service name|Yes|string|The name of the service as it appears in the [Service Catalog](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Clients.md#service-catalog) -2|Region|Yes|string|The region you want your service to operate in -3|URL type|No|enum|Each service has two different URL types for API transactions: a public URL (i.e. over the Internet) and a private URL (through Rackspace's private network). Private URL's are beneficial because they have lower latency and incur no bandwidth charges; the only condition is that you are communicating to the API from the **same geographic region** (i.e. from a Cloud Serve in the same region).

      The accepted options you may pass in are either `OpenCloud\Common\Constants\Service::INTERNAL_URL` or `OpenCloud\Common\Constants\Service::PUBLIC_URL`. - - -## Differences between OpenStack and Rackspace - -Not all of Rackspace's services are supported by OpenStack; examples include: -Cloud Databases, Auto Scale, Load Balancers, DNS, etc. - -For this reason, if you want to use or interact with these Rackspace-only -services, you must do so by using the methods of the `OpenCloud\Rackspace` -client object and **not** the `OpenCloud\OpenStack` client. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md index 60c89b606..5002ef8de 100644 --- a/docs/userguide/accessip.md +++ b/docs/userguide/accessip.md @@ -1,51 +1,6 @@ About the Access IP Addresses ============================= -OpenStack deployments generally provide new [servers](servers.md) with one -or two network interfaces, each with its own address(es). Usually, one of -these will be a public interface, with its addresses available on the Internet. +Our docs have moved! Please visit the below link: -However, in some cases, the servers are created on an internal network -(this is especially common in a hybrid solution where physical and virtual -servers are intermixed). The servers may be behind a NAT device, firewall, -or other network device that prohibits direct access to the server itself. - -The `OpenCloud\Compute\Resource\Server` object provides two attributes that are -used to instruct applications what IP address to use. These are called the -*access IP* addresses, and they are, in essence, documentation strings used to -direct applications to the correct network address. They can be changed at will -by the server's owner, and OpenStack Nova does not perform any validation on -them: - -* `accessIPv4` holds the IPv4 access address, and -* `accessIPv6` holds the IPv6 access address. - -### Updating the access IP address(es) - -For example, you may have a private cloud with internal addresses in the -10.1.x range. However, you can access a server via a firewall device at -address 50.57.94.244. In this case, you can change the `accessIPv4` attribute -to point to the firewall: - -```php -$server->update(array('accessIPv4' => '50.57.94.244')); -``` - -When a client application retrieves the server's information, it will know -that it needs to use the `accessIPv4` address to connect to the server, and -*not* the IP address assigned to one of the network interfaces. - -### Retrieving the server's IP address - -The `Server::ip()` method is used to retrieve the server's IP address. -It has one optional parameter: the format (either IPv4 or IPv6) of the address -to return (by default, it returns the IPv4 address): - -```php -// IPv4 -echo $server->ip(); -echo $server->ip(4); - -// IPv6 -echo $server->ip(6); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/caching-credentials.md b/docs/userguide/caching-credentials.md index d38d69937..365e6997e 100644 --- a/docs/userguide/caching-credentials.md +++ b/docs/userguide/caching-credentials.md @@ -1,39 +1,5 @@ # Caching credentials -You can speed up your API operations by caching your credentials in a (semi-)permanent location, such as your -DB or local filesystem. This enable subsequent requests to access a shared resource, instead of repetitively having to -re-authenticate on every thread of execution. +Our docs have moved! Please visit the below link: -Tokens are valid for 24 hours, so you can effectively re-use the same cached value for that period. If you try to use -a cached version that has expired, an authentication request will be made. - -## Filesystem example - -In this example, credentials will be saved to a file in the local filesystem. Be sure to exclude it from your VCS. - -```php -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' -)); - -$cacheFile = __DIR__ . '/.opencloud_token'; - -// If the cache file exists, try importing it into the client -if (file_exists($cacheFile)) { - $data = unserialize(file_get_contents($cacheFile)); - $client->importCredentials($data); -} - -$token = $client->getTokenObject(); - -// If no token exists, or the current token is expired, re-authenticate and save the new token to disk -if (!$token || ($token && $token->hasExpired())) { - $client->authenticate(); - file_put_contents($cacheFile, serialize($client->exportCredentials())); -} -``` - -In tests, the above code shaved about 1-2s off the execution time. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/caching-creds.html diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md index 8a8a05176..48d108014 100644 --- a/docs/userguide/dbaas.md +++ b/docs/userguide/dbaas.md @@ -1,319 +1,6 @@ Working with Cloud Databases ============================ -Cloud Databases is a "database-as-a-service" product offered by Rackspace. Since it is -not an official OpenStack project, it is only available via Rackspace connections, -and *not* through an OpenStack connection. +Our docs have moved! Please visit the below link: -## Setup - -```php -$service = $client->databaseService('cloudDatabases', 'ORD'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Instances - -A database instance is an isolated MySQL instance in a single tenant environment -on a shared physical host machine. Also referred to as instance. - -### Create a new Instance - -```php -// Create an empty OpenCloud\Database\Resource\Instance object -$instance = $service->instance(); - -// Send to the API -$instance->create(array( - // Pass in a name for your database instance - 'name' => '', - // Pass in a particular flavor object - 'flavor' => $service->flavor(''), - // Specify a 4GB volume - 'volume' => array('size' => 4) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/create-instance.php) ] - -### Retrieving an instance - -```php -$instance = $service->instance(''); -``` -[ [Get the executable PHP script for this example](/samples/Database/get-instance.php) ] - -### Updating an instance -An instance can be updated to use a specific [configuration](#configurations) as shown below. - -```php -$instance->update(array( - 'configuration' => '' -)); -``` - -**Note:** If any parameters in the associated configuration require a restart, then -you will need to [restart the instance](#restarting-an-instance) after the update. - -### Deleting an instance - -```php -$instance->delete(); -``` - -### Restarting an instance - -```php -$instance->restart(); -``` - -### Resizing an instance - -There are two methods for resizing an instance. The first, `resize()`, changes the amount -of RAM allocated to the instance: - -```php -$flavor = $service->flavor(''); -$instance->resize($flavor); -``` - -You can also independently change the volume size to increase the disk space: - -```php -// Increase to 8GB disk -$instance->resizeVolume(8); -``` - -## Databases - -Instances can have multiple databases; the `OpenCloud\Database\Resource\Database` -class corresponds to a single MySQL database. - -### Creating a new database - -To create a new database, you must supply it with a name; you can optionally -specify its character set and collating sequence: - -```php -// Create a new OpenCloud\Database\Resource\Database object -$database = $instance->database(); - -// Send to API -$database->create(array( - 'name' => 'production', - 'character_set' => 'utf8', - 'collate' => 'utf8_general_ci' -)); -``` - -You can find values for `character_set` and `collate` at -[the MySQL website](http://dev.mysql.com/doc/refman/5.0/en/charset-mysql.html). - -### Deleting a database - -```php -$database->delete(); -``` - -Note that this is destructive; all your data will be wiped away and will not be -retrievable. - -### Listing databases - -```php -$databases = $service->databaseList(); - -foreach ($databases as $database) { - /** @param $database OpenCloud\Database\Resource\Database */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Creating users - -Database users exist at the `Instance` level, but can be associated with a specific -`Database`. They are represented by the `OpenCloud\Database\Resource\User` class. - -Users cannot be altered after they are created, so they must be assigned to -databases when they are created: - -```php -// New instance of OpenCloud\Database\Resource\User -$user = $instance->user(); - -// Send to API -$user->create(array( - 'name' => 'Alice', - 'password' => 'fooBar' - 'databases' => array('production') -)); -``` - -If you need to add a new database to a user after it's been created, you must -delete the user and then re-add it. - -### Deleting users - -```php -$user->delete(); -``` - -## The root user - -By default, Cloud Databases does not enable the root user. In most cases, the root -user is not needed, and having one can leave you open to security violations. However, -if you want to enable access to the root user, the `enableRootUser()` method is -available: - -```php -$rootUser = $instance->enableRootUser(); -``` - -This returns a regular `User` object with the `name` attribute set to `root` and the -`password` attribute set to an auto-generated password. - -To check if the root user is enabled, use the `isRootEnabled()` method. - -## Configurations - -Configurations are groups of settings that can be [applied to instances](#updating-an-instance). - -### Creating a configuration -You can create a new configuration as shown below: - -```php -$configuration = $service->configuration(); -$configuration->create(array( - 'name' => 'example-configuration-name', - 'description' => 'An example configuration', - 'values' => array( - 'collation_server' => 'latin1_swedish_ci', - 'connect_timeout' => 120 - ), - 'datastore' => array( - 'type' => '10000000-0000-0000-0000-000000000001', - 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' - ) -)); -/** @var $configuration OpenCloud\Database\Resource\Configuration **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/create-configuration.php) ] - -### Listing configurations -You can list out all the configurations you have created as shown below: - -```php -$configurations = $service->configurationList(); -foreach ($configurations as $configuration) { - /** @var $configuration OpenCloud\Database\Resource\Configuration **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-configurations.php) ] - -### Retrieving a configuration -You can retrieve a specific configuration, using its ID, as shown below: - -```php -$configuration = $service->configuration(getenv('OS_DB_CONFIGURATION_ID')); -/** @var OpenCloud\Database\Resource\Configuration **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/get-configuration.php) ] - -### Updating a configuration -You have two choices when updating a configuration: -* You could [patch a configuration](#patching-a-configuration) to change only some configuration parameters, _or_ -* You could [entirely replace a configuration](#replacing-a-configuration) to replace all configuration parameters with new ones. - -#### Patching a configuration -You can patch a configuration as shown below: - -```php -$configuration->patch(array( - 'values' => array( - 'connect_timeout' => 30 - ) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/patch-configuration.php) ] - -#### Replacing a configuration -You can replace a configuration as shown below: - -```php -$configuration->update(array( - 'values' => array( - 'collation_server' => 'utf8_general_ci', - 'connect_timeout' => 60 - ) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/replace-configuration.php) ] - -### Deleting a configuration -You can delete a configuration as shown below: - -```php -$configuration->delete(); -``` -[ [Get the executable PHP script for this example](/samples/Database/delete-configuration.php) ] - -**Note:** You cannot delete a configuration if it is in use by a running instance. - -### Listing instances using a configuration -You can list all instances using a specific configuration, using its ID, as shown below: - -```php -$instances = $configuration->instanceList(); -foreach ($instances as $instance) { - /** @var $instance OpenCloud\Database\Resource\Instance **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-configuration-instances.php) ] - -## Datastores - -Datastores are technologies avaialable to persist data. - -### Listing datastores -You can list out all the datastores available as shown below: - -```php -$datastores = $service->datastoreList(); -foreach ($datastores as $datastore) { - /** @var $datastore OpenCloud\Database\Resource\Datastore **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-datastores.php) ] - -### Retrieving a datastore -You can retrieve a specific datastore's information, using its ID, as shown below: - -```php -$datastore = $service->datastore(''); -/** @var OpenCloud\Database\Resource\Datastore **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/get-datastore.php) ] - -### Listing datastore versions -You can list out all the versions available for a specific datastore, as shown below: - -```php -$versions = $datastore->versionList(); -foreach ($versions as $version) { - /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-datastore-versions.php) ] - -### Retrieving a datastore version -You a retrieve a specific datastore version, using its ID, as shown below: - -```php -$datastoreVersion = $datastore->version(''); -/** @var OpenCloud\Database\Resource\DatastoreVersion **/ -[ [Get the executable PHP script for this example](/samples/Database/get-datastore-version.php) ] +https://doc.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md index 41e3fa8d4..dd7806be5 100644 --- a/docs/userguide/flavors.md +++ b/docs/userguide/flavors.md @@ -1,60 +1,6 @@ Working with Flavors ==================== -A *flavor* is a named definition of certain server parameters such as the -amount of RAM and disk space available. (There are other parameters set via -the flavor, such as the amount of disk space and the number of virtual CPUs, -but a discussion of those is too in-depth for a simple Getting Started Guide -like this one.) +Our docs have moved! Please visit the below link: -## Get a flavor - -A `Flavor` object is generated from the `flavor()` method on an -`OpenCloud\Compute\Service` object: - -```php -$flavor = $service->flavor(''); -``` - -## List flavors - -```php -$flavors = $service->flavorList(); - -foreach ($flavors as $flavor) { - /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Details - -By default, the `flavorList()` method returns full details on all flavors. -However, because of the overhead involved in retrieving all the details, -this function can be slower than expected. You can supply an optional -boolean parameter to the `flavorList()` method to determine whether or not -the flavor details are included: - -```php -// Name and ID only -$compute->flavorList(false); - -// All details -$compute->flavorList(true); -``` - -### Filters - -The optional second parameter to the `flavorList()` method is an -associative array of filter parameters. See the -[List Flavors operation](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Flavors-d1e4188.html) - for a list of the available parameters. - -For example, you may be only interested in flavors that have at least 4GB of -memory: - -```php -$fourGbFlavors = $compute->FlavorList(true, array('minRam' => 4096)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/flavors.html diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md index f24752fc4..d42b73964 100644 --- a/docs/userguide/networks.md +++ b/docs/userguide/networks.md @@ -1,103 +1,6 @@ Working with Cloud Networks =========================== -Rackspace Cloud Networks is a virtual "isolated network" product -based upon (though not strictly identical to) the [OpenStack -Quantum](http://quantum.openstack.org) project. With Cloud Networks, -you can create multiple isolated networks and associate them with -new Cloud Servers. (You cannot add an isolated network to an -existing Cloud Server at this time, although that feature may be -available in the future.) +Our docs have moved! Please visit the below link: -Since the network is a feature of the Cloud Servers product, you -use the `Compute::network()` method to create a new network, and -the `Compute::networkList()` method to retrieve information about -existing networks. - -### Pseudo-networks - -Rackspace has two *pseudo-networks* called `public` and `private`. -The `public` network represents the Internet, while the `private` -network represents the Rackspace internal ServiceNet (an infrastructure -network used to facilitate communication within a Rackspace data -center). These are called "pseudo-networks" because they are not -actually represented in the Quantum component, but have a special -representation that you must use if you want to associate your -server with them. - -The `public` network is represented by the special UUID -`00000000-0000-0000-0000-000000000000` and the `private` network -by `11111111-1111-1111-1111-111111111111`. php-opencloud -provides the special constants `RAX_PUBLIC` and `RAX_PRIVATE` to -make using these networks easier (see [Create a server with an -isolated network](#server) below). - - -### Create a network - -A Cloud Network is identified by a *label* and must be defined with -a network *CIDR* address range. For example, we can create a network -used by our backend servers on the 192.168.0.0 network like this: - -```php -// Create instance of OpenCloud\Compute\Resource\Network -$network = $compute->network(); - -// Send to API -$network->create(array( - 'label' => 'Backend Network', - 'cidr' => '192.168.0.0/16' -)); -``` - -### Delete a network - -```php -$network->delete(); -``` - -Note that you cannot delete a network if there are still servers attached to it. - -### Listing networks - -``` -$networks = $service->networkList(); - -foreach ($networks as $network) { - /** @param $network OpenCloud\Compute\Resource\Network */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -## Create a server with an isolated network - -When you create a new server, you can specify the networks to which -it is attached via the `networks` attribute in the `Server::create()` -method: - -use OpenCloud\Compute\Constants\Network; - -// Create instance of OpenCloud\Compute\Resource\Server -$server = $compute->server(); - -// Send to API -$server->create(array( - 'name' => 'My Server', - 'flavor' => $compute->flavor(''), - 'image' => $compute->image(''), - 'networks' => array( - $network, - $compute->network(Network::RAX_PUBLIC) - ) -)); -``` - -In this example, the server `$server` is attached to the network that we -created in the previous example. It is also attached to the Rackspace `public` -network (the Internet). However, it is *not* attached to the Rackspace `private` -network (ServiceNet). - -Note that the `networks` attribute is an array of `OpenCloud\Compute\Resource\Network` -objects. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md index 8f58adea0..127a5b6c5 100644 --- a/docs/userguide/servers.md +++ b/docs/userguide/servers.md @@ -1,188 +1,6 @@ Working with Servers ==================== -A server is a virtual machine instance that is managed by OpenStack Nova. It is -represented by the `OpenCloud\Compute\Resource\Server` class. - -## Setup - -In order to use a `Server` object, you must create the Compute service first: - -```php -$service = $client->computeService('cloudServersOpenStack', 'ORD'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Get an existing server - -To retrieve an existing server, all you need is its unique ID: - -```php -/** @param $server OpenCloud\Compute\Resource\Server */ -$server = $server->server(''); -``` - -## Create a new server - -A server requires both a [Flavor object](flavors.md) and an -[Image object](images.md) to be created. In addition, a server requires a name: - -```php -// New instance of OpenCloud\Compute\Resource\Server -$server = $service->server(); - -/** @param $server OpenCloud\Image\Resource\ImageInterface */ -$image = $service->image(''); - -/** @param $server OpenCloud\Compute\Resource\Flavor */ -$flavor = $service->flavor(''); - -// Send to API -$server->create(array( - 'name' => 'New server', - 'flavor' => $flavor, - 'image' => $image -)); -``` - -Server builds typically take under 5 minutes to complete (depending upon the size -of the server). However, the initial response will return the server's ID as -well as the assigned root password: - -```php -echo $server->adminPass; -``` - -(Note: it is not recommended that you print out the root password because of -security risks. This is only provided as an example.) - -When you create a new server on the Rackspace public cloud, you can also -associate it with one or more isolated networks. For more information, see -[Working with Cloud Networks](networks.md). - -### Rebuilding an existing server - -"Rebuilding" a server is nearly identical to creating one; you must supply -an Image object. You can also change the server's name as part of the rebuild. -The primary difference between a create and a rebuild is that, in the rebuild, -the server's IP address(es) are retained (when the server is created, new IP -addresses are assigned). - -To rebuild a server: - -```php -$server->rebuild(array( - 'adminPass' => 'rootPassword', - 'name' => 'A Bigger Server', - 'image' => $compute->image('') -)); -``` - -### Updating a server - -The `update()` method is very similar to `create()` except that the only -attributes of a server that you are permitted to update are its name and -the [access IP addresses](accessip.md). - -```php -$server->update(array('accessIPv4' => '50.57.94.244')); -``` - -### Deleting a server - -```php -$server->delete(); -``` - -## Server actions - -You can perform various actions on a server, such as rebooting it, resizing -it, or changing the root password. - -### Setting the root password - -Use the `setPassword()` method to change the root user's password: - -```php -$server->setPassword('new password'); -``` - -Note that it may take a few second for the new password to take effect. Also, -password restrictions (such as the minimum number of characters, numbers of -punctuation characters, and so forth) are enforced by the operating system and are -not always detectable by the Compute service. This means that, even though -the `setPassword()` method succeeds, the password may not be changed, and -there may not be any feedback to that effect. - -### To reboot the server - -You can perform either a *hard* reboot (this is like pulling the power cord -and then restarting) or a *soft* reboot (initiated by the operating system -and generally less disruptive than a hard reboot). A hard reboot is -performed by default: - -```php -$server->reboot(); // hard reboot -$server->reboot(ServerState::REBOOT_STATE_HARD); // also a hard reboot -$server->reboot(ServerState::REBOOT_STATE_SOFT); // a soft reboot -``` - -If the server is "hung," or unresponsive, a hard reboot may sometimes be -the only way to access the server. - -### To resize the server - -A server can be resized by providing a new [Flavor object](flavors.md): - -```php -$server->resize($compute->flavor(5)); -``` - -Once the resize completes (check the `$server->status`), you can either -confirm it: - -```php -$server->resizeConfirm(); -``` - -or revert it back to the original size: - -```php -$server->resizeRevert(); -``` - -### To rescue/unrescue a server - -In rescue mode, a server is rebuilt to a pristine state and the existing -filesystem is mounted so that you can edit files and diagnose issues. -See [this document](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/rescue_mode.html) -for more details. - -Put server into rescue mode: - -```php -$password = $server->rescue(); -``` - -The `$password` is the assigned root password of the rescue server. - -Take server out of rescue mode: - -```php -$server->unrescue(); -``` - -This restores the server to its original state (plus any changes you may have -made while it was in rescue mode). - -## What next? - -* See also [Working with Networks](networks.md). -* To learn about dynamic - volume creation and assignment, see - [Working with Volumes](volumes.md). +Our docs have moved! Please visit the below link: +https://doc.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md index bd2f13a9d..8c891d4f2 100644 --- a/docs/userguide/volumes.md +++ b/docs/userguide/volumes.md @@ -1,173 +1,6 @@ Working with Volumes ---------------------------------------------- -Cloud Block Storage (CBS) is a dynamic volume creation and management service -built upon the OpenStack Cinder project. See http://cinder.openstack.org for -complete details about the available features and functionality. -## Setup +Our docs have moved! Please visit the below link: -```php -$service = $client->volumeService('cloudBlockStorage', 'DFW'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Volume Types - -Providers may support multiple types of volumes; at Rackspace, a volume can -either be `SSD` (solid state disk: expensive, high-performance) or -`SATA` (serial attached storage: regular disks, less expensive). - -### List volume types - -```php -$volumeTypes = $service->volumeTypeList(); - -foreach ($volumeTypes as $volumeType) { - /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Describe a volume type - -If you know the ID of a volume type, use the `volumeType` method to retrieve -information on it: - -```php -$volumeType = $service->volumeType(1); -``` - -A volume type has three attributes: - -* `$id` the volume type identifier -* `$name` its name -* `$extra_specs` additional information for the provider - -## Volumes - -A volume is a detachable block storage device. You can think of it as a USB -hard drive. It can only be attached to one instance at a time. - -A volume is represented by the `OpenCloud\Volume\Resource\Volume` class. - -### To create a volume - -To create a volume, you must specify its size (in gigabytes). All other -parameters are optional: - -```php -// Create instance of OpenCloud\Volume\Resource\Volume -$volume = $service->volume(); - -$volume->create(array( - 'size' => 200, - 'volume_type' => $service->volumeType(''), - 'display_name' => 'My Volume', - 'display_description' => 'Used for large object storage' -)); -``` - -### List volumes - -```php -$volumes = $service->volumeList(); - -foreach ($volumes as $volume) { - /** @param $volumeType OpenCloud\Volume\Resource\Volume */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Get details on a single volume - -If you specify an ID on the `volume()` method, it retrieves information on -the specified volume: - -```php -$volume = $dallas->volume(''); -echo $volume->size; -``` - -### To delete a volume - -```php -$volume->delete(); -``` - -## Snapshots - -A snapshot is a point-in-time copy of the data contained in a volume. It is -represented by the `OpenCloud\Volume\Resource\Snapshot` class. - -### Create a snapshot - -A `Snapshot` object is created from the Cloud Block Storage service. However, -it is associated with a volume, and you must specify a volume to create one: - -```php -// New instance of OpenCloud\Volume\Resource\Snapshot -$snapshot = $service->snapshot(); - -// Send to API -$snapshot->create(array( - 'display_name' => 'Name that snapshot', - 'volume_id' => $volume->id -)); -``` - -### List snapshots - -```php -$snapshots = $service->snapshotList(); - -foreach ($snapshots as $snapshot) { - /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### To get details on a single snapshot - -```php -$snapshot = $dallas->snapshot(''); -``` - -#### To delete a snapshot - -```php -$snapshot->delete(); -``` - -### Volumes and Servers - -A volume by itself is useful when it is attached to a server so that the -server can use the volume. - -### To attach a volume to a server - -```php -$server->attachVolume($volume, '') -``` - -The `` is the location on the server on which to -mount the volume (usually `/dev/xvhdd` or similar). You can also supply -`'auto'` as the mount point, in which case the mount point will be -automatically selected for you. `auto` is the default value for -`{mount-point}`, so you do not actually need to supply anything for that -parameter. - -### To detach a volume from a server - -```php -$server->detachVolume($volume); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/volume/index.html From 95ffc95e5f2bf8801435e5259485aed4eea69e45 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 11 Mar 2015 19:35:26 +0100 Subject: [PATCH 468/835] Add more information about configuring clients --- doc/auth.rst | 54 ++++++++++++++++++++++++++++++++++++++++++ doc/http-clients.rst | 56 ++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 3 +++ doc/logging.rst | 33 ++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 doc/auth.rst create mode 100644 doc/http-clients.rst create mode 100644 doc/logging.rst diff --git a/doc/auth.rst b/doc/auth.rst new file mode 100644 index 000000000..ce5ba7233 --- /dev/null +++ b/doc/auth.rst @@ -0,0 +1,54 @@ +Authentication +============== + +The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary. + +You can force authentication, by calling: + +.. code-block:: php + + $client->authenticate(); + +If the credentials are incorrect, a ``401`` error will be returned. If +credentials are correct, a ``200`` status is returned with your Service +Catalog. + + +Service Catalog +--------------- + +The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the ``Catalog`` +object, which allows you greater control and interactivity. + +.. code-block:: php + + /** @var OpenCloud\Common\Service\Catalog */ + $catalog = $client->getCatalog(); + + // Return a list of OpenCloud\Common\Service\CatalogItem objects + foreach ($catalog->getItems() as $catalogItem) { + + $name = $catalogItem->getName(); + $type = $catalogItem->getType(); + + if ($name == 'cloudServersOpenStack' && $type == 'compute') { + break; + } + + // Array of OpenCloud\Common\Service\Endpoint objects + $endpoints = $catalogItem->getEndpoints(); + foreach ($endpoints as $endpoint) { + if ($endpoint->getRegion() == 'DFW') { + echo $endpoint->getPublicUrl(); + } + } + } + +As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs. diff --git a/doc/http-clients.rst b/doc/http-clients.rst new file mode 100644 index 000000000..49828509b --- /dev/null +++ b/doc/http-clients.rst @@ -0,0 +1,56 @@ +HTTP Clients +============ + +Default HTTP headers +-------------------- + +To set default HTTP headers: + +.. code-block:: php + + $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); + + +User agents +----------- + +php-opencloud will send a default ``User-Agent`` header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format: + + User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz + +where ``xxx`` is the current version of the SDK, ``yyy`` is the current +version of cURL, and ``zzz`` is the current PHP version. To override +this default, you must run: + +.. code-block:: php + + $client->setUserAgent('MyCustomUserAgent'); + +which will result in: + + User-Agent: MyCustomUserAgent + +If you want to set a *prefix* for the user agent, but retain the default +``User-Agent`` as a suffix, you must run: + +.. code-block:: php + + $client->setUserAgent('MyPrefix', true); + +which will result in: + + User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + +where ``$client`` is an instance of ``OpenCloud\OpenStack`` or +``OpenCloud\Rackspace``. + + +Other functionality +------------------- + +For a full list of functionality provided by Guzzle, please consult the +`official documentation`_. + +.. _official documentation: http://docs.guzzlephp.org/en/latest/http-client/client.html diff --git a/doc/index.rst b/doc/index.rst index fc7c280c3..4994f0efb 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -50,6 +50,9 @@ Usage tips iterators regions url-types + logging + http-clients + auth Help and support ---------------- diff --git a/doc/logging.rst b/doc/logging.rst new file mode 100644 index 000000000..bb2a3de09 --- /dev/null +++ b/doc/logging.rst @@ -0,0 +1,33 @@ +Logging +======= + +Logger injection +---------------- + +As the ``Rackspace`` client extends the ``OpenStack`` client, they both support +passing ``$options`` as an array via the constructor's third parameter. The +options are passed as a config to the `Guzzle` client, but also allow to inject +your own logger. + +Your logger should implement the ``Psr\Log\LoggerInterface`` `as defined in +PSR-3 `_. +One example of a compatible logger is `Monolog `_. +When the client does create a service, it will inject the logger if one is +available. + +To inject a ``LoggerInterface`` compatible logger into a new client: + +.. code-block:: php + + use Monolog\Logger; + use OpenCloud\OpenStack; + + // create a log channel + $logger = new Logger('name'); + + $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' + ), array( + 'logger' => $logger, + )); From 975866eec02c86573c03a8d0616a5ed15dcb26af Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 13 Mar 2015 11:07:32 -0700 Subject: [PATCH 469/835] Bumping up patch version for release. --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index 061ac53d9..47aafabf1 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.12.1'; + const VERSION = '1.12.2'; /** * @return string Indicate current SDK version. From 6e699ce0ca3e59c19c78a9ec2e420b20674a512a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 16 Mar 2015 14:22:04 +0100 Subject: [PATCH 470/835] Use correct CNAME record --- docs/getting-started-openstack.md | 2 +- docs/getting-started.md | 2 +- docs/userguide/Autoscale/Config.md | 2 +- docs/userguide/Autoscale/Groups.md | 2 +- docs/userguide/Autoscale/Policies.md | 2 +- docs/userguide/Autoscale/Webhooks.md | 2 +- docs/userguide/Clients.md | 2 +- docs/userguide/CloudMonitoring/Agents.md | 2 +- docs/userguide/CloudMonitoring/Alarms.md | 2 +- docs/userguide/CloudMonitoring/Changelogs.md | 2 +- docs/userguide/CloudMonitoring/Checks.md | 2 +- docs/userguide/CloudMonitoring/Entities.md | 2 +- docs/userguide/CloudMonitoring/Metrics.md | 2 +- docs/userguide/CloudMonitoring/Notifications.md | 2 +- docs/userguide/CloudMonitoring/Service.md | 2 +- docs/userguide/CloudMonitoring/Views.md | 2 +- docs/userguide/CloudMonitoring/Zones.md | 2 +- docs/userguide/Compute/Images.md | 2 +- docs/userguide/Compute/Keypair.md | 2 +- docs/userguide/Compute/Server.md | 2 +- docs/userguide/Compute/Service.md | 2 +- docs/userguide/DNS/Domains.md | 2 +- docs/userguide/DNS/Limits.md | 2 +- docs/userguide/DNS/Records.md | 2 +- docs/userguide/DNS/Reverse-DNS.md | 2 +- docs/userguide/DNS/Service.md | 2 +- docs/userguide/Database/README.md | 2 +- docs/userguide/Debugging.md | 2 +- docs/userguide/Identity/Roles.md | 2 +- docs/userguide/Identity/Service.md | 2 +- docs/userguide/Identity/Tenants.md | 2 +- docs/userguide/Identity/Tokens.md | 2 +- docs/userguide/Identity/Users.md | 2 +- docs/userguide/Image/Images.md | 2 +- docs/userguide/Image/Schemas.md | 2 +- docs/userguide/Image/Sharing.md | 2 +- docs/userguide/Image/Tags.md | 2 +- docs/userguide/Iterators.md | 2 +- docs/userguide/LoadBalancer/README.md | 2 +- docs/userguide/LoadBalancer/USERGUIDE.md | 2 +- docs/userguide/Networking/README.md | 2 +- docs/userguide/Networking/USERGUIDE.md | 2 +- docs/userguide/ObjectStore/Access.md | 2 +- docs/userguide/ObjectStore/Account.md | 2 +- docs/userguide/ObjectStore/CDN/Container.md | 2 +- docs/userguide/ObjectStore/CDN/Object.md | 2 +- docs/userguide/ObjectStore/README.md | 2 +- docs/userguide/ObjectStore/Storage/Container.md | 2 +- docs/userguide/ObjectStore/Storage/Migrating.md | 2 +- docs/userguide/ObjectStore/Storage/Object.md | 2 +- docs/userguide/ObjectStore/USERGUIDE.md | 2 +- docs/userguide/Orchestration/README.md | 2 +- docs/userguide/Orchestration/USERGUIDE.md | 2 +- docs/userguide/Queues/Claim.md | 2 +- docs/userguide/Queues/Message.md | 2 +- docs/userguide/Queues/Queue.md | 2 +- docs/userguide/README.md | 2 +- docs/userguide/Services.md | 2 +- docs/userguide/accessip.md | 2 +- docs/userguide/caching-credentials.md | 2 +- docs/userguide/dbaas.md | 2 +- docs/userguide/flavors.md | 2 +- docs/userguide/networks.md | 2 +- docs/userguide/servers.md | 2 +- docs/userguide/volumes.md | 2 +- 65 files changed, 65 insertions(+), 65 deletions(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index 24eda2779..1133b948d 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/getting-started-with-openstack.html +http://docs.php-opencloud.com/en/latest/getting-started-with-openstack.html diff --git a/docs/getting-started.md b/docs/getting-started.md index af65271fe..c7d731a4c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/getting-started-with-rackspace.html +http://docs.php-opencloud.com/en/latest/getting-started-with-rackspace.html diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md index 02f2e891d..328fdcc93 100644 --- a/docs/userguide/Autoscale/Config.md +++ b/docs/userguide/Autoscale/Config.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/group-config.html +http://docs.php-opencloud.com/en/latest/services/autoscale/group-config.html diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md index 7ec7aa269..e0d8bfcdb 100644 --- a/docs/userguide/Autoscale/Groups.md +++ b/docs/userguide/Autoscale/Groups.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/groups.html +http://docs.php-opencloud.com/en/latest/services/autoscale/groups.html diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md index 7a312af50..1f890d7ad 100644 --- a/docs/userguide/Autoscale/Policies.md +++ b/docs/userguide/Autoscale/Policies.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/policies.html +http://docs.php-opencloud.com/en/latest/services/autoscale/policies.html diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md index c5e6453ed..783075508 100644 --- a/docs/userguide/Autoscale/Webhooks.md +++ b/docs/userguide/Autoscale/Webhooks.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/webhooks.html +http://docs.php-opencloud.com/en/latest/services/autoscale/webhooks.html diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 184b601f4..762a7ed70 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md index b316d004c..f1b8cab5d 100644 --- a/docs/userguide/CloudMonitoring/Agents.md +++ b/docs/userguide/CloudMonitoring/Agents.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/agents.html +http://docs.php-opencloud.com/en/latest/services/monitoring/agents.html diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md index b08caf4cd..b64c1e948 100644 --- a/docs/userguide/CloudMonitoring/Alarms.md +++ b/docs/userguide/CloudMonitoring/Alarms.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/alarms.html +http://docs.php-opencloud.com/en/latest/services/monitoring/alarms.html diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md index ff943e592..a1d137c9f 100644 --- a/docs/userguide/CloudMonitoring/Changelogs.md +++ b/docs/userguide/CloudMonitoring/Changelogs.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/changelogs.html +http://docs.php-opencloud.com/en/latest/services/monitoring/changelogs.html diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md index 5710aa465..37aefb644 100644 --- a/docs/userguide/CloudMonitoring/Checks.md +++ b/docs/userguide/CloudMonitoring/Checks.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/checks.html +http://docs.php-opencloud.com/en/latest/services/monitoring/checks.html diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md index beed807a2..0f09a74a2 100644 --- a/docs/userguide/CloudMonitoring/Entities.md +++ b/docs/userguide/CloudMonitoring/Entities.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/entities.html +http://docs.php-opencloud.com/en/latest/services/monitoring/entities.html diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md index 0b275c97e..86450bee9 100644 --- a/docs/userguide/CloudMonitoring/Metrics.md +++ b/docs/userguide/CloudMonitoring/Metrics.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/metrics.html +http://docs.php-opencloud.com/en/latest/services/monitoring/metrics.html diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md index 129881fae..30a1cf97c 100644 --- a/docs/userguide/CloudMonitoring/Notifications.md +++ b/docs/userguide/CloudMonitoring/Notifications.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/notifications.html +http://docs.php-opencloud.com/en/latest/services/monitoring/notifications.html diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md index 6445de039..be9f9435f 100644 --- a/docs/userguide/CloudMonitoring/Service.md +++ b/docs/userguide/CloudMonitoring/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/index.html +http://docs.php-opencloud.com/en/latest/services/monitoring/index.html diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md index ac0017db0..7672c2ea8 100644 --- a/docs/userguide/CloudMonitoring/Views.md +++ b/docs/userguide/CloudMonitoring/Views.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/views.html +http://docs.php-opencloud.com/en/latest/services/monitoring/views.html diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md index fcad4b900..8257efa29 100644 --- a/docs/userguide/CloudMonitoring/Zones.md +++ b/docs/userguide/CloudMonitoring/Zones.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/zones.html +http://docs.php-opencloud.com/en/latest/services/monitoring/zones.html diff --git a/docs/userguide/Compute/Images.md b/docs/userguide/Compute/Images.md index 6cc06d37f..b7a99025d 100644 --- a/docs/userguide/Compute/Images.md +++ b/docs/userguide/Compute/Images.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/images.html +http://docs.php-opencloud.com/en/latest/services/compute/images.html diff --git a/docs/userguide/Compute/Keypair.md b/docs/userguide/Compute/Keypair.md index 37e5080fe..a3e0b4870 100644 --- a/docs/userguide/Compute/Keypair.md +++ b/docs/userguide/Compute/Keypair.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/keypairs.html +http://docs.php-opencloud.com/en/latest/services/compute/keypairs.html diff --git a/docs/userguide/Compute/Server.md b/docs/userguide/Compute/Server.md index e43e1cc42..dedc1da77 100644 --- a/docs/userguide/Compute/Server.md +++ b/docs/userguide/Compute/Server.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/servers.html +http://docs.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/Compute/Service.md b/docs/userguide/Compute/Service.md index ba49ff4e4..9bb8e996a 100644 --- a/docs/userguide/Compute/Service.md +++ b/docs/userguide/Compute/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/index.html +http://docs.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index d8d3a12d3..53aa45c3b 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/domains.html +http://docs.php-opencloud.com/en/latest/services/dns/domains.html diff --git a/docs/userguide/DNS/Limits.md b/docs/userguide/DNS/Limits.md index 70fac6504..68e928594 100644 --- a/docs/userguide/DNS/Limits.md +++ b/docs/userguide/DNS/Limits.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/limits.html +http://docs.php-opencloud.com/en/latest/services/dns/limits.html diff --git a/docs/userguide/DNS/Records.md b/docs/userguide/DNS/Records.md index 52998b955..fb428c60e 100644 --- a/docs/userguide/DNS/Records.md +++ b/docs/userguide/DNS/Records.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/records.html +http://docs.php-opencloud.com/en/latest/services/dns/records.html diff --git a/docs/userguide/DNS/Reverse-DNS.md b/docs/userguide/DNS/Reverse-DNS.md index e930431c1..84a3a2d15 100644 --- a/docs/userguide/DNS/Reverse-DNS.md +++ b/docs/userguide/DNS/Reverse-DNS.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/reverse-dns.html +http://docs.php-opencloud.com/en/latest/services/dns/reverse-dns.html diff --git a/docs/userguide/DNS/Service.md b/docs/userguide/DNS/Service.md index 3404ed7c7..993648cd9 100644 --- a/docs/userguide/DNS/Service.md +++ b/docs/userguide/DNS/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/index.html +http://docs.php-opencloud.com/en/latest/services/dns/index.html diff --git a/docs/userguide/Database/README.md b/docs/userguide/Database/README.md index 199d049e4..2fc712efd 100644 --- a/docs/userguide/Database/README.md +++ b/docs/userguide/Database/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/database/index.html +http://docs.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/Debugging.md b/docs/userguide/Debugging.md index 0c6afa4d6..8922df10d 100644 --- a/docs/userguide/Debugging.md +++ b/docs/userguide/Debugging.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/debugging.html +http://docs.php-opencloud.com/en/latest/debugging.html diff --git a/docs/userguide/Identity/Roles.md b/docs/userguide/Identity/Roles.md index 333d53c81..5d608bf51 100644 --- a/docs/userguide/Identity/Roles.md +++ b/docs/userguide/Identity/Roles.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/roles.html +http://docs.php-opencloud.com/en/latest/services/identity/roles.html diff --git a/docs/userguide/Identity/Service.md b/docs/userguide/Identity/Service.md index 579cf2a9f..4195d4bcb 100644 --- a/docs/userguide/Identity/Service.md +++ b/docs/userguide/Identity/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/index.html +http://docs.php-opencloud.com/en/latest/services/identity/index.html diff --git a/docs/userguide/Identity/Tenants.md b/docs/userguide/Identity/Tenants.md index ae9bcdcb0..2129f9604 100644 --- a/docs/userguide/Identity/Tenants.md +++ b/docs/userguide/Identity/Tenants.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/tenants.html +http://docs.php-opencloud.com/en/latest/services/identity/tenants.html diff --git a/docs/userguide/Identity/Tokens.md b/docs/userguide/Identity/Tokens.md index a60c57ab5..f59835ef4 100644 --- a/docs/userguide/Identity/Tokens.md +++ b/docs/userguide/Identity/Tokens.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/tokens.html +http://docs.php-opencloud.com/en/latest/services/identity/tokens.html diff --git a/docs/userguide/Identity/Users.md b/docs/userguide/Identity/Users.md index e387cbee5..a69fbb7dc 100644 --- a/docs/userguide/Identity/Users.md +++ b/docs/userguide/Identity/Users.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/users.html +http://docs.php-opencloud.com/en/latest/services/identity/users.html diff --git a/docs/userguide/Image/Images.md b/docs/userguide/Image/Images.md index 1f593f17a..95824b2a5 100644 --- a/docs/userguide/Image/Images.md +++ b/docs/userguide/Image/Images.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/images.html +http://docs.php-opencloud.com/en/latest/services/image/images.html diff --git a/docs/userguide/Image/Schemas.md b/docs/userguide/Image/Schemas.md index b37222b84..5efce8c59 100644 --- a/docs/userguide/Image/Schemas.md +++ b/docs/userguide/Image/Schemas.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/schemas.html +http://docs.php-opencloud.com/en/latest/services/image/schemas.html diff --git a/docs/userguide/Image/Sharing.md b/docs/userguide/Image/Sharing.md index 91515b9fa..3fbe0c1d1 100644 --- a/docs/userguide/Image/Sharing.md +++ b/docs/userguide/Image/Sharing.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/sharing.html +http://docs.php-opencloud.com/en/latest/services/image/sharing.html diff --git a/docs/userguide/Image/Tags.md b/docs/userguide/Image/Tags.md index fc8bcd8aa..2686391f4 100644 --- a/docs/userguide/Image/Tags.md +++ b/docs/userguide/Image/Tags.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/tags.html +http://docs.php-opencloud.com/en/latest/services/image/tags.html diff --git a/docs/userguide/Iterators.md b/docs/userguide/Iterators.md index 114982668..4c8c794ce 100644 --- a/docs/userguide/Iterators.md +++ b/docs/userguide/Iterators.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/iterators.html +http://docs.php-opencloud.com/en/latest/iterators.html diff --git a/docs/userguide/LoadBalancer/README.md b/docs/userguide/LoadBalancer/README.md index 34008ab6c..3531e3dc8 100644 --- a/docs/userguide/LoadBalancer/README.md +++ b/docs/userguide/LoadBalancer/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html +http://docs.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 0a5c3b15d..d29925da0 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html +http://docs.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md index 1778646ad..9597c33a1 100644 --- a/docs/userguide/Networking/README.md +++ b/docs/userguide/Networking/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 376daa093..d3185c6f2 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md index 8e224392a..53466362e 100644 --- a/docs/userguide/ObjectStore/Access.md +++ b/docs/userguide/ObjectStore/Access.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/access.html +http://docs.php-opencloud.com/en/latest/services/object-store/access.html diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md index fc60751a8..286bb9ca1 100644 --- a/docs/userguide/ObjectStore/Account.md +++ b/docs/userguide/ObjectStore/Account.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/account.html +http://docs.php-opencloud.com/en/latest/services/object-store/account.html diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 9f3b4a5ca..c854b31e5 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html +http://docs.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md index 9f3b4a5ca..c854b31e5 100644 --- a/docs/userguide/ObjectStore/CDN/Object.md +++ b/docs/userguide/ObjectStore/CDN/Object.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html +http://docs.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index ea43fcdf7..c66bbabdb 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/index.html +http://docs.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index e9fd5af14..ab705fa15 100644 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/containers.html +http://docs.php-opencloud.com/en/latest/services/object-store/containers.html diff --git a/docs/userguide/ObjectStore/Storage/Migrating.md b/docs/userguide/ObjectStore/Storage/Migrating.md index 8d12c382a..550268f60 100644 --- a/docs/userguide/ObjectStore/Storage/Migrating.md +++ b/docs/userguide/ObjectStore/Storage/Migrating.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/migrating-containers.html +http://docs.php-opencloud.com/en/latest/services/object-store/migrating-containers.html diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 0c58ca9fb..2ecca81e1 100644 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/objects.html +http://docs.php-opencloud.com/en/latest/services/object-store/objects.html diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 6881ee3e6..3773b0078 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/index.html +http://docs.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 862a61ed7..3647da0c9 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/orchestration/index.html +http://docs.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 902d7741b..4a5b9a135 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/orchestration/index.html +http://docs.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index 0e338956c..58b57d22f 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/claims.html +http://docs.php-opencloud.com/en/latest/services/queues/claims.html diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md index ce0f54b07..767bb0b1d 100644 --- a/docs/userguide/Queues/Message.md +++ b/docs/userguide/Queues/Message.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/messages.html +http://docs.php-opencloud.com/en/latest/services/queues/messages.html diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md index 01bb95e74..2299a72e2 100644 --- a/docs/userguide/Queues/Queue.md +++ b/docs/userguide/Queues/Queue.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/queues.html +http://docs.php-opencloud.com/en/latest/services/queues/queues.html diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 0be239d94..b49b4e67c 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/Services.md b/docs/userguide/Services.md index 9c9946e34..6973130eb 100644 --- a/docs/userguide/Services.md +++ b/docs/userguide/Services.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md index 5002ef8de..9d343de11 100644 --- a/docs/userguide/accessip.md +++ b/docs/userguide/accessip.md @@ -3,4 +3,4 @@ About the Access IP Addresses Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/servers.html +http://docs.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/caching-credentials.md b/docs/userguide/caching-credentials.md index 365e6997e..949dcad53 100644 --- a/docs/userguide/caching-credentials.md +++ b/docs/userguide/caching-credentials.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/caching-creds.html +http://docs.php-opencloud.com/en/latest/caching-creds.html diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md index 48d108014..fe0327861 100644 --- a/docs/userguide/dbaas.md +++ b/docs/userguide/dbaas.md @@ -3,4 +3,4 @@ Working with Cloud Databases Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/database/index.html +http://docs.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md index dd7806be5..4bf978b52 100644 --- a/docs/userguide/flavors.md +++ b/docs/userguide/flavors.md @@ -3,4 +3,4 @@ Working with Flavors Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/flavors.html +http://docs.php-opencloud.com/en/latest/services/compute/flavors.html diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md index d42b73964..f6a153508 100644 --- a/docs/userguide/networks.md +++ b/docs/userguide/networks.md @@ -3,4 +3,4 @@ Working with Cloud Networks Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md index 127a5b6c5..1ff28fd30 100644 --- a/docs/userguide/servers.md +++ b/docs/userguide/servers.md @@ -3,4 +3,4 @@ Working with Servers Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/index.html +http://docs.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md index 8c891d4f2..bc161e5b3 100644 --- a/docs/userguide/volumes.md +++ b/docs/userguide/volumes.md @@ -3,4 +3,4 @@ Working with Volumes Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/volume/index.html +http://docs.php-opencloud.com/en/latest/services/volume/index.html From 6e96119f1160c7a5457d19820a5d9e703bb3a059 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:21:50 -0700 Subject: [PATCH 471/835] Using TLSv1 cipher suite for Cloud Databases client. --- lib/OpenCloud/Database/Service.php | 22 +++++++++++++++++++ .../OpenCloud/Tests/Database/ServiceTest.php | 7 ++++++ 2 files changed, 29 insertions(+) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index dd63a60f1..50845ba24 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -17,6 +17,8 @@ namespace OpenCloud\Database; +use Guzzle\Http\ClientInterface; +use Psr\Log\LogLevel; use OpenCloud\Common\Service\NovaService; use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; @@ -104,4 +106,24 @@ public function datastoreList($params = array()) return $this->resourceList('Datastore', $url); } + + /** + * {@inheritDoc} + */ + public function setClient(ClientInterface $client) + { + // The Rackspace Cloud Databases service only supports the + // RC4 SSL cipher which is not supported by modern OpenSSL clients. + // Until the service can support additional, more modern and secure + // ciphers, this SDK has to ask curl to allow using the weaker + // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 + + $curlOptions = $client->getConfig()->get('curl.options'); + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; + $client->getConfig()->set('curl.options', $curlOptions); + + $client->getLogger()->critical('The SDK is using the TLSv1 cipher suite when connecting to the Rackspace Cloud Databases service. This suite contains a weak cipher (RC4) so please use at your own risk. See https://github.com/rackspace/php-opencloud/issues/560 for details.'); + + $this->client = $client; + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 8212fb61a..4c690ae81 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -71,4 +71,11 @@ public function testDatastoreList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } + + public function testClientUsesTLSv1CipherSuite() + { + $client = $this->service->getClient(); + $curlOptions = $client->getConfig('curl.options'); + $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + } } From 488a69d8aebdb0a9a2350f7b7919e9a70e0afef1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:40:04 -0700 Subject: [PATCH 472/835] Check that critical message is being logged + suppress log output. --- .../Tests/Database/DatabaseTestCase.php | 25 ++++++++++++++++++- .../OpenCloud/Tests/Database/ServiceTest.php | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index 0e8ad75cb..c0a0b38e0 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -18,6 +18,22 @@ namespace OpenCloud\Tests\Database; use OpenCloud\Tests\OpenCloudTestCase; +use OpenCloud\Common\Log\Logger; + +class UnitTestLogger extends Logger +{ + protected $criticalLogMessage; + + public function critical($message, array $context = array()) + { + ++$this->criticalLogMessage; + } + + public function getCriticalLogMessage() + { + return $this->criticalLogMessage; + } +} class DatabaseTestCase extends OpenCloudTestCase { @@ -28,7 +44,9 @@ class DatabaseTestCase extends OpenCloudTestCase public function setupObjects() { - $this->service = $this->getClient()->databaseService(); + $client = $this->getClient(); + $client->setLogger(new UnitTestLogger()); + $this->service = $client->databaseService(); $this->addMockSubscriber($this->getTestFilePath('Instance')); $this->instance = $this->service->instance('foo'); @@ -37,4 +55,9 @@ public function setupObjects() $this->datastore = $this->service->datastore('10000000-0000-0000-0000-000000000001'); $this->datastoreVersion = $this->datastore->version('b00000b0-00b0-0b00-00b0-000b000000bb'); } + + protected function assertCriticalMessageWasLogged() + { + $this->assertNotEmpty($this->getClient()->getLogger()->getCriticalLogMessage()); + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 4c690ae81..3dc5a11f1 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -77,5 +77,6 @@ public function testClientUsesTLSv1CipherSuite() $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertCriticalMessageWasLogged(); } } From e7f8dc4be23fc568f13c5e02bb518ac1b7992aa4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:49:39 -0700 Subject: [PATCH 473/835] Removing unnecessary import. --- lib/OpenCloud/Database/Service.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 50845ba24..2e9d56093 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -18,7 +18,6 @@ namespace OpenCloud\Database; use Guzzle\Http\ClientInterface; -use Psr\Log\LogLevel; use OpenCloud\Common\Service\NovaService; use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; From 2295b6b111905cd0a2bfc2ef190eda0f730d3ecf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:49:46 -0700 Subject: [PATCH 474/835] Breaking up log message over several lines. --- lib/OpenCloud/Database/Service.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 2e9d56093..00498ed28 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -121,7 +121,11 @@ public function setClient(ClientInterface $client) $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; $client->getConfig()->set('curl.options', $curlOptions); - $client->getLogger()->critical('The SDK is using the TLSv1 cipher suite when connecting to the Rackspace Cloud Databases service. This suite contains a weak cipher (RC4) so please use at your own risk. See https://github.com/rackspace/php-opencloud/issues/560 for details.'); + $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' + . 'to the Rackspace Cloud Databases service. This suite contains ' + . 'a weak cipher (RC4) so please use at your own risk. See ' + . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; + $client->getLogger()->critical($logMessage); $this->client = $client; } From 8bccc6be0ea8807d0348c2a6a65695ea6dff1013 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:54:37 -0700 Subject: [PATCH 475/835] Fixing option value - should be cipher name not integer (constant). --- lib/OpenCloud/Database/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 00498ed28..b73ee2f7c 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -118,7 +118,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'TLSv1'; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' From e07ff1598a465f72a4ccb8f34f0d712dbb0a3123 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 09:50:56 -0700 Subject: [PATCH 476/835] Use custom cipher suite stronger than TLSv1 (but still including RC4 for Cloud Databases). --- lib/OpenCloud/Database/Service.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index b73ee2f7c..065191095 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -118,7 +118,10 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'TLSv1'; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' From 78f36450367307fef800d640f4cf5fa1c6ddad98 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:08:35 -0700 Subject: [PATCH 477/835] Refactoring SSL cipher list into constant; fixing test. --- lib/OpenCloud/Database/Service.php | 10 ++++++---- tests/OpenCloud/Tests/Database/ServiceTest.php | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 065191095..0828d47f1 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -31,6 +31,11 @@ class Service extends NovaService const DEFAULT_TYPE = 'rax:database'; const DEFAULT_NAME = 'cloudDatabases'; + const SSL_CIPHER_LIST = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + /** * Returns an Instance * @@ -118,10 +123,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = self::SSL_CIPHER_LIST; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 3dc5a11f1..28636a363 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -27,6 +27,8 @@ namespace OpenCloud\Tests\Database; +use OpenCloud\Database\Service; + class ServiceTest extends DatabaseTestCase { public function test__construct() @@ -76,7 +78,7 @@ public function testClientUsesTLSv1CipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertEquals(Service::SSL_CIPHER_LIST, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); $this->assertCriticalMessageWasLogged(); } } From 64e643070da51a2e40507ed1477e97b1b5ba15bf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:16:20 -0700 Subject: [PATCH 478/835] PHP < 5.6 does not like multi-line consts :| --- lib/OpenCloud/Database/Service.php | 15 +++++++++------ tests/OpenCloud/Tests/Database/ServiceTest.php | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 0828d47f1..1098e6add 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -31,11 +31,6 @@ class Service extends NovaService const DEFAULT_TYPE = 'rax:database'; const DEFAULT_NAME = 'cloudDatabases'; - const SSL_CIPHER_LIST = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; - /** * Returns an Instance * @@ -123,7 +118,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = self::SSL_CIPHER_LIST; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' @@ -134,4 +129,12 @@ public function setClient(ClientInterface $client) $this->client = $client; } + + public static function getSslCipherList() + { + return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 28636a363..a3f5f4401 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -78,7 +78,7 @@ public function testClientUsesTLSv1CipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(Service::SSL_CIPHER_LIST, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertEquals(Service::getSslCipherList(), $curlOptions['CURLOPT_SSL_CIPHER_LIST']); $this->assertCriticalMessageWasLogged(); } } From d4ff6f4c9dcee0d0b1051d637f1088ee181e0c68 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:18:45 -0700 Subject: [PATCH 479/835] Fixing log message to match reality. --- lib/OpenCloud/Database/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 1098e6add..58c5ba260 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -121,7 +121,7 @@ public function setClient(ClientInterface $client) $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); $client->getConfig()->set('curl.options', $curlOptions); - $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' + $logMessage = 'The SDK is using a custom cipher suite when connecting ' . 'to the Rackspace Cloud Databases service. This suite contains ' . 'a weak cipher (RC4) so please use at your own risk. See ' . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; From f7eecae377c17423c8bb790bfb92d0631eddedda Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:20:01 -0700 Subject: [PATCH 480/835] Fixing unit test method name to match reality. --- tests/OpenCloud/Tests/Database/ServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index a3f5f4401..96a6be1c6 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -74,7 +74,7 @@ public function testDatastoreList() $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } - public function testClientUsesTLSv1CipherSuite() + public function testClientUsesCustomCipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); From b9279e3fd5d97cb60981528866f2f03ab1906329 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:29:50 -0700 Subject: [PATCH 481/835] Refactoring logger into common class to enable reuse. --- .../Tests/Database/DatabaseTestCase.php | 19 ++-------- tests/OpenCloud/Tests/MockLogger.php | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 tests/OpenCloud/Tests/MockLogger.php diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index c0a0b38e0..403f6955f 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -18,22 +18,7 @@ namespace OpenCloud\Tests\Database; use OpenCloud\Tests\OpenCloudTestCase; -use OpenCloud\Common\Log\Logger; - -class UnitTestLogger extends Logger -{ - protected $criticalLogMessage; - - public function critical($message, array $context = array()) - { - ++$this->criticalLogMessage; - } - - public function getCriticalLogMessage() - { - return $this->criticalLogMessage; - } -} +use OpenCloud\Tests\MockLogger; class DatabaseTestCase extends OpenCloudTestCase { @@ -45,7 +30,7 @@ class DatabaseTestCase extends OpenCloudTestCase public function setupObjects() { $client = $this->getClient(); - $client->setLogger(new UnitTestLogger()); + $client->setLogger(new MockLogger()); $this->service = $client->databaseService(); $this->addMockSubscriber($this->getTestFilePath('Instance')); diff --git a/tests/OpenCloud/Tests/MockLogger.php b/tests/OpenCloud/Tests/MockLogger.php new file mode 100644 index 000000000..9aca3dd68 --- /dev/null +++ b/tests/OpenCloud/Tests/MockLogger.php @@ -0,0 +1,35 @@ +criticalLogMessage; + } + + public function getCriticalLogMessage() + { + return $this->criticalLogMessage; + } +} From 6d133a314af596395e9721965eb9990ab45dde73 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:30:04 -0700 Subject: [PATCH 482/835] Use mock logger for database service test to supress log output. --- tests/OpenCloud/Tests/RackspaceTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/OpenCloud/Tests/RackspaceTest.php b/tests/OpenCloud/Tests/RackspaceTest.php index 790d54645..7cf37f183 100644 --- a/tests/OpenCloud/Tests/RackspaceTest.php +++ b/tests/OpenCloud/Tests/RackspaceTest.php @@ -17,6 +17,8 @@ namespace OpenCloud\Tests; +use OpenCloud\Tests\MockLogger; + class RackspaceTest extends OpenCloudTestCase { const CREDENTIALS = <<getClient()->getLogger(); + $this->getClient()->setLogger(new MockLogger()); + $this->assertInstanceOf( 'OpenCloud\Database\Service', $this->getClient()->databaseService('cloudDatabases', 'DFW') ); + + // Re-inject old logger + $this->getClient()->setLogger($oldLogger); + $this->assertInstanceOf( 'OpenCloud\LoadBalancer\Service', $this->getClient()->loadBalancerService('cloudLoadBalancers', 'DFW') From a1eb1e09778ab8e5e35a880304c88d87d97325ef Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 12:06:59 -0700 Subject: [PATCH 483/835] Adding reference to cipher list comment in docblock. --- lib/OpenCloud/Database/Service.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 58c5ba260..de20872b6 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -130,6 +130,9 @@ public function setClient(ClientInterface $client) $this->client = $client; } + /** + * @see https://github.com/rackspace/php-opencloud/issues/560#issuecomment-81790778 + */ public static function getSslCipherList() { return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' From f0fbce0ad750aa25147d6b38b9dab5185e644c64 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Wed, 18 Mar 2015 14:50:43 +0000 Subject: [PATCH 484/835] Added symlink functionality via X-Object-Manifest header management --- lib/OpenCloud/Common/Constants/Header.php | 1 + .../ObjectStore/Resource/DataObject.php | 75 +++++++++++++++++-- .../ObjectStore/Resource/DataObjectTest.php | 17 +++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Common/Constants/Header.php b/lib/OpenCloud/Common/Constants/Header.php index c0246f71f..5de04bc2a 100644 --- a/lib/OpenCloud/Common/Constants/Header.php +++ b/lib/OpenCloud/Common/Constants/Header.php @@ -70,4 +70,5 @@ class Header const USER_AGENT = 'User-Agent'; const VARY = 'Vary'; const VIA = 'Via'; + const X_OBJECT_MANIFEST = 'X-Object-Manifest'; } diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index 5877431b8..ca74e4d66 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -76,6 +76,11 @@ class DataObject extends AbstractResource * @var string Etag. */ protected $etag; + + /** + * @var string Manifest. Can be null so we use false to mean unset. + */ + protected $manifest = false; /** * Also need to set Container parent and handle pseudo-directories. @@ -139,7 +144,9 @@ public function populateFromResponse(Response $response) ->setContentType((string) $headers[HeaderConst::CONTENT_TYPE]) ->setLastModified((string) $headers[HeaderConst::LAST_MODIFIED]) ->setContentLength((string) $headers[HeaderConst::CONTENT_LENGTH]) - ->setEtag((string) $headers[HeaderConst::ETAG]); + ->setEtag((string) $headers[HeaderConst::ETAG]) + // do not cast to a string to allow for null (i.e. no header) + ->setManifest($headers[HeaderConst::X_OBJECT_MANIFEST]); } public function refresh() @@ -293,6 +300,26 @@ public function getEtag() { return $this->etag ? : $this->content->getContentMd5(); } + + /** + * @param $manifest + * @return $this + */ + public function setManifest($manifest) + { + $this->manifest = $manifest; + + return $this; + } + + /** + * @return null|string + */ + public function getManifest() + { + // only make a request if manifest has not been set (is false) + return $this->manifest !== false ? $this->manifest : $this->getManifestHeader(); + } public function setLastModified($lastModified) { @@ -327,10 +354,11 @@ public function update($params = array()) // merge specific properties with metadata $metadata += array( - HeaderConst::CONTENT_TYPE => $this->contentType, - HeaderConst::LAST_MODIFIED => $this->lastModified, - HeaderConst::CONTENT_LENGTH => $this->contentLength, - HeaderConst::ETAG => $this->etag + HeaderConst::CONTENT_TYPE => $this->contentType, + HeaderConst::LAST_MODIFIED => $this->lastModified, + HeaderConst::CONTENT_LENGTH => $this->contentLength, + HeaderConst::ETAG => $this->etag, + HeaderConst::X_OBJECT_MANIFEST => $this->manifest ); return $this->container->uploadObject($this->name, $this->content, $metadata); @@ -354,6 +382,26 @@ public function delete($params = array()) { return $this->getService()->getClient()->delete($this->getUrl())->send(); } + + /** + * @param string $source Path (`container/object') of other object to symlink this object to + * @return \Guzzle\Http\Message\Response + */ + public function symlink($source) + { + $response = $this->getService() + ->getClient() + ->createRequest('PUT', $this->getUrl(), array( + HeaderConst::X_OBJECT_MANIFEST => (string) $source + )) + ->send(); + + if ($response->getStatusCode() == 201) { + $this->setManifest($source); + } + + return $response; + } /** * Get a temporary URL for this object. @@ -449,4 +497,21 @@ protected static function headerIsValidMetadata($header) return preg_match($pattern, $header); } + + /** + * @return null|string + */ + protected function getManifestHeader() + { + $response = $this->getService() + ->getClient() + ->head($this->getUrl()) + ->send(); + + $manifest = $response->getHeader(HeaderConst::X_OBJECT_MANIFEST); + + $this->setManifest($manifest); + + return $manifest; + } } diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 8193c267d..fc67ad35d 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -101,4 +101,21 @@ public function test_Public_Urls() $this->assertNotNull($object->getPublicUrl(UrlType::STREAMING)); $this->assertNotNull($object->getPublicUrl(UrlType::IOS_STREAMING)); } + + public function test_Symlink() + { + $object = $this->container->dataObject('foobar'); + $this->assertInstanceOf( + 'Guzzle\Http\Message\Response', + $object->symlink('/new_container/new_object') + ); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\NoNameError + */ + public function test_Symlink_Fails() + { + $this->container->dataObject()->symlink(null); + } } From 6ce77ce5adbdad7bfff439322c3400795ab13cfa Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Thu, 19 Mar 2015 18:28:25 +0000 Subject: [PATCH 485/835] Added createSymlinkFrom. Renamed symlink to createSymlinkTo. Made setManifest protected. Updated PHPDoc and tests. --- .../ObjectStore/Resource/DataObject.php | 69 ++++++++++++++----- .../ObjectStore/Resource/DataObjectTest.php | 34 ++++++--- 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index ca74e4d66..ace797bd3 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -302,10 +302,10 @@ public function getEtag() } /** - * @param $manifest + * @param string $manifest Path (`container/object') to set as the value to X-Object-Manifest * @return $this */ - public function setManifest($manifest) + protected function setManifest($manifest) { $this->manifest = $manifest; @@ -313,7 +313,7 @@ public function setManifest($manifest) } /** - * @return null|string + * @return null|string Path (`container/object') from X-Object-Manifest header or null if the header does not exist */ public function getManifest() { @@ -384,23 +384,60 @@ public function delete($params = array()) } /** - * @param string $source Path (`container/object') of other object to symlink this object to - * @return \Guzzle\Http\Message\Response + * @param string $destination Path (`container/object') of other object to symlink this object to + * @return null|\Guzzle\Http\Message\Response The response or null if $this is not empty */ - public function symlink($source) + public function createSymlinkTo($destination) { - $response = $this->getService() - ->getClient() - ->createRequest('PUT', $this->getUrl(), array( - HeaderConst::X_OBJECT_MANIFEST => (string) $source - )) - ->send(); - - if ($response->getStatusCode() == 201) { - $this->setManifest($source); + if (!$this->name) { + throw new Exceptions\NoNameError(Lang::translate('Object has no name')); + } + + if ($this->getContentLength() == 0) { + $response = $this->getService() + ->getClient() + ->createRequest('PUT', $this->getUrl(), array( + HeaderConst::X_OBJECT_MANIFEST => (string) $destination + )) + ->send(); + + if ($response->getStatusCode() == 201) { + $this->setManifest($source); + } + + return $response; + } + + return null; + } + + /** + * @param string $source Path (`container/object') of other object to symlink this object from + * @return null|DataObject The symlinked object or null if object already exists and is not empty + */ + public function createSymlinkFrom($source) + { + if (!strlen($source)) { + throw new Exceptions\NoNameError(Lang::translate('Object has no name')); + } + + // Use ltrim to remove leading slash from source + list($containerName, $resourceName) = explode("/", ltrim($source, '/'), 2); + + $container = $this->getService()->getContainer($containerName); + + if ($unsafe = $container->objectExists($resourceName)) { + $object = $container->getPartialObject($source); + $unsafe = $object->getContentLength() > 0; + } + + if (!$unsafe) { + return $container->uploadObject($resourceName, 'data', array( + HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl() + )); } - return $response; + return null; } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index fc67ad35d..bac0ebebf 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -101,21 +101,37 @@ public function test_Public_Urls() $this->assertNotNull($object->getPublicUrl(UrlType::STREAMING)); $this->assertNotNull($object->getPublicUrl(UrlType::IOS_STREAMING)); } - - public function test_Symlink() + + public function test_Symlink_To() { $object = $this->container->dataObject('foobar'); - $this->assertInstanceOf( - 'Guzzle\Http\Message\Response', - $object->symlink('/new_container/new_object') - ); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $object->createSymlinkTo('new_container/new_object')); + // @todo getManifest should return the manifest not null + //$this->assertEquals('new_container/new_object', $object->getManifest()); } - + + /** + * @expectedException OpenCloud\Common\Exceptions\NoNameError + */ + public function test_Symlink_To_Fails() + { + $object = $this->container->dataObject()->createSymlinkTo(null); + } + + public function test_Symlink_From() + { + $object = $this->container->dataObject('foobar'); + $symlink = $object->createSymlinkFrom('new_container/new_object'); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $symlink); + // @todo getManifest should return the manifest not null + //$this->assertEquals('new_container/new_object', $symlink->getManifest()); + } + /** * @expectedException OpenCloud\Common\Exceptions\NoNameError */ - public function test_Symlink_Fails() + public function test_Symlink_From_Fails() { - $this->container->dataObject()->symlink(null); + $object = $this->container->dataObject()->createSymlinkFrom(null); } } From 78914cbf288e43b0dde15d9b0c4720b41e54b385 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 20 Mar 2015 15:00:34 +0100 Subject: [PATCH 486/835] [ci skip] Add more links to our official docs --- README.md | 2 +- docs/README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/README.md diff --git a/README.md b/README.md index 6031cf4ac..8ddd9f1b5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ PHP SDK for OpenStack/Rackspace APIs [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) -For SDKs in different languages, see http://developer.rackspace.com. +Our official documentation is now available on http://docs.php-opencloud.com. For SDKs in different languages, see http://developer.rackspace.com. The PHP SDK should work with most OpenStack-based cloud deployments, though it specifically targets the Rackspace public cloud. In diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..154fd158d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Documentation + +Our official docs are hosted on http://docs.php-opencloud.com. From ceebd95ec570c671dc4dbe9e80eed64a38283a23 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Fri, 20 Mar 2015 15:51:08 +0000 Subject: [PATCH 487/835] Added getManifest assertions to symlink tests --- .../ObjectStore/Resource/DataObjectTest.php | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index bac0ebebf..c7859649e 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -17,7 +17,10 @@ namespace OpenCloud\Tests\ObjectStore\Resource; +use Guzzle\Http\Message\Response; +use OpenCloud\Common\Constants\Header; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\Tests\MockSubscriber; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; class DataObjectTest extends ObjectStoreTestCase @@ -104,10 +107,11 @@ public function test_Public_Urls() public function test_Symlink_To() { + $targetName = 'new_container/new_object'; + $this->addMockSubscriber(new Response(200, array(Header::X_OBJECT_MANIFEST => $targetName))); $object = $this->container->dataObject('foobar'); - $this->assertInstanceOf('Guzzle\Http\Message\Response', $object->createSymlinkTo('new_container/new_object')); - // @todo getManifest should return the manifest not null - //$this->assertEquals('new_container/new_object', $object->getManifest()); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $object->createSymlinkTo($targetName)); + $this->assertEquals($targetName, $object->getManifest()); } /** @@ -120,11 +124,27 @@ public function test_Symlink_To_Fails() public function test_Symlink_From() { - $object = $this->container->dataObject('foobar'); - $symlink = $object->createSymlinkFrom('new_container/new_object'); - $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $symlink); - // @todo getManifest should return the manifest not null - //$this->assertEquals('new_container/new_object', $symlink->getManifest()); + $symlinkName = 'new_container/new_object'; + + // We have to fill the mock response queue to properly get the correct X-Object-Manifest header + // Container\dataObject( ) + // - Container\refresh( ) + $this->addMockSubscriber(new Response(200)); + // DataObject\createSymlinkFrom( ) + // - Container\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - CDNContainer\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - Container\objectExists( ) + $this->addMockSubscriber(new Response(200)); + // - Container\getPartialObject( ) + $this->addMockSubscriber(new Response(200)); + // - Container\uploadObject( ) + $this->addMockSubscriber(new Response(200, array(Header::X_OBJECT_MANIFEST => $symlinkName))); + + $object = $this->container->dataObject('foobar')->createSymlinkFrom($symlinkName); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $object); + $this->assertEquals($symlinkName, $object->getManifest()); } /** From 32a4cf79b5f5c202535d8337d2ebf61054045c84 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 20 Mar 2015 17:42:30 +0100 Subject: [PATCH 488/835] [ci skip] Fix incorrect method in docs --- doc/services/object-store/objects.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 0eb0ea836..7d9b2120e 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -204,8 +204,8 @@ Get file name .. code-block:: php - /** @param $container OpenCloud\ObjectStore\Resource\Container */ - $container = $object->getContainer(); + /** @param $name string */ + $name = $object->getName(); Get file size From a5cba84be3c7362d74a3d93b97076f594ae92f36 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Fri, 20 Mar 2015 17:25:06 +0000 Subject: [PATCH 489/835] Changed symlink failures from null to exceptions. Added tests. --- .../Exception/ObjectNotEmptyException.php | 35 ++++++++++++++++ .../ObjectStore/Resource/DataObject.php | 42 +++++++++---------- .../ObjectStore/Resource/DataObjectTest.php | 37 ++++++++++++++-- 3 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php diff --git a/lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php b/lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php new file mode 100644 index 000000000..6bf75f2f9 --- /dev/null +++ b/lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php @@ -0,0 +1,35 @@ +name = $name; + + return $e; + } +} diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index ace797bd3..12ab65d8b 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -24,6 +24,7 @@ use OpenCloud\Common\Exceptions; use OpenCloud\Common\Lang; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\ObjectStore\Exception\ObjectNotEmptyException; /** * Objects are the basic storage entities in Cloud Files. They represent the @@ -393,22 +394,22 @@ public function createSymlinkTo($destination) throw new Exceptions\NoNameError(Lang::translate('Object has no name')); } - if ($this->getContentLength() == 0) { - $response = $this->getService() - ->getClient() - ->createRequest('PUT', $this->getUrl(), array( - HeaderConst::X_OBJECT_MANIFEST => (string) $destination - )) - ->send(); + if ($this->getContentLength()) { + throw new ObjectNotEmptyException($this->getContainer()->getName() . '/' . $this->getName()); + } - if ($response->getStatusCode() == 201) { - $this->setManifest($source); - } + $response = $this->getService() + ->getClient() + ->createRequest('PUT', $this->getUrl(), array( + HeaderConst::X_OBJECT_MANIFEST => (string) $destination + )) + ->send(); - return $response; + if ($response->getStatusCode() == 201) { + $this->setManifest($source); } - return null; + return $response; } /** @@ -423,21 +424,18 @@ public function createSymlinkFrom($source) // Use ltrim to remove leading slash from source list($containerName, $resourceName) = explode("/", ltrim($source, '/'), 2); - $container = $this->getService()->getContainer($containerName); - if ($unsafe = $container->objectExists($resourceName)) { + if ($container->objectExists($resourceName)) { $object = $container->getPartialObject($source); - $unsafe = $object->getContentLength() > 0; - } - - if (!$unsafe) { - return $container->uploadObject($resourceName, 'data', array( - HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl() - )); + if ($object->getContentLength() > 0) { + throw new ObjectNotEmptyException($source); + } } - return null; + return $container->uploadObject($resourceName, 'data', array( + HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl() + )); } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index c7859649e..97af383a9 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -20,6 +20,7 @@ use Guzzle\Http\Message\Response; use OpenCloud\Common\Constants\Header; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\ObjectStore\Exception\ObjectNotEmptyException; use OpenCloud\Tests\MockSubscriber; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; @@ -117,11 +118,20 @@ public function test_Symlink_To() /** * @expectedException OpenCloud\Common\Exceptions\NoNameError */ - public function test_Symlink_To_Fails() + public function test_Symlink_To_Fails_With_NoName() { $object = $this->container->dataObject()->createSymlinkTo(null); } + /** + * @expectedException OpenCloud\ObjectStore\Exception\ObjectNotEmptyException + */ + public function test_Symlink_To_Fails_With_NotEmpty() + { + $this->addMockSubscriber(new Response(200, array(Header::CONTENT_LENGTH => 100))); + $object = $this->container->dataObject('foobar')->createSymlinkTo('new_container/new_object'); + } + public function test_Symlink_From() { $symlinkName = 'new_container/new_object'; @@ -144,14 +154,35 @@ public function test_Symlink_From() $object = $this->container->dataObject('foobar')->createSymlinkFrom($symlinkName); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $object); - $this->assertEquals($symlinkName, $object->getManifest()); } /** * @expectedException OpenCloud\Common\Exceptions\NoNameError */ - public function test_Symlink_From_Fails() + public function test_Symlink_From_Fails_With_NoName() { $object = $this->container->dataObject()->createSymlinkFrom(null); } + + /** + * @expectedException OpenCloud\ObjectStore\Exception\ObjectNotEmptyException + */ + public function test_Symlink_From_Fails_With_NotEmpty() + { + // We have to fill the mock response queue to properly get the correct Content-Length header + // Container\dataObject( ) + // - Container\refresh( ) + $this->addMockSubscriber(new Response(200)); + // DataObject\createSymlinkFrom( ) + // - Container\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - CDNContainer\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - Container\objectExists( ) + $this->addMockSubscriber(new Response(200)); + // - Container\getPartialObject( ) + $this->addMockSubscriber(new Response(200, array(Header::CONTENT_LENGTH => 100))); + + $object = $this->container->dataObject('foobar')->createSymlinkFrom('new_container/new_object'); + } } From f3a5658551004e0e88385ca48e9fdeddb0497c40 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Fri, 20 Mar 2015 17:38:32 +0000 Subject: [PATCH 490/835] Added documentation --- doc/services/object-store/objects.rst | 35 ++++++++++++++++++++ samples/ObjectStore/symlink-object.php | 45 ++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 samples/ObjectStore/symlink-object.php diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 0eb0ea836..107d7c584 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -326,6 +326,41 @@ the name of the object inside the container that does not exist yet. `Get the executable PHP script for this example `_ +Symlinking to this object from another location +----------------------------------------------- + +To create a symlink to this file in another location you need to specify +a string-based source + +.. code-block:: php + + $object->createSymlinkFrom('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container that either does not exist yet or +is an empty file. + +`Get the executable PHP script for this example `_ + + +Setting this object to symlink to another location +-------------------------------------------------- + +To set this file to symlink to another location you need to specify +a string-based destination + +.. code-block:: php + + $object->createSymlinkTo('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container. + +The object must be an empty file. + +`Get the executable PHP script for this example `_ + + Get object metadata ------------------- diff --git a/samples/ObjectStore/symlink-object.php b/samples/ObjectStore/symlink-object.php new file mode 100644 index 000000000..f5c4bc129 --- /dev/null +++ b/samples/ObjectStore/symlink-object.php @@ -0,0 +1,45 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Get container. +$container = $objectStoreService->getContainer('{sourceContainerName}'); + +// 4. Get object. +$object = $container->getObject('{objectName}'); + +// 5. Create another container for object's copy. +$objectStoreService->createContainer('{destinationContainerName}'); + +// 6. Symlink to object from another container object. {objectName} must either not exist or be an empty file. +$object->createSymlinkFrom('{destinationContainerName}/{objectName}'); + +// 7. Symlink from object to another container object. $object must be an empty file. +$object->createSymlinkTo('{destinationContainerName}/{objectName}'); From 27ed6104e7453ce75b42309cc8cf5d108b037993 Mon Sep 17 00:00:00 2001 From: justin Finkelstein Date: Sat, 21 Mar 2015 13:27:20 +0000 Subject: [PATCH 491/835] Added example of how to retrieve more than 10,000 objects --- doc/services/object-store/objects.rst | 36 +++++++++++ .../ObjectStore/list-objects-over-10000.php | 62 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 samples/ObjectStore/list-objects-over-10000.php diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 0eb0ea836..50af7aff3 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -177,6 +177,42 @@ docs `_ +List over 10,000 objects +------------------------ + +To retrieve more than 10,000 objects (the default limit), you'll need to use +the built-in paging which uses a 'marker' parameter to fetch the next page of data. + +.. code-block:: php + + $containerObjects = array(); + $marker = ''; + + while ($marker !== null) { + $params = array( + 'marker' => $marker, + ); + + $objects = $container->objectList($params); + $total = $objects->count(); + $count = 0; + + if ($total == 0) { + break; + } + + while ($object = $objects->next()) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + $containerObjects[] = $object->getName(); + $count++; + + $marker = ($count == $total) ? $object->getName() : null; + } + } + +`Get the executable PHP script for this example `_ + + Get object ---------- diff --git a/samples/ObjectStore/list-objects-over-10000.php b/samples/ObjectStore/list-objects-over-10000.php new file mode 100644 index 000000000..364ebfdc7 --- /dev/null +++ b/samples/ObjectStore/list-objects-over-10000.php @@ -0,0 +1,62 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Get container. +$container = $objectStoreService->getContainer('{containerName}'); + +// 4. Get the list of objects +$objects = $container->objectList(); + +// 5. Create a list of all objects in the container +$containerObjects = array(); +$marker = ''; + +while ($marker !== null) { + $params = array( + 'marker' => $marker, + ); + + $objects = $container->objectList($params); + $total = $objects->count(); + $count = 0; + + if ($total == 0) { + break; + } + + while ($object = $objects->next()) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + $containerObjects[] = $object->getName(); + $count++; + + $marker = ($count == $total) ? $object->getName() : null; + } +} From 7d6a182d9904eaf48f8b80880eeb26f5cddc56ce Mon Sep 17 00:00:00 2001 From: justin Finkelstein Date: Sat, 21 Mar 2015 15:50:46 +0000 Subject: [PATCH 492/835] Updated to use foreach rather than while --- doc/services/object-store/objects.rst | 2 +- samples/ObjectStore/list-objects-over-10000.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 50af7aff3..2f70be7e5 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -201,7 +201,7 @@ the built-in paging which uses a 'marker' parameter to fetch the next page of da break; } - while ($object = $objects->next()) { + foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ $containerObjects[] = $object->getName(); $count++; diff --git a/samples/ObjectStore/list-objects-over-10000.php b/samples/ObjectStore/list-objects-over-10000.php index 364ebfdc7..ec349e88d 100644 --- a/samples/ObjectStore/list-objects-over-10000.php +++ b/samples/ObjectStore/list-objects-over-10000.php @@ -52,7 +52,7 @@ break; } - while ($object = $objects->next()) { + foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ $containerObjects[] = $object->getName(); $count++; From 4e6b676ab4bfd314ac4fe1e8d0a90a73c826d755 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 22 Mar 2015 12:08:35 -0700 Subject: [PATCH 493/835] Fixing return type. --- lib/OpenCloud/DNS/Resource/Domain.php | 2 +- lib/OpenCloud/DNS/Service.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/DNS/Resource/Domain.php b/lib/OpenCloud/DNS/Resource/Domain.php index cdd8ef28e..fc9bd89b8 100644 --- a/lib/OpenCloud/DNS/Resource/Domain.php +++ b/lib/OpenCloud/DNS/Resource/Domain.php @@ -76,7 +76,7 @@ public function record($info = null) * returns a Collection of Record objects * * @param array $filter query-string parameters - * @return \OpenCloud\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function recordList($filter = array()) { diff --git a/lib/OpenCloud/DNS/Service.php b/lib/OpenCloud/DNS/Service.php index cafb4ad94..cdee6dc48 100644 --- a/lib/OpenCloud/DNS/Service.php +++ b/lib/OpenCloud/DNS/Service.php @@ -59,7 +59,7 @@ public function domain($info = null) * Returns a collection of domains * * @param array $filter key/value pairs to use as query strings - * @return \OpenCloud\Common\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function domainList($filter = array()) { @@ -85,7 +85,7 @@ public function ptrRecord($info = null) * * @param \OpenCloud\Compute\Resource\Server $server the server for which to * retrieve the PTR records - * @return \OpenCloud\Common\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function ptrRecordList(HasPtrRecordsInterface $parent) { From 163b0358fecea3cc0069e75f53a4237024961a45 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 22 Mar 2015 12:08:46 -0700 Subject: [PATCH 494/835] Adding return type. --- lib/OpenCloud/DNS/Resource/Domain.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/OpenCloud/DNS/Resource/Domain.php b/lib/OpenCloud/DNS/Resource/Domain.php index fc9bd89b8..a103677f5 100644 --- a/lib/OpenCloud/DNS/Resource/Domain.php +++ b/lib/OpenCloud/DNS/Resource/Domain.php @@ -107,6 +107,7 @@ public function subdomain($info = array()) * * @param array $filter key/value pairs for query string parameters * return \OpenCloud\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function subdomainList($filter = array()) { From 1caf1d39f046b6f4da1c8a9572461c2058471711 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 22 Mar 2015 12:08:54 -0700 Subject: [PATCH 495/835] Adding docblock. --- lib/OpenCloud/DNS/Service.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/OpenCloud/DNS/Service.php b/lib/OpenCloud/DNS/Service.php index cdee6dc48..9d3bbc9dd 100644 --- a/lib/OpenCloud/DNS/Service.php +++ b/lib/OpenCloud/DNS/Service.php @@ -181,6 +181,13 @@ public function limitTypes() return $body->limitTypes; } + /** + * List asynchronous responses' statuses. + * @see http://docs.rackspace.com/cdns/api/v1.0/cdns-devguide/content/viewing_status_all_asynch_jobs.html + * + * @param array $query Any query parameters. Optional. + * @return OpenCloud\DNS\Collection\DnsIterator + */ public function listAsyncJobs(array $query = array()) { $url = clone $this->getUrl(); From 9958eb9b61dcff38b3a12e638451512539751ca7 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Mon, 23 Mar 2015 15:09:51 +0000 Subject: [PATCH 496/835] Updated phpdoc for createSymlink functions --- lib/OpenCloud/ObjectStore/Resource/DataObject.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index 12ab65d8b..de302b4cd 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -385,8 +385,12 @@ public function delete($params = array()) } /** + * Create a symlink to another named object from this object. Requires this object to be empty. + * * @param string $destination Path (`container/object') of other object to symlink this object to - * @return null|\Guzzle\Http\Message\Response The response or null if $this is not empty + * @return \Guzzle\Http\Message\Response The response + * @throws \OpenCloud\Common\Exceptions\NoNameError if a destination name is not provided + * @throws \OpenCloud\ObjectStore\Exception\ObjectNotEmptyException if $this is not an empty object */ public function createSymlinkTo($destination) { @@ -413,8 +417,12 @@ public function createSymlinkTo($destination) } /** + * Create a symlink to this object from another named object. Requires the other object to either not exist or be empty. + * * @param string $source Path (`container/object') of other object to symlink this object from - * @return null|DataObject The symlinked object or null if object already exists and is not empty + * @return DataObject The symlinked object + * @throws \OpenCloud\Common\Exceptions\NoNameError if a source name is not provided + * @throws \OpenCloud\ObjectStore\Exception\ObjectNotEmptyException if object already exists and is not empty */ public function createSymlinkFrom($source) { From 450356387f84de00b8a485b5e64a18f466189bbe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Mar 2015 10:41:16 -0700 Subject: [PATCH 497/835] Appeasing the PSR-2 linter. --- lib/OpenCloud/ObjectStore/Resource/AbstractResource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php index 8e16b30b3..37722e93d 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php @@ -77,7 +77,7 @@ public function getClient() /** * Factory method that allows for easy instantiation from a Response object. - * + * * For internal use only. * * @param Response $response HTTP response from an API operation. From 90dfc12c926c40c70289c599e17d1458f73abf38 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Mar 2015 16:13:51 -0700 Subject: [PATCH 498/835] Lazily initialize CDN container object composed insider regular container object. --- .../ObjectStore/Resource/Container.php | 41 +++++++++++-------- .../Tests/ObjectStore/ObjectStoreTestCase.php | 11 +++-- .../ObjectStore/Resource/ContainerTest.php | 6 ++- .../ObjectStore/Resource/DataObjectTest.php | 2 + 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 0f7b0df08..57b8ce34a 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -323,23 +323,6 @@ public function refresh($id = null, $url = null) { $headers = $this->createRefreshRequest()->send()->getHeaders(); $this->setMetadata($headers, true); - - try { - if (null !== ($cdnService = $this->getService()->getCDNService())) { - $cdn = new CDNContainer($cdnService); - $cdn->setName($this->name); - - $response = $cdn->createRefreshRequest()->send(); - - if ($response->isSuccessful()) { - $this->cdn = $cdn; - $this->cdn->setMetadata($response->getHeaders(), true); - } - } else { - $this->cdn = null; - } - } catch (ClientErrorResponseException $e) { - } } /** @@ -597,6 +580,30 @@ public function uploadDirectory($path) public function isCdnEnabled() { + // If CDN object is not already populated, try to populate it. + if (null === $this->cdn) { + $this->refreshCdnObject(); + } return ($this->cdn instanceof CDNContainer) && $this->cdn->isCdnEnabled(); } + + protected function refreshCdnObject() + { + try { + if (null !== ($cdnService = $this->getService()->getCDNService())) { + $cdn = new CDNContainer($cdnService); + $cdn->setName($this->name); + + $response = $cdn->createRefreshRequest()->send(); + + if ($response->isSuccessful()) { + $this->cdn = $cdn; + $this->cdn->setMetadata($response->getHeaders(), true); + } + } else { + $this->cdn = null; + } + } catch (ClientErrorResponseException $e) { + } + } } diff --git a/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php b/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php index 985f7830c..3c3c995c4 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php +++ b/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php @@ -42,7 +42,12 @@ public function setupObjects() $this->addMockSubscriber($response1); - $response2 = new Response(204, array( + $this->container = $this->service->getContainer('foo'); + } + + protected function setupCdnContainerMockResponse() + { + $response = new Response(204, array( 'X-Cdn-Ssl-Uri' => 'https://83c49b9a2f7ad18250b3-346eb45fd42c58ca13011d659bfc1ac1.ssl.cf0.rackcdn.com', 'X-Ttl' => '259200', 'X-Cdn-Uri' => 'http://081e40d3ee1cec5f77bf-346eb45fd42c58ca13011d659bfc1ac1.r49.cf0.rackcdn.com', @@ -52,8 +57,6 @@ public function setupObjects() 'X-Trans-Id' => 'tx82a6752e00424edb9c46fa2573132e2c' )); - $this->addMockSubscriber($response2); - - $this->container = $this->service->getContainer('foo'); + $this->addMockSubscriber($response); } } diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index b5d2bc602..c5b191249 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -51,6 +51,8 @@ public function test_Get_Container() $this->assertEquals('3846773', $container->getBytesUsed()); $this->assertFalse($container->hasLogRetention()); + $this->setupCdnContainerMockResponse(); + $cdn = $container->getCdn(); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\CDNContainer', $cdn); $this->assertEquals('tx82a6752e00424edb9c46fa2573132e2c', $cdn->getTransId()); @@ -115,13 +117,13 @@ public function test_Delete_NonEmpty_Container() $this->addMockSubscriber($this->makeResponse('[]', 409)); $container->delete(); } + public function test_Object_List() { $container = $this->container; $this->addMockSubscriber($this->makeResponse('[{"name":"test_obj_1","hash":"4281c348eaf83e70ddce0e07221c3d28","bytes":14,"content_type":"application\/octet-stream","last_modified":"2009-02-03T05:26:32.612278"},{"name":"test_obj_2","hash":"b039efe731ad111bc1b0ef221c3849d0","bytes":64,"content_type":"application\/octet-stream","last_modified":"2009-02-03T05:26:32.612278"}]', 200)); - $list = $container->objectList(); $this->assertInstanceOf(self::COLLECTION_CLASS, $list); $this->assertEquals('test_obj_1', $list->first()->getName()); @@ -141,6 +143,8 @@ public function test_Misc_Operations() $container->disableLogging() ); + $this->setupCdnContainerMockResponse(); + $this->assertInstanceOf( 'Guzzle\Http\Message\Response', $container->getCdn()->setStaticIndexPage('index.html') diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 8193c267d..50a44d0ec 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -86,6 +86,7 @@ public function test_Temp_Url_Fails_With_Incorrect_Method() public function test_Purge() { $object = $this->container->dataObject('foobar'); + $this->setupCdnContainerMockResponse(); $this->assertInstanceOf( 'Guzzle\Http\Message\Response', $object->purge('test@example.com') @@ -96,6 +97,7 @@ public function test_Public_Urls() { $object = $this->container->dataObject('foobar'); + $this->setupCdnContainerMockResponse(); $this->assertNotNull($object->getPublicUrl()); $this->assertNotNull($object->getPublicUrl(UrlType::SSL)); $this->assertNotNull($object->getPublicUrl(UrlType::STREAMING)); From 7868606902b59f3ed5e2690b5445949aa81cd01e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Mar 2015 16:28:30 -0700 Subject: [PATCH 499/835] Removing mocking for refreshing CDN container composed object. --- tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index cd35674d3..4bebaf346 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -178,8 +178,6 @@ public function test_Symlink_From_Fails_With_NotEmpty() // DataObject\createSymlinkFrom( ) // - Container\createRefreshRequest( ) $this->addMockSubscriber(new Response(200)); - // - CDNContainer\createRefreshRequest( ) - $this->addMockSubscriber(new Response(200)); // - Container\objectExists( ) $this->addMockSubscriber(new Response(200)); // - Container\getPartialObject( ) From 2bf6e0db9064d5f303cea2fba39fa77dc7d29334 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 03:19:04 +0100 Subject: [PATCH 500/835] Fixing add, modify, and remove records links. --- doc/services/dns/domains.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/dns/domains.rst b/doc/services/dns/domains.rst index 0a7b83b96..b6c60290c 100644 --- a/doc/services/dns/domains.rst +++ b/doc/services/dns/domains.rst @@ -268,8 +268,8 @@ Modify domain Only the TTL, email address and comment attributes of a domain can be modified. Records cannot be added, modified, or removed through this API operation - you -will need to use the `add records `__, `modify records -`__ or `remove records `__ +will need to use the `add records `__, `modify records +`__ or `remove records `__ operations respectively. .. code-block:: php From 24b5e97e161a8f52438e5766ee6b1916a9fa2e99 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 08:51:08 +0100 Subject: [PATCH 501/835] Adding convenience method to retrieve domain by name. --- .../Exceptions/DomainNotFoundException.php | 22 +++++++++++++++++++ lib/OpenCloud/DNS/Service.php | 19 ++++++++++++++++ tests/OpenCloud/Tests/DNS/ServiceTest.php | 9 ++++++++ 3 files changed, 50 insertions(+) create mode 100644 lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php diff --git a/lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php b/lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php new file mode 100644 index 000000000..07ddfb608 --- /dev/null +++ b/lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php @@ -0,0 +1,22 @@ +resource('Domain', $info); } + /** + * Returns a domain, given a domain name + * + * @param string $domainName Domain name + * @return Domain the domain object + */ + public function domainByName($domainName) + { + $domainList = $this->domainList(array("name" => $domainName)); + + if (count($domainList) != 1) { + throw new DomainNotFoundException(); + } + + return $this->resource('Domain', $domainList[0]); + } + + /** * Returns a collection of domains * diff --git a/tests/OpenCloud/Tests/DNS/ServiceTest.php b/tests/OpenCloud/Tests/DNS/ServiceTest.php index 73da7403e..5c299ff1c 100644 --- a/tests/OpenCloud/Tests/DNS/ServiceTest.php +++ b/tests/OpenCloud/Tests/DNS/ServiceTest.php @@ -40,6 +40,15 @@ public function testDomain() $this->assertInstanceOf('OpenCloud\DNS\Resource\Domain', $this->service->domain()); } + public function testDomainByName() + { + $this->addMockSubscriber($this->makeResponse('{"domains":[{"name":"region2.example.net","id":2725352,"updated":"2011-06-23T20:21:06.000+0000","created":"2011-06-23T19:24:27.000+0000"}],"totalEntries":114}', 200)); + $domain = $this->service->domainByName("region2.example.net"); + + $this->assertInstanceOf('OpenCloud\DNS\Resource\Domain', $domain); + $this->assertEquals("region2.example.net", $domain->getName()); + } + /** * @mockFile Domain_List */ From 920e1f3b14f6e54ce4146db11863e60afe5cfec5 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 08:58:48 +0100 Subject: [PATCH 502/835] Adding test for domain not found, given domain name. --- tests/OpenCloud/Tests/DNS/ServiceTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/OpenCloud/Tests/DNS/ServiceTest.php b/tests/OpenCloud/Tests/DNS/ServiceTest.php index 5c299ff1c..9503e0409 100644 --- a/tests/OpenCloud/Tests/DNS/ServiceTest.php +++ b/tests/OpenCloud/Tests/DNS/ServiceTest.php @@ -49,6 +49,15 @@ public function testDomainByName() $this->assertEquals("region2.example.net", $domain->getName()); } + /** + * @expectedException OpenCloud\Common\Exceptions\DomainNotFoundException + */ + public function testDomainByNameWhenDomainNotFound() + { + $this->addMockSubscriber($this->makeResponse('{"domains":[],"totalEntries":114}', 200)); + $domain = $this->service->domainByName("region2.example.net"); + } + /** * @mockFile Domain_List */ From cebb5d5f517ee491345080e23699c0f2da71139f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 09:40:29 +0100 Subject: [PATCH 503/835] Adding support for availability zones in Compute servers. --- lib/OpenCloud/Compute/Resource/Server.php | 13 +++++ .../create_server_with_availability_zone.php | 48 +++++++++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 9 ++++ .../Tests/Compute/_response/Extensions.resp | 8 ++++ 4 files changed, 78 insertions(+) create mode 100644 samples/Compute/create_server_with_availability_zone.php diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 11fb474ca..490c285e6 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -172,6 +172,12 @@ class Server extends NovaResource implements HasPtrRecordsInterface */ public $powerStatus; + /** + * @link http://developer.openstack.org/api-ref-compute-v2-ext.html#ext-os-ext-az + * @var string Availability zone of the VM + */ + public $availabilityZone; + protected static $json_name = 'server'; protected static $url_resource = 'servers'; @@ -206,6 +212,7 @@ class Server extends NovaResource implements HasPtrRecordsInterface 'OS-EXT-STS:vm_state' => 'extendedStatus', 'OS-EXT-STS:task_state' => 'taskStatus', 'OS-EXT-STS:power_state' => 'powerStatus', + 'OS-EXT-AZ:availability_zone' => 'availabilityZone' ); /** @@ -729,6 +736,12 @@ protected function createJson() $server->user_data = $this->user_data; } + // Availability zone + if (!empty($this->availabilityZone)) { + $this->checkExtension('OS-EXT-AZ'); + $server->availability_zone = $this->availabilityZone; + } + return (object) array('server' => $server); } diff --git a/samples/Compute/create_server_with_availability_zone.php b/samples/Compute/create_server_with_availability_zone.php new file mode 100644 index 000000000..cf4b1f4bc --- /dev/null +++ b/samples/Compute/create_server_with_availability_zone.php @@ -0,0 +1,48 @@ + '{username}', + 'password' => '{password}', +)); + +// 2. Create Compute service +$service = $client->computeService('nova', '{region}'); + +// 3. Get empty server +$server = $service->server(); + +// 4. Create the server. If you do not know what imageId or flavorId to use, +// please run the list_flavors.php and list_images.php scripts. +try { + $response = $server->create(array( + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'availabilityZone' => '{availabilityZone}' + )); +} catch (BadResponseException $e) { + echo $e->getResponse(); +} diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index 580e77e65..13ed80a35 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -403,4 +403,13 @@ public function test_Stop() $this->addMockSubscriber(new \Guzzle\Http\Message\Response(202)); $this->assertEquals(202, $this->server->stop()->getStatusCode()); } + + public function test_Create_Availability_Zone() + { + $new = new PublicServer($this->service); + $new->setAvailabilityZone('AZ1'); + $obj = $new->CreateJson(); + + $this->assertEquals('AZ1', $obj->server->availability_zone); + } } diff --git a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp index 68c63da10..8474a182c 100644 --- a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp +++ b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp @@ -15,6 +15,14 @@ Server: Jetty(8.0.y.z-SNAPSHOT) "alias": "OS-DCF", "description": "Disk Management Extension." }, + { + "updated": "2013-01-30T00:00:00Z", + "name": "ExtendedAvailabilityZone", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/extended_availability_zone/api/v2", + "alias": "OS-EXT-AZ", + "description": "Extended Availability Zone support." + }, { "updated": "2013-02-19T00:00:00Z", "name": "ImageSize", From d2d33299af505da2741e7b490b9cae487f9a6bef Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 09:49:09 +0100 Subject: [PATCH 504/835] Fixing indentation. --- lib/OpenCloud/Compute/Resource/Server.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 490c285e6..0d47f5dec 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -738,8 +738,8 @@ protected function createJson() // Availability zone if (!empty($this->availabilityZone)) { - $this->checkExtension('OS-EXT-AZ'); - $server->availability_zone = $this->availabilityZone; + $this->checkExtension('OS-EXT-AZ'); + $server->availability_zone = $this->availabilityZone; } return (object) array('server' => $server); From a108d3165118a5913f24182b23c62cd5e72f52e5 Mon Sep 17 00:00:00 2001 From: AdamMerrifield Date: Wed, 1 Apr 2015 08:09:19 -0400 Subject: [PATCH 505/835] Changed 'privateURL' to 'internalURL' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Object store documentation is displaying the incorrect url type. It says 'privateURL’ but it should be ‘internalURL’. --- doc/services/object-store/migrating-containers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/migrating-containers.rst b/doc/services/object-store/migrating-containers.rst index 22adb839b..da983af3e 100644 --- a/doc/services/object-store/migrating-containers.rst +++ b/doc/services/object-store/migrating-containers.rst @@ -68,12 +68,12 @@ You can access all this functionality by executing: It's advisable to do this process in a Cloud Server in one of the two -regions you're migrating to/from. This allows you to use ``privateURL`` +regions you're migrating to/from. This allows you to use ``internalURL`` as the third argument in the ``objectStoreService`` methods like this: .. code-block:: php - $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + $client->objectStoreService('cloudFiles', 'IAD', 'internalURL'); This will ensure that traffic between your server and your new IAD From 14d0de842fc25cd69930dee3646ca69ee0986192 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 10:41:02 +0200 Subject: [PATCH 506/835] Fix dead links; fixes #579 --- doc/services/common/service-args.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/services/common/service-args.rst b/doc/services/common/service-args.rst index c1cc754b8..128986d91 100644 --- a/doc/services/common/service-args.rst +++ b/doc/services/common/service-args.rst @@ -3,9 +3,9 @@ default will be provided if you pass in ``null``. * ``{region}`` is the region the service will operate in. For Rackspace - users, you can select one of the following from the `supported regions page - `_. + users, you can select one of the following from the :doc:`supported regions page + `. -* ``{urlType}`` is the `type of URL `_ to use, depending on which +* ``{urlType}`` is the :doc:`type of URL ` to use, depending on which endpoints your catalog provides. If omitted, it will default to the public network. From 067cf97ea49b781052b614085dee42f389b1fc2e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 14:08:54 +0200 Subject: [PATCH 507/835] Bump versions for development packages --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 693e986ec..a0fa3b76a 100644 --- a/composer.json +++ b/composer.json @@ -31,10 +31,11 @@ "psr/log": "~1.0" }, "require-dev" : { + "php": ">=5.4", "phpunit/phpunit": "4.3.*", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", - "apigen/apigen": "~2.8" + "apigen/apigen": "~4.0" } } From c099ce58bd5fc12ec2ef5c8388229f9e19d4d1cc Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:06:34 +0200 Subject: [PATCH 508/835] Shift minimum PHP version to 5.4 --- README.md | 4 +++- composer.json | 3 +-- doc/using-php-5.4.rst | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 doc/using-php-5.4.rst diff --git a/README.md b/README.md index 8ddd9f1b5..5f4d75da5 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,11 @@ so that you can still use the SDK with a pure OpenStack instance Requirements ------------ -* PHP >=5.3.3 +* PHP >=5.4 * cURL extension for PHP +**Note**: Since PHP 5.3 has reached [end of life](http://php.net/eol.php) and is no longer officially supported, we are moving to 5.4 as a minimum requirement. If upgrading is not an option and you still need a stable version of the SDK for 5.3, please follow [this guide](http://docs.php-opencloud.com/en/latest/using-php-5.3). + Installation ------------ You must install this library through Composer: diff --git a/composer.json b/composer.json index a0fa3b76a..d22c56524 100644 --- a/composer.json +++ b/composer.json @@ -26,12 +26,11 @@ } }, "require": { - "php" : ">=5.3.3", + "php" : ">=5.4", "guzzle/guzzle" : "~3.8", "psr/log": "~1.0" }, "require-dev" : { - "php": ">=5.4", "phpunit/phpunit": "4.3.*", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", diff --git a/doc/using-php-5.4.rst b/doc/using-php-5.4.rst new file mode 100644 index 000000000..8790710f2 --- /dev/null +++ b/doc/using-php-5.4.rst @@ -0,0 +1,20 @@ +Using the SDK with PHP v5.3 +=========================== + +Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. + +The first way is by requiring it through the command line: + +.. code-block:: bash + + composer require rackspace/php-opencloud:1.12 + +The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: + +.. code-block:: json + + "require": { + "rackspace/php-opencloud": "~1.12" + } + +Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. From cd838faa3a939e9ba63f931244bba8afc4cf119b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:07:03 +0200 Subject: [PATCH 509/835] Remove 5.3 from build matrix --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bdd5f76af..78dd0003f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ php: - "5.6" - "5.5" - "5.4" - - "5.3" - hhvm sudo: false @@ -33,4 +32,4 @@ notifications: - shaunak.kashyap@rackspace.com env: - - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" \ No newline at end of file + - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" From 6c084c6faefc28b76e7f3474c453a306cac0d784 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:07:58 +0200 Subject: [PATCH 510/835] Fix path --- doc/using-php-5.4.rst | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 doc/using-php-5.4.rst diff --git a/doc/using-php-5.4.rst b/doc/using-php-5.4.rst deleted file mode 100644 index 8790710f2..000000000 --- a/doc/using-php-5.4.rst +++ /dev/null @@ -1,20 +0,0 @@ -Using the SDK with PHP v5.3 -=========================== - -Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. - -The first way is by requiring it through the command line: - -.. code-block:: bash - - composer require rackspace/php-opencloud:1.12 - -The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: - -.. code-block:: json - - "require": { - "rackspace/php-opencloud": "~1.12" - } - -Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. From 78ef25d023807bda66c198d4a6ca347e58de7ac4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:08:30 +0200 Subject: [PATCH 511/835] Fix path --- doc/using-php-5.3.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 doc/using-php-5.3.rst diff --git a/doc/using-php-5.3.rst b/doc/using-php-5.3.rst new file mode 100644 index 000000000..8790710f2 --- /dev/null +++ b/doc/using-php-5.3.rst @@ -0,0 +1,20 @@ +Using the SDK with PHP v5.3 +=========================== + +Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. + +The first way is by requiring it through the command line: + +.. code-block:: bash + + composer require rackspace/php-opencloud:1.12 + +The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: + +.. code-block:: json + + "require": { + "rackspace/php-opencloud": "~1.12" + } + +Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. From 8545dda0f1cbda7125edea1ba9869964141f6637 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 16:55:23 +0200 Subject: [PATCH 512/835] Add note to main install guide --- doc/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/index.rst b/doc/index.rst index 4994f0efb..3c47091cc 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -30,6 +30,10 @@ Read the :doc:`getting-started-with-openstack` or :doc:`getting-started-with-rackspace` to help you get started with basic Compute operations. +.. note:: + + If you are running PHP 5.3, please see our :doc:`using-php-5.3` guide. + Services -------- From 9457407e387b67a43c7ade53d04a4a8ffb0f1b2d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 16:55:34 +0200 Subject: [PATCH 513/835] Bump library version --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index 47aafabf1..87afdd772 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.12.2'; + const VERSION = '1.13'; /** * @return string Indicate current SDK version. From 2b0a6961236a01155901e86d017e93b5154ca705 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 17:00:35 +0200 Subject: [PATCH 514/835] Use correct format --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index 87afdd772..b38917918 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.13'; + const VERSION = '1.13.0'; /** * @return string Indicate current SDK version. From 5b2a951c408abe032914714ceef90b2931526aab Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 3 Apr 2015 19:58:32 +0200 Subject: [PATCH 515/835] Use correct link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f4d75da5..12fb530b8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Requirements * PHP >=5.4 * cURL extension for PHP -**Note**: Since PHP 5.3 has reached [end of life](http://php.net/eol.php) and is no longer officially supported, we are moving to 5.4 as a minimum requirement. If upgrading is not an option and you still need a stable version of the SDK for 5.3, please follow [this guide](http://docs.php-opencloud.com/en/latest/using-php-5.3). +**Note**: Since PHP 5.3 has reached [end of life](http://php.net/eol.php) and is no longer officially supported, we are moving to 5.4 as a minimum requirement. If upgrading is not an option and you still need a stable version of the SDK for 5.3, please follow [this guide](http://docs.php-opencloud.com/en/latest/using-php-5.3.html). Installation ------------ From 51223391a413017d0a8b86f619d4494547232d34 Mon Sep 17 00:00:00 2001 From: AdamMerrifield Date: Sun, 5 Apr 2015 22:08:55 -0400 Subject: [PATCH 516/835] Guzzle Backoff Correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first parameter in Guzzle’s BackoffPlugin is the max number of retries to attempt. --- doc/services/object-store/migrating-containers.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/services/object-store/migrating-containers.rst b/doc/services/object-store/migrating-containers.rst index da983af3e..2a985d20a 100644 --- a/doc/services/object-store/migrating-containers.rst +++ b/doc/services/object-store/migrating-containers.rst @@ -37,13 +37,13 @@ Requirements use Guzzle\Plugin\Backoff\BackoffPlugin; - // set timeout in secs - $timeout = 10; + // maximum number of retries + $maxRetries = 10; // set HTTP error codes $httpErrors = array(500, 503, 408); - $backoffPlugin = BackoffPlugin::getExponentialBackoff($timeout, $httpErrors); + $backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, $httpErrors); $client->addSubscriber($backoffPlugin); From baeaf15cef233db7d203e0da3273215f02f6eb08 Mon Sep 17 00:00:00 2001 From: AdamMerrifield Date: Mon, 6 Apr 2015 07:28:25 -0400 Subject: [PATCH 517/835] Fix method in documentation of Object->getContent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under the ‘get content of file’ header the code block should be $object->getContent(); --- doc/services/object-store/objects.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index e23d6d4cb..7e32f3610 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -259,7 +259,7 @@ Get content of file .. code-block:: php /** @param $content Guzzle\Http\EntityBody */ - $content = $object->getContainer(); + $content = $object->getContent(); Get type of file From 1f6ba4361100edcee4a622aaa02c45bd904d8d92 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 15:12:39 +0100 Subject: [PATCH 518/835] replace Logger::deprecated with PRS-3 Logger::warning --- lib/OpenCloud/Common/Collection.php | 3 ++- lib/OpenCloud/Common/Collection/ResourceIterator.php | 3 ++- lib/OpenCloud/Common/Log/Logger.php | 4 ++++ lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 6 ++++-- lib/OpenCloud/LoadBalancer/Service.php | 3 ++- lib/OpenCloud/ObjectStore/Service.php | 3 ++- lib/OpenCloud/OpenStack.php | 9 ++++++--- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index f2710639e..b6fe7471b 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -50,7 +50,8 @@ class Collection extends Base */ public function __construct($service, $class, array $array = array()) { - $service->getLogger()->deprecated(__METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder'); + $service->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); $this->setService($service); diff --git a/lib/OpenCloud/Common/Collection/ResourceIterator.php b/lib/OpenCloud/Common/Collection/ResourceIterator.php index 627ed78eb..a95455386 100644 --- a/lib/OpenCloud/Common/Collection/ResourceIterator.php +++ b/lib/OpenCloud/Common/Collection/ResourceIterator.php @@ -223,7 +223,8 @@ public function getElement($offset) */ public function first() { - Logger::newInstance()->deprecated(__METHOD__, 'getElement'); + Logger::newInstance()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'getElement')); return $this->getElement(0); } diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index 7eb48cdc0..56cb7cbf2 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -239,6 +239,10 @@ private function dispatch($message, $context) } } + /** + * @deprecated use warning for deprecated messages + * @see http://www.php-fig.org/psr/psr-3/ + */ public function deprecated($method, $new) { $string = sprintf('The %s method is deprecated, please use %s instead', $method, $new); diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index bd0742bcb..080dbb732 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -621,7 +621,8 @@ public function enableConnectionLogging($bool) */ public function connectionLogging() { - $this->getLogger()->deprecated(__METHOD__, 'hasConnectionLogging or enableConnectionLogging'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasConnectionLogging or enableConnectionLogging')); } /** @@ -663,7 +664,8 @@ public function enableContentCaching($bool) */ public function contentCaching() { - $this->getLogger()->deprecated(__METHOD__, 'hasContentCaching or setContentCaching'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasContentCaching or setContentCaching')); } /** diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 8ba659f87..c11022114 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -62,7 +62,8 @@ public function loadBalancerList($detail = true, array $filter = array()) */ public function billableLoadBalancer($id = null) { - $this->getLogger()->deprecated(__METHOD__, 'loadBalancer'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'loadBalancer')); return $this->resource('LoadBalancer', $id); } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index a2d01a479..ce3446880 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -174,7 +174,8 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ */ public function bulkDelete(array $paths) { - $this->getLogger()->deprecated(__METHOD__, '::batchDelete()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, '::batchDelete()')); return $this->executeBatchDeleteRequest($paths); } diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 6bc6eeba1..3511bc4f4 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -165,7 +165,8 @@ public function getTokenObject() */ public function setExpiration($expiration) { - $this->getLogger()->deprecated(__METHOD__, '::getTokenObject()->setExpires()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->setExpires()')); if ($this->getTokenObject()) { $this->getTokenObject()->setExpires($expiration); } @@ -178,7 +179,8 @@ public function setExpiration($expiration) */ public function getExpiration() { - $this->getLogger()->deprecated(__METHOD__, '::getTokenObject()->getExpires()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->getExpires()')); if ($this->getTokenObject()) { return $this->getTokenObject()->getExpires(); } @@ -290,7 +292,8 @@ public function getLogger() */ public function hasExpired() { - $this->getLogger()->deprecated(__METHOD__, 'getTokenObject()->hasExpired()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'getTokenObject()->hasExpired()')); return $this->getTokenObject() && $this->getTokenObject()->hasExpired(); } From 1d21d41b8749d8c0d8f8988cd687fe3ba4845583 Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 17:41:36 +0100 Subject: [PATCH 519/835] move deprecation message to static function --- lib/OpenCloud/Common/Collection.php | 5 +++-- lib/OpenCloud/Common/Collection/ResourceIterator.php | 3 +-- lib/OpenCloud/Common/Log/Logger.php | 8 +++----- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 4 ++-- lib/OpenCloud/LoadBalancer/Service.php | 4 ++-- lib/OpenCloud/ObjectStore/Service.php | 4 ++-- lib/OpenCloud/OpenStack.php | 10 ++++------ tests/OpenCloud/Tests/Common/Log/LoggerTest.php | 8 ++++++++ 8 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index b6fe7471b..f15e83c47 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -17,6 +17,8 @@ namespace OpenCloud\Common; +use OpenCloud\Common\Log\Logger; + /** * @deprecated * @codeCoverageIgnore @@ -50,8 +52,7 @@ class Collection extends Base */ public function __construct($service, $class, array $array = array()) { - $service->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); + $service->getLogger()->warning(Logger::deprecated(__METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); $this->setService($service); diff --git a/lib/OpenCloud/Common/Collection/ResourceIterator.php b/lib/OpenCloud/Common/Collection/ResourceIterator.php index a95455386..c0054b8d1 100644 --- a/lib/OpenCloud/Common/Collection/ResourceIterator.php +++ b/lib/OpenCloud/Common/Collection/ResourceIterator.php @@ -223,8 +223,7 @@ public function getElement($offset) */ public function first() { - Logger::newInstance()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'getElement')); + Logger::newInstance()->warning(Logger::deprecated(__METHOD__, 'getElement')); return $this->getElement(0); } diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index 56cb7cbf2..b054fc004 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -240,13 +240,11 @@ private function dispatch($message, $context) } /** - * @deprecated use warning for deprecated messages + * Helper method, use PSR-3 warning function for deprecation warnings * @see http://www.php-fig.org/psr/psr-3/ */ - public function deprecated($method, $new) + public static function deprecated($method, $new) { - $string = sprintf('The %s method is deprecated, please use %s instead', $method, $new); - - return $this->warning($string); + return sprintf('The %s method is deprecated, please use %s instead', $method, $new); } } diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 080dbb732..cc3e85339 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -18,6 +18,7 @@ namespace OpenCloud\LoadBalancer\Resource; use OpenCloud\Common\Exceptions; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Resource\PersistentResource; use OpenCloud\DNS\Resource\HasPtrRecordsInterface; use OpenCloud\LoadBalancer\Enum\NodeCondition; @@ -621,8 +622,7 @@ public function enableConnectionLogging($bool) */ public function connectionLogging() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasConnectionLogging or enableConnectionLogging')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'hasConnectionLogging or enableConnectionLogging')); } /** diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index c11022114..93905a0d2 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -17,6 +17,7 @@ namespace OpenCloud\LoadBalancer; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\NovaService; /** @@ -62,8 +63,7 @@ public function loadBalancerList($detail = true, array $filter = array()) */ public function billableLoadBalancer($id = null) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'loadBalancer')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'loadBalancer')); return $this->resource('LoadBalancer', $id); } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index ce3446880..66735dea8 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -24,6 +24,7 @@ use OpenCloud\Common\Exceptions\InvalidArgumentError; use OpenCloud\Common\Http\Client; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\ServiceBuilder; use OpenCloud\ObjectStore\Constants\UrlType; use OpenCloud\ObjectStore\Resource\Container; @@ -174,8 +175,7 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ */ public function bulkDelete(array $paths) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, '::batchDelete()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::batchDelete()')); return $this->executeBatchDeleteRequest($paths); } diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 3511bc4f4..8e177135f 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -23,6 +23,7 @@ use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Http\Message\RequestSubscriber; use OpenCloud\Common\Lang; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\Catalog; use OpenCloud\Common\Service\ServiceBuilder; use OpenCloud\Identity\Resource\Tenant; @@ -165,8 +166,7 @@ public function getTokenObject() */ public function setExpiration($expiration) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->setExpires()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->setExpires()')); if ($this->getTokenObject()) { $this->getTokenObject()->setExpires($expiration); } @@ -179,8 +179,7 @@ public function setExpiration($expiration) */ public function getExpiration() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->getExpires()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->getExpires()')); if ($this->getTokenObject()) { return $this->getTokenObject()->getExpires(); } @@ -292,8 +291,7 @@ public function getLogger() */ public function hasExpired() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'getTokenObject()->hasExpired()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'getTokenObject()->hasExpired()')); return $this->getTokenObject() && $this->getTokenObject()->hasExpired(); } diff --git a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php index da06da6d0..c18f38c62 100644 --- a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php +++ b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php @@ -85,4 +85,12 @@ public function testOutputFailsWithIncorrectFile() $this->logger->emergency('Can anyone see this?'); } + + public function testDeprecationMessage() + { + $this->assertEquals( + 'The OpenCloud\Tests\Common\Log\LoggerTest::testDeprecationMessage method is deprecated, please use testMethod instead', + $this->logger->deprecated(__METHOD__, 'testMethod') + ); + } } From a810f581dbbf731e0954a28280fce9404247d5e7 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 7 Jan 2015 10:47:14 +0100 Subject: [PATCH 520/835] + inject Logger in service when available in Client * allow OpenStack Logger to be set via options array --- lib/OpenCloud/Common/Base.php | 8 +++++ .../Common/Service/CatalogService.php | 6 ++++ lib/OpenCloud/Identity/Service.php | 7 ++++ lib/OpenCloud/OpenStack.php | 12 +++++++ tests/OpenCloud/Tests/OpenStackTest.php | 35 +++++++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/lib/OpenCloud/Common/Base.php b/lib/OpenCloud/Common/Base.php index fd638cc81..82bf0061b 100644 --- a/lib/OpenCloud/Common/Base.php +++ b/lib/OpenCloud/Common/Base.php @@ -266,6 +266,14 @@ public function getLogger() return $this->logger; } + /** + * @return bool + */ + public function hasLogger() + { + return (null !== $this->logger); + } + /** * @deprecated */ diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index addcd170b..69e5c659d 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -20,8 +20,10 @@ use Guzzle\Http\ClientInterface; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Url; +use OpenCloud\Common\Base; use OpenCloud\Common\Exceptions; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\OpenStack; use Symfony\Component\EventDispatcher\EventSubscriberInterface; abstract class CatalogService extends AbstractService @@ -71,6 +73,10 @@ abstract class CatalogService extends AbstractService */ public function __construct(ClientInterface $client, $type = null, $name = null, $region = null, $urlType = null) { + if (($client instanceof Base || $client instanceof OpenStack) && $client->hasLogger()) { + $this->setLogger($client->getLogger()); + } + $this->setClient($client); $this->name = $name ? : static::DEFAULT_NAME; diff --git a/lib/OpenCloud/Identity/Service.php b/lib/OpenCloud/Identity/Service.php index 9c90d2811..4e34f1e24 100644 --- a/lib/OpenCloud/Identity/Service.php +++ b/lib/OpenCloud/Identity/Service.php @@ -18,11 +18,13 @@ namespace OpenCloud\Identity; use Guzzle\Http\ClientInterface; +use OpenCloud\Common\Base; use OpenCloud\Common\Collection\PaginatedIterator; use OpenCloud\Common\Collection\ResourceIterator; use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Service\AbstractService; use OpenCloud\Identity\Constants\User as UserConst; +use OpenCloud\OpenStack; /** * Class responsible for working with Rackspace's Cloud Identity service. @@ -40,6 +42,11 @@ class Service extends AbstractService public static function factory(ClientInterface $client) { $identity = new self(); + + if (($client instanceof Base || $client instanceof OpenStack) && $client->hasLogger()) { + $identity->setLogger($client->getLogger()); + } + $identity->setClient($client); $identity->setEndpoint(clone $client->getAuthUrl()); diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 8e177135f..e970b9912 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -79,6 +79,10 @@ class OpenStack extends Client public function __construct($url, array $secret, array $options = array()) { + if (isset($options['logger']) && $options['logger'] instanceof LoggerInterface) { + $this->setLogger($options['logger']); + } + $this->setSecret($secret); $this->setAuthUrl($url); @@ -286,6 +290,14 @@ public function getLogger() return $this->logger; } + /** + * @return bool + */ + public function hasLogger() + { + return (null !== $this->logger); + } + /** * @deprecated */ diff --git a/tests/OpenCloud/Tests/OpenStackTest.php b/tests/OpenCloud/Tests/OpenStackTest.php index f38f3777b..8ff20892a 100644 --- a/tests/OpenCloud/Tests/OpenStackTest.php +++ b/tests/OpenCloud/Tests/OpenStackTest.php @@ -184,4 +184,39 @@ public function test_Import_Credentials_Numeric_Tenant() $this->assertEquals('{expiration}', $this->client->getExpiration()); $this->assertEquals($randomNumericTenant, $this->client->getTenant()); } + + public function testLoggerServiceInjection() + { + // Create a new client, pass stub via constructor options argument + $stubLogger = $this->getMock('Psr\Log\NullLogger'); + $client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, $this->credentials, array( + 'logger' => $stubLogger, + )); + $client->addSubscriber(new MockSubscriber()); + + // Test all OpenStack factory methods on proper Logger service injection + $service = $client->objectStoreService('cloudFiles', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $service->getCdnService(); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->computeService('cloudServersOpenStack', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->orchestrationService(null, 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->volumeService('cloudBlockStorage', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->identityService(); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->imageService('cloudImages', 'IAD'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->networkingService(null, 'IAD'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + } } From 8f44559e6e7ab6a715b357cdb2951fbcdede9e17 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:22:14 +0100 Subject: [PATCH 521/835] Update getting-started.md added information about injecting `Logger` in the client --- docs/getting-started.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 48f99fb86..f47daf5cf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -55,6 +55,11 @@ You can see in the first example that the constant `Rackspace::US_IDENTITY_ENDPO Rackspace's identity endpoint (`https://identity.api.rackspacecloud.com/v2.0/`). Another difference is that Rackspace uses API key for authentication, whereas OpenStack uses a generic password. +#### 1.2 Logger injection +As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. + +Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. + ### 2. Pick what service you want to use In this case, we want to use the Compute (Nova) service: @@ -175,4 +180,4 @@ $callback = function($server) { $server->waitFor(ServerState::ACTIVE, 600, $callback); ``` So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. \ No newline at end of file +callback function is executed - which in this case just logs some output. From c2ce3449c6c57ff5d0ac1588a266c4a96a66d366 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:26:47 +0100 Subject: [PATCH 522/835] Update Clients.md also add `Logger` injection to Clients userguide --- docs/userguide/Clients.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 9a7efec8e..70f1a8131 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -11,7 +11,7 @@ Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\R 3. `OpenCloud\OpenStack` 4. `OpenCloud\Rackspace` -## Initializing a client +## 1. Initializing a client ### Rackspace @@ -44,7 +44,29 @@ $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( )); ``` -## Authentication +#### 1.2 Logger injection +As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. + +Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. + +To inject a `LoggerInterface` compatible logger into a new `Client`: + +```php +use Monolog\Logger; +use OpenCloud\OpenStack; + +// create a log channel +$log = new Logger('name'); + +$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' +), array( + 'logger' => $log, +)); +``` + +## 2. Authentication The Client does not automatically authenticate against the API on object creation - it waits for an API call. When this happens, it checks whether the current "token" has expired, and (re-)authenticates if necessary. @@ -56,7 +78,7 @@ $client->authenticate(); If the credentials are incorrect, a `401` error will be returned. If credentials are correct, a `200` status is returned with your Service Catalog. -## Service Catalog +## 3. Service Catalog The Service Catalog is returned on successful authentication, and is composed of all the different API services available to the current tenant. All of this functionality is encapsulated in the `Catalog` object, which allows you greater control and interactivity. @@ -86,7 +108,7 @@ foreach ($catalog->getItems() as $catalogItem) { As you can see, you have access to each Service's name, type and list of endpoints. Each endpoint provides access to the specific region, along with its public and private endpoint URLs. -## Default HTTP headers +## 4. Default HTTP headers To set default HTTP headers: @@ -94,6 +116,6 @@ To set default HTTP headers: $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); ``` -## Other functionality +## 5. Other functionality -For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). \ No newline at end of file +For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). From 1f25a26ef3517204266adcc26da73aa9f8633c86 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:28:26 +0100 Subject: [PATCH 523/835] Link getting-started.md to the clients userguide --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index f47daf5cf..e661e0ce8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -58,7 +58,7 @@ uses API key for authentication, whereas OpenStack uses a generic password. #### 1.2 Logger injection As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. -Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. +Prerequisities and usage example can be found in [the Clients userguide](/docs/userguide/Clients.md#12-logger-injection) ### 2. Pick what service you want to use From 31ca4900b7520c5afc7f38a6315c3168833dcb39 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:12:13 +0100 Subject: [PATCH 524/835] Add mock HTTP responses --- .../_response/LoadBalancers1.resp | 80 +++++++++++++++++++ .../_response/LoadBalancers2.resp | 80 +++++++++++++++++++ .../_response/LoadBalancers3.resp | 34 ++++++++ 3 files changed, 194 insertions(+) create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp new file mode 100644 index 000000000..1827f2b44 --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site1", + "id":1, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":3, + "virtualIps":[ + { + "id":403, + "address":"206.55.130.1", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site2", + "id":2, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site3", + "id":3, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp new file mode 100644 index 000000000..2ab7b2381 --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site3", + "id":3, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site4", + "id":4, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site5", + "id":5, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp new file mode 100644 index 000000000..554a4430d --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp @@ -0,0 +1,34 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site5", + "id":5, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file From 7f45e9b0006af341c24955d7001a0a889d13cbac Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:22 +0100 Subject: [PATCH 525/835] Decouple the process of retrieving last element from setting marker --- lib/OpenCloud/Common/Collection/PaginatedIterator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/OpenCloud/Common/Collection/PaginatedIterator.php b/lib/OpenCloud/Common/Collection/PaginatedIterator.php index a0628993d..ef827ac80 100644 --- a/lib/OpenCloud/Common/Collection/PaginatedIterator.php +++ b/lib/OpenCloud/Common/Collection/PaginatedIterator.php @@ -145,7 +145,11 @@ public function updateMarkerToCurrent() } $element = $this->elements[$this->position]; + $this->setMarkerFromElement($element); + } + protected function setMarkerFromElement($element) + { $key = $this->getOption('key.marker'); if (isset($element->$key)) { From b7522f3867b69c266cb12eddc179664e4aea5846 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:39 +0100 Subject: [PATCH 526/835] Add new iterator to handle differences --- .../Collection/LoadBalancerIterator.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php new file mode 100644 index 000000000..f554c6512 --- /dev/null +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -0,0 +1,43 @@ +getQuery(); + $query['limit'] = $query['limit'] + 1; + $url->setQuery($query); + + return $url; + } + + public function updateMarkerToCurrent() + { + $this->setMarkerFromElement($this->nextElement); + } + + public function parseResponseBody($body) + { + $response = parent::parseResponseBody($body); + + if (count($response) >= $this->getOption('limit.page')) { + // Save last element (we will need it for the next marker) + $this->nextElement = end($response); + + // Since we previously asked for n+1 elements, pop the unwanted element + array_pop($response); + reset($response); + } + + return $response; + } +} \ No newline at end of file From 344016d69b281301a34a0c7f168bbb788e699d9e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:51 +0100 Subject: [PATCH 527/835] Implement into service and refactor tests --- lib/OpenCloud/LoadBalancer/Service.php | 12 ++++- .../Tests/LoadBalancer/ServiceTest.php | 48 ++++++++++++++----- tests/OpenCloud/Tests/OpenCloudTestCase.php | 13 ++--- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 93905a0d2..6f5052f2c 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -19,6 +19,7 @@ use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\NovaService; +use OpenCloud\LoadBalancer\Collection\LoadBalancerIterator; /** * Class that encapsulates the Rackspace Cloud Load Balancers service @@ -51,11 +52,20 @@ public function loadBalancer($id = null) */ public function loadBalancerList($detail = true, array $filter = array()) { + $options = $this->makeResourceIteratorOptions($this->resolveResourceClass('LoadBalancer')); + + if (isset($filter['limit'])) { + $options['limit.page'] = $filter['limit']; + unset($filter['limit']); + } + $url = $this->getUrl(); $url->addPath(Resource\LoadBalancer::resourceName()); $url->setQuery($filter); - return $this->resourceList('LoadBalancer', $url); + $options += array('baseUrl' => $url, 'key.marker' => 'id'); + + return LoadBalancerIterator::factory($this, $options); } /** diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 6bd047064..2d01e2f93 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -27,15 +27,13 @@ namespace OpenCloud\Tests\LoadBalancer; -class ServiceTest extends \OpenCloud\Tests\OpenCloudTestCase -{ - private $service; - - public function setupObjects() - { - $this->service = $this->getClient()->loadBalancerService('cloudLoadBalancers', 'DFW', 'publicURL'); - } +use Guzzle\Http\Message\Response; +use Guzzle\Plugin\Mock\MockPlugin; +use OpenCloud\Rackspace; +use OpenCloud\Tests\MockSubscriber; +class ServiceTest extends LoadBalancerTestCase +{ public function test__construct() { $this->assertInstanceOf( @@ -52,12 +50,36 @@ public function testLoadBalancer() ); } - public function testLoadBalancerList() + public function test_Listing_Load_Balancers() { - $this->assertInstanceOf( - self::COLLECTION_CLASS, - $this->service->loadBalancerList() - ); + // Load JSON HTTP data + $authData = file_get_contents($this->getTestFilePath('Auth', './')); + $data1 = file_get_contents($this->getTestFilePath('LoadBalancers1')); + $data2 = file_get_contents($this->getTestFilePath('LoadBalancers2')); + $data3 = file_get_contents($this->getTestFilePath('LoadBalancers3')); + + // Populate mock response queue + $mock = new MockPlugin(); + $mock->addResponse(Response::fromMessage($authData)) + ->addResponse(Response::fromMessage($data1)) + ->addResponse(Response::fromMessage($data2)) + ->addResponse(Response::fromMessage($data3)); + + // We need to define our own setup because *jazz hands* + $client = $this->newClient(); + $client->addSubscriber($mock); + $service = $client->loadBalancerService(null, 'IAD'); + + // Ensure that a series of paginated calls return a holistic collection + $lbs = $service->loadBalancerList(false, array('limit' => 2)); + $ids = array(); + foreach ($lbs as $lb) { + $ids[] = $lb->id; + } + + // Check our assumptions + $this->isCollection($lbs); + $this->assertEquals($ids, array(1,2,3,4,5)); } public function testBillableLoadBalancer() diff --git a/tests/OpenCloud/Tests/OpenCloudTestCase.php b/tests/OpenCloud/Tests/OpenCloudTestCase.php index 628881fbb..8d7582c1f 100644 --- a/tests/OpenCloud/Tests/OpenCloudTestCase.php +++ b/tests/OpenCloud/Tests/OpenCloudTestCase.php @@ -38,17 +38,10 @@ abstract class OpenCloudTestCase extends \PHPUnit_Framework_TestCase public function newClient() { - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + return new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => 'foo', 'apiKey' => 'bar' )); - - $client->addSubscriber(new MockSubscriber()); - //$client->addSubscriber(LogPlugin::getDebugPlugin()); - - $client->authenticate(); - - return $client; } public function getClient() @@ -59,6 +52,10 @@ public function getClient() public function setUp() { $this->client = $this->newClient(); + + $this->client->addSubscriber(new MockSubscriber()); + $this->client->authenticate(); + $this->setupObjects(); $this->handleMockSubscribers(); } From 09da4f7f14882b06bc3b5e67bebbaf74895647fb Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 9 Jan 2015 11:25:08 +0100 Subject: [PATCH 528/835] Appease PSR-2 gods --- lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php | 2 +- tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php index f554c6512..d6326048e 100644 --- a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -40,4 +40,4 @@ public function parseResponseBody($body) return $response; } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 2d01e2f93..9beb0c620 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -79,7 +79,7 @@ public function test_Listing_Load_Balancers() // Check our assumptions $this->isCollection($lbs); - $this->assertEquals($ids, array(1,2,3,4,5)); + $this->assertEquals($ids, array(1, 2, 3, 4, 5)); } public function testBillableLoadBalancer() From 9d431709f97be2df6b5b26bffe2bf0e68dd2b569 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 9 Jan 2015 11:32:29 +0100 Subject: [PATCH 529/835] Attempt to fix 5.3 segfault --- doc/_build/doctrees/auth.doctree | Bin 0 -> 7264 bytes doc/_build/doctrees/caching-creds.doctree | Bin 0 -> 5935 bytes .../doctrees/customizing-clients.doctree | Bin 0 -> 20271 bytes doc/_build/doctrees/debugging.doctree | Bin 0 -> 14033 bytes doc/_build/doctrees/environment.pickle | Bin 0 -> 289822 bytes .../getting-started-with-openstack.doctree | Bin 0 -> 24424 bytes .../getting-started-with-rackspace.doctree | Bin 0 -> 20899 bytes doc/_build/doctrees/http-clients.doctree | Bin 0 -> 9751 bytes doc/_build/doctrees/index.doctree | Bin 0 -> 14297 bytes doc/_build/doctrees/iterators.doctree | Bin 0 -> 44714 bytes doc/_build/doctrees/logging.doctree | Bin 0 -> 7286 bytes doc/_build/doctrees/regions.doctree | Bin 0 -> 6172 bytes .../services/autoscale/group-config.doctree | Bin 0 -> 6395 bytes .../services/autoscale/groups.doctree | Bin 0 -> 7417 bytes .../doctrees/services/autoscale/index.doctree | Bin 0 -> 18878 bytes .../services/autoscale/policies.doctree | Bin 0 -> 8187 bytes .../services/autoscale/service.sample.doctree | Bin 0 -> 8073 bytes .../services/autoscale/webhooks.doctree | Bin 0 -> 6218 bytes .../services/common/clients.sample.doctree | Bin 0 -> 4522 bytes .../services/common/os-client.sample.doctree | Bin 0 -> 2801 bytes .../services/common/rs-client.doctree | Bin 0 -> 2983 bytes .../services/common/rs-client.sample.doctree | Bin 0 -> 4251 bytes .../services/common/rs-only.sample.doctree | Bin 0 -> 3611 bytes .../services/common/service-args.doctree | Bin 0 -> 6027 bytes .../services/compute/Images.md.doctree | Bin 0 -> 20348 bytes .../services/compute/Keypair.md.doctree | Bin 0 -> 12007 bytes .../services/compute/Server.md.doctree | Bin 0 -> 55723 bytes .../services/compute/Service.md.doctree | Bin 0 -> 6867 bytes .../doctrees/services/compute/flavors.doctree | Bin 0 -> 12184 bytes .../doctrees/services/compute/images.doctree | Bin 0 -> 18732 bytes .../doctrees/services/compute/index.doctree | Bin 0 -> 17348 bytes .../doctrees/services/compute/keypair.doctree | Bin 0 -> 13233 bytes .../services/compute/keypairs.doctree | Bin 0 -> 8422 bytes .../doctrees/services/compute/server.doctree | Bin 0 -> 52060 bytes .../doctrees/services/compute/servers.doctree | Bin 0 -> 55100 bytes .../services/compute/service.sample.doctree | Bin 0 -> 8147 bytes .../services/database/README.md.doctree | Bin 0 -> 17730 bytes .../services/database/configurations.doctree | Bin 0 -> 16196 bytes .../services/database/databases.doctree | Bin 0 -> 7651 bytes .../services/database/datastores.doctree | Bin 0 -> 8349 bytes .../doctrees/services/database/index.doctree | Bin 0 -> 21373 bytes .../services/database/instances.doctree | Bin 0 -> 17933 bytes .../services/database/service.sample.doctree | Bin 0 -> 8146 bytes .../doctrees/services/database/users.doctree | Bin 0 -> 8793 bytes .../doctrees/services/dns/Domains.md.doctree | Bin 0 -> 42004 bytes .../doctrees/services/dns/Limits.md.doctree | Bin 0 -> 8041 bytes .../doctrees/services/dns/Records.md.doctree | Bin 0 -> 13680 bytes .../services/dns/Reverse-DNS.md.doctree | Bin 0 -> 16504 bytes .../doctrees/services/dns/Service.md.doctree | Bin 0 -> 3823 bytes .../doctrees/services/dns/domains.doctree | Bin 0 -> 39316 bytes .../doctrees/services/dns/index.doctree | Bin 0 -> 13517 bytes .../doctrees/services/dns/limits.doctree | Bin 0 -> 6702 bytes .../doctrees/services/dns/records.doctree | Bin 0 -> 15192 bytes .../doctrees/services/dns/reverse-dns.doctree | Bin 0 -> 15276 bytes .../services/identity/Roles.md.doctree | Bin 0 -> 14598 bytes .../services/identity/Service.md.doctree | Bin 0 -> 3531 bytes .../services/identity/Tenants.md.doctree | Bin 0 -> 5239 bytes .../services/identity/Tokens.md.doctree | Bin 0 -> 22851 bytes .../services/identity/Users.md.doctree | Bin 0 -> 31154 bytes .../doctrees/services/identity/index.doctree | Bin 0 -> 12611 bytes .../doctrees/services/identity/roles.doctree | Bin 0 -> 10991 bytes .../services/identity/tenants.doctree | Bin 0 -> 4156 bytes .../doctrees/services/identity/tokens.doctree | Bin 0 -> 14819 bytes .../doctrees/services/identity/users.doctree | Bin 0 -> 34801 bytes .../doctrees/services/image/Images.md.doctree | Bin 0 -> 17718 bytes .../services/image/Schemas.md.doctree | Bin 0 -> 67636 bytes .../services/image/Sharing.md.doctree | Bin 0 -> 23296 bytes .../doctrees/services/image/Tags.md.doctree | Bin 0 -> 5065 bytes .../doctrees/services/image/images.doctree | Bin 0 -> 12799 bytes .../doctrees/services/image/index.doctree | Bin 0 -> 12154 bytes .../doctrees/services/image/schemas.doctree | Bin 0 -> 19484 bytes .../doctrees/services/image/sharing.doctree | Bin 0 -> 21762 bytes .../doctrees/services/image/tags.doctree | Bin 0 -> 4579 bytes doc/_build/doctrees/services/index.doctree | Bin 0 -> 2457 bytes .../services/load-balancer/README.md.doctree | Bin 0 -> 14555 bytes .../load-balancer/USERGUIDE.md.doctree | Bin 0 -> 126207 bytes .../services/load-balancer/access.doctree | Bin 0 -> 12999 bytes .../services/load-balancer/caching.doctree | Bin 0 -> 5602 bytes .../services/load-balancer/errors.doctree | Bin 0 -> 6544 bytes .../services/load-balancer/index.doctree | Bin 0 -> 23842 bytes .../load-balancer/lb-setup.sample.doctree | Bin 0 -> 2998 bytes .../load-balancer/load-balancer.doctree | Bin 0 -> 29537 bytes .../services/load-balancer/logging.doctree | Bin 0 -> 5495 bytes .../services/load-balancer/metadata.doctree | Bin 0 -> 5650 bytes .../services/load-balancer/monitors.doctree | Bin 0 -> 6366 bytes .../services/load-balancer/nodes.doctree | Bin 0 -> 26574 bytes .../services/load-balancer/sessions.doctree | Bin 0 -> 11150 bytes .../services/load-balancer/ssl.doctree | Bin 0 -> 8684 bytes .../services/load-balancer/stats.doctree | Bin 0 -> 11269 bytes .../load-balancer/virtual-ips.doctree | Bin 0 -> 9942 bytes .../services/monitoring/Agents.md.doctree | Bin 0 -> 32106 bytes .../services/monitoring/Alarms.md.doctree | Bin 0 -> 17820 bytes .../services/monitoring/Changelogs.md.doctree | Bin 0 -> 3997 bytes .../services/monitoring/Checks.md.doctree | Bin 0 -> 43610 bytes .../services/monitoring/Entities.md.doctree | Bin 0 -> 15807 bytes .../services/monitoring/Metrics.md.doctree | Bin 0 -> 25023 bytes .../monitoring/Notifications.md.doctree | Bin 0 -> 46791 bytes .../services/monitoring/Service.md.doctree | Bin 0 -> 6402 bytes .../services/monitoring/Views.md.doctree | Bin 0 -> 4937 bytes .../services/monitoring/Zones.md.doctree | Bin 0 -> 12439 bytes .../services/monitoring/agents.doctree | Bin 0 -> 21855 bytes .../services/monitoring/alarms.doctree | Bin 0 -> 19180 bytes .../services/monitoring/changelogs.doctree | Bin 0 -> 3550 bytes .../services/monitoring/checks.doctree | Bin 0 -> 43494 bytes .../services/monitoring/entities.doctree | Bin 0 -> 13126 bytes .../services/monitoring/index.doctree | Bin 0 -> 23381 bytes .../services/monitoring/metrics.doctree | Bin 0 -> 25744 bytes .../services/monitoring/notifications.doctree | Bin 0 -> 46340 bytes .../services/monitoring/views.doctree | Bin 0 -> 3821 bytes .../services/monitoring/zones.doctree | Bin 0 -> 10582 bytes .../services/networking/README.md.doctree | Bin 0 -> 25466 bytes .../services/networking/USERGUIDE.md.doctree | Bin 0 -> 153161 bytes .../services/networking/index.doctree | Bin 0 -> 19923 bytes .../services/networking/networks.doctree | Bin 0 -> 32322 bytes .../services/networking/ports.doctree | Bin 0 -> 46546 bytes .../networking/security-group-rules.doctree | Bin 0 -> 27849 bytes .../networking/security-groups.doctree | Bin 0 -> 16954 bytes .../services/networking/subnets.doctree | Bin 0 -> 46302 bytes .../services/object-store/Access.md.doctree | Bin 0 -> 11197 bytes .../services/object-store/Account.md.doctree | Bin 0 -> 4725 bytes .../object-store/Container.md.cdn.doctree | Bin 0 -> 10380 bytes .../object-store/Container.md.storage.doctree | Bin 0 -> 32544 bytes .../object-store/Migrating.md.storage.doctree | Bin 0 -> 20597 bytes .../object-store/Object.md.cdn.doctree | Bin 0 -> 5068 bytes .../object-store/Object.md.storage.doctree | Bin 0 -> 42440 bytes .../services/object-store/README.md.doctree | Bin 0 -> 15195 bytes .../object-store/USERGUIDE.md.doctree | Bin 0 -> 157393 bytes .../services/object-store/access.doctree | Bin 0 -> 15118 bytes .../services/object-store/account.doctree | Bin 0 -> 10549 bytes .../services/object-store/cdn.doctree | Bin 0 -> 16110 bytes .../services/object-store/containers.doctree | Bin 0 -> 41332 bytes .../services/object-store/index.doctree | Bin 0 -> 19400 bytes .../object-store/migrating-containers.doctree | Bin 0 -> 19256 bytes .../services/object-store/objects.doctree | Bin 0 -> 54948 bytes .../services/object-store/rs-only.doctree | Bin 0 -> 2418 bytes .../services/orchestration/README.md.doctree | Bin 0 -> 19360 bytes .../orchestration/USERGUIDE.md.doctree | Bin 0 -> 133933 bytes .../services/orchestration/build-info.doctree | Bin 0 -> 4332 bytes .../services/orchestration/events.doctree | Bin 0 -> 9280 bytes .../services/orchestration/index.doctree | Bin 0 -> 17915 bytes .../orchestration/resource-types.doctree | Bin 0 -> 9052 bytes .../services/orchestration/resources.doctree | Bin 0 -> 9009 bytes .../services/orchestration/stacks.doctree | Bin 0 -> 70371 bytes .../services/orchestration/templates.doctree | Bin 0 -> 9513 bytes .../doctrees/services/queues/Claim.md.doctree | Bin 0 -> 21398 bytes .../services/queues/Message.md.doctree | Bin 0 -> 37520 bytes .../doctrees/services/queues/Queue.md.doctree | Bin 0 -> 26480 bytes .../doctrees/services/queues/claims.doctree | Bin 0 -> 16599 bytes .../doctrees/services/queues/index.doctree | Bin 0 -> 17402 bytes .../doctrees/services/queues/messages.doctree | Bin 0 -> 38569 bytes .../doctrees/services/queues/queues.doctree | Bin 0 -> 19485 bytes .../doctrees/services/volume/index.doctree | Bin 0 -> 17493 bytes .../services/volume/snapshots.doctree | Bin 0 -> 10012 bytes .../services/volume/volume-types.doctree | Bin 0 -> 5676 bytes .../doctrees/services/volume/volumes.doctree | Bin 0 -> 13693 bytes doc/_build/doctrees/url-types.doctree | Bin 0 -> 3554 bytes doc/_build/doctrees/using-php-5.3.doctree | Bin 0 -> 4656 bytes doc/_build/html/.buildinfo | 4 + doc/_build/html/_sources/auth.txt | 54 + doc/_build/html/_sources/caching-creds.txt | 44 + .../html/_sources/customizing-clients.txt | 144 +++ doc/_build/html/_sources/debugging.txt | 103 ++ .../getting-started-with-openstack.txt | 233 ++++ .../getting-started-with-rackspace.txt | 185 +++ doc/_build/html/_sources/http-clients.txt | 56 + doc/_build/html/_sources/index.txt | 80 ++ doc/_build/html/_sources/iterators.txt | 174 +++ doc/_build/html/_sources/logging.txt | 33 + doc/_build/html/_sources/regions.txt | 20 + .../services/autoscale/group-config.txt | 59 + .../_sources/services/autoscale/groups.txt | 81 ++ .../_sources/services/autoscale/index.txt | 69 ++ .../_sources/services/autoscale/policies.txt | 81 ++ .../services/autoscale/service.sample.txt | 9 + .../_sources/services/autoscale/webhooks.txt | 62 + .../services/common/clients.sample.txt | 22 + .../services/common/os-client.sample.txt | 9 + .../services/common/rs-client.sample.txt | 29 + .../_sources/services/common/rs-client.txt | 11 + .../services/common/rs-only.sample.txt | 8 + .../_sources/services/common/service-args.txt | 11 + .../_sources/services/compute/Images.md.txt | 98 ++ .../_sources/services/compute/Keypair.md.txt | 103 ++ .../_sources/services/compute/Server.md.txt | 242 ++++ .../_sources/services/compute/Service.md.txt | 27 + .../_sources/services/compute/flavors.txt | 62 + .../html/_sources/services/compute/images.txt | 85 ++ .../html/_sources/services/compute/index.txt | 55 + .../_sources/services/compute/keypair.txt | 71 ++ .../_sources/services/compute/keypairs.txt | 70 ++ .../html/_sources/services/compute/server.txt | 202 ++++ .../_sources/services/compute/servers.txt | 250 ++++ .../services/compute/service.sample.txt | 9 + .../_sources/services/database/README.md.txt | 125 ++ .../services/database/configurations.txt | 127 ++ .../_sources/services/database/databases.txt | 60 + .../_sources/services/database/datastores.txt | 59 + .../html/_sources/services/database/index.txt | 77 ++ .../_sources/services/database/instances.txt | 155 +++ .../services/database/service.sample.txt | 9 + .../html/_sources/services/database/users.txt | 67 ++ .../html/_sources/services/dns/Domains.md.txt | 290 +++++ .../html/_sources/services/dns/Limits.md.txt | 70 ++ .../html/_sources/services/dns/Records.md.txt | 111 ++ .../_sources/services/dns/Reverse-DNS.md.txt | 96 ++ .../html/_sources/services/dns/Service.md.txt | 13 + .../html/_sources/services/dns/domains.txt | 288 +++++ .../html/_sources/services/dns/index.txt | 59 + .../html/_sources/services/dns/limits.txt | 57 + .../html/_sources/services/dns/records.txt | 113 ++ .../_sources/services/dns/reverse-dns.txt | 92 ++ .../_sources/services/identity/Roles.md.txt | 92 ++ .../_sources/services/identity/Service.md.txt | 13 + .../_sources/services/identity/Tenants.md.txt | 29 + .../_sources/services/identity/Tokens.md.txt | 105 ++ .../_sources/services/identity/Users.md.txt | 170 +++ .../html/_sources/services/identity/index.txt | 55 + .../html/_sources/services/identity/roles.txt | 78 ++ .../_sources/services/identity/tenants.txt | 26 + .../_sources/services/identity/tokens.txt | 101 ++ .../html/_sources/services/identity/users.txt | 175 +++ .../_sources/services/image/Images.md.txt | 107 ++ .../_sources/services/image/Schemas.md.txt | 170 +++ .../_sources/services/image/Sharing.md.txt | 129 +++ .../html/_sources/services/image/Tags.md.txt | 28 + .../html/_sources/services/image/images.txt | 84 ++ .../html/_sources/services/image/index.txt | 50 + .../html/_sources/services/image/schemas.txt | 163 +++ .../html/_sources/services/image/sharing.txt | 127 ++ .../html/_sources/services/image/tags.txt | 29 + doc/_build/html/_sources/services/index.txt | 4 + .../services/load-balancer/README.md.txt | 92 ++ .../services/load-balancer/USERGUIDE.md.txt | 840 ++++++++++++++ .../services/load-balancer/access.txt | 84 ++ .../services/load-balancer/caching.txt | 35 + .../services/load-balancer/errors.txt | 44 + .../_sources/services/load-balancer/index.txt | 96 ++ .../load-balancer/lb-setup.sample.txt | 9 + .../services/load-balancer/load-balancer.txt | 171 +++ .../services/load-balancer/logging.txt | 33 + .../services/load-balancer/metadata.txt | 46 + .../services/load-balancer/monitors.txt | 43 + .../_sources/services/load-balancer/nodes.txt | 124 ++ .../services/load-balancer/sessions.txt | 53 + .../_sources/services/load-balancer/ssl.txt | 54 + .../_sources/services/load-balancer/stats.txt | 59 + .../services/load-balancer/virtual-ips.txt | 74 ++ .../services/monitoring/Agents.md.txt | 202 ++++ .../services/monitoring/Alarms.md.txt | 81 ++ .../services/monitoring/Changelogs.md.txt | 21 + .../services/monitoring/Checks.md.txt | 211 ++++ .../services/monitoring/Entities.md.txt | 77 ++ .../services/monitoring/Metrics.md.txt | 131 +++ .../services/monitoring/Notifications.md.txt | 281 +++++ .../services/monitoring/Service.md.txt | 23 + .../_sources/services/monitoring/Views.md.txt | 27 + .../_sources/services/monitoring/Zones.md.txt | 67 ++ .../_sources/services/monitoring/agents.txt | 188 +++ .../_sources/services/monitoring/alarms.txt | 99 ++ .../services/monitoring/changelogs.txt | 18 + .../_sources/services/monitoring/checks.txt | 228 ++++ .../_sources/services/monitoring/entities.txt | 77 ++ .../_sources/services/monitoring/index.txt | 97 ++ .../_sources/services/monitoring/metrics.txt | 143 +++ .../services/monitoring/notifications.txt | 319 +++++ .../_sources/services/monitoring/views.txt | 21 + .../_sources/services/monitoring/zones.txt | 57 + .../services/networking/README.md.txt | 108 ++ .../services/networking/USERGUIDE.md.txt | 582 ++++++++++ .../_sources/services/networking/index.txt | 68 ++ .../_sources/services/networking/networks.txt | 128 +++ .../_sources/services/networking/ports.txt | 150 +++ .../networking/security-group-rules.txt | 61 + .../services/networking/security-groups.txt | 67 ++ .../_sources/services/networking/subnets.txt | 152 +++ .../services/object-store/Access.md.txt | 75 ++ .../services/object-store/Account.md.txt | 33 + .../object-store/Container.md.cdn.txt | 90 ++ .../object-store/Container.md.storage.txt | 218 ++++ .../object-store/Migrating.md.storage.txt | 101 ++ .../services/object-store/Object.md.cdn.txt | 25 + .../object-store/Object.md.storage.txt | 330 ++++++ .../services/object-store/README.md.txt | 86 ++ .../services/object-store/USERGUIDE.md.txt | 900 +++++++++++++++ .../_sources/services/object-store/access.txt | 93 ++ .../services/object-store/account.txt | 52 + .../_sources/services/object-store/cdn.txt | 130 +++ .../services/object-store/containers.txt | 247 ++++ .../_sources/services/object-store/index.txt | 65 ++ .../object-store/migrating-containers.txt | 111 ++ .../services/object-store/objects.txt | 449 ++++++++ .../services/object-store/rs-only.txt | 3 + .../services/orchestration/README.md.txt | 98 ++ .../services/orchestration/USERGUIDE.md.txt | 672 +++++++++++ .../services/orchestration/build-info.txt | 15 + .../services/orchestration/events.txt | 53 + .../_sources/services/orchestration/index.txt | 59 + .../services/orchestration/resource-types.txt | 48 + .../services/orchestration/resources.txt | 50 + .../services/orchestration/stacks.txt | 299 +++++ .../services/orchestration/templates.txt | 55 + .../_sources/services/queues/Claim.md.txt | 164 +++ .../_sources/services/queues/Message.md.txt | 257 +++++ .../_sources/services/queues/Queue.md.txt | 197 ++++ .../html/_sources/services/queues/claims.txt | 121 ++ .../html/_sources/services/queues/index.txt | 56 + .../_sources/services/queues/messages.txt | 203 ++++ .../html/_sources/services/queues/queues.txt | 135 +++ .../html/_sources/services/volume/index.txt | 51 + .../_sources/services/volume/snapshots.txt | 56 + .../_sources/services/volume/volume-types.txt | 30 + .../html/_sources/services/volume/volumes.txt | 88 ++ doc/_build/html/_sources/url-types.txt | 15 + doc/_build/html/_sources/using-php-5.3.txt | 20 + doc/_build/html/_static/ajax-loader.gif | Bin 0 -> 673 bytes doc/_build/html/_static/basic.css | 537 +++++++++ doc/_build/html/_static/comment-bright.png | Bin 0 -> 3500 bytes doc/_build/html/_static/comment-close.png | Bin 0 -> 3578 bytes doc/_build/html/_static/comment.png | Bin 0 -> 3445 bytes doc/_build/html/_static/css/badge_only.css | 1 + doc/_build/html/_static/css/theme.css | 4 + doc/_build/html/_static/default.css | 256 +++++ doc/_build/html/_static/doctools.js | 238 ++++ doc/_build/html/_static/down-pressed.png | Bin 0 -> 368 bytes doc/_build/html/_static/down.png | Bin 0 -> 363 bytes doc/_build/html/_static/file.png | Bin 0 -> 392 bytes .../_static/fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes .../_static/fonts/fontawesome-webfont.svg | 414 +++++++ .../_static/fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes .../_static/fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes doc/_build/html/_static/jquery.js | 2 + doc/_build/html/_static/js/theme.js | 47 + doc/_build/html/_static/minus.png | Bin 0 -> 199 bytes doc/_build/html/_static/plus.png | Bin 0 -> 199 bytes doc/_build/html/_static/pygments.css | 63 + doc/_build/html/_static/searchtools.js | 622 ++++++++++ doc/_build/html/_static/sidebar.js | 159 +++ doc/_build/html/_static/underscore.js | 31 + doc/_build/html/_static/up-pressed.png | Bin 0 -> 372 bytes doc/_build/html/_static/up.png | Bin 0 -> 363 bytes doc/_build/html/_static/websupport.js | 808 +++++++++++++ doc/_build/html/auth.html | 334 ++++++ doc/_build/html/caching-creds.html | 330 ++++++ doc/_build/html/customizing-clients.html | 398 +++++++ doc/_build/html/debugging.html | 375 ++++++ doc/_build/html/genindex.html | 615 ++++++++++ .../html/getting-started-with-openstack.html | 469 ++++++++ .../html/getting-started-with-rackspace.html | 435 +++++++ doc/_build/html/http-clients.html | 332 ++++++ doc/_build/html/index.html | 365 ++++++ doc/_build/html/iterators.html | 515 +++++++++ doc/_build/html/logging.html | 320 ++++++ doc/_build/html/objects.inv | Bin 0 -> 686 bytes doc/_build/html/regions.html | 325 ++++++ doc/_build/html/search.html | 297 +++++ doc/_build/html/searchindex.js | 1 + .../html/services/autoscale/group-config.html | 343 ++++++ .../html/services/autoscale/groups.html | 367 ++++++ doc/_build/html/services/autoscale/index.html | 409 +++++++ .../html/services/autoscale/policies.html | 361 ++++++ .../services/autoscale/service.sample.html | 211 ++++ .../html/services/autoscale/webhooks.html | 345 ++++++ .../html/services/common/clients.sample.html | 307 +++++ .../services/common/os-client.sample.html | 237 ++++ .../html/services/common/rs-client.html | 289 +++++ .../services/common/rs-client.sample.html | 254 ++++ .../html/services/common/rs-only.sample.html | 296 +++++ .../html/services/common/service-args.html | 289 +++++ .../html/services/compute/Images.md.html | 253 ++++ .../html/services/compute/Keypair.md.html | 242 ++++ .../html/services/compute/Server.md.html | 447 ++++++++ .../html/services/compute/Service.md.html | 186 +++ doc/_build/html/services/compute/flavors.html | 358 ++++++ doc/_build/html/services/compute/images.html | 380 ++++++ doc/_build/html/services/compute/index.html | 419 +++++++ doc/_build/html/services/compute/keypair.html | 246 ++++ .../html/services/compute/keypairs.html | 351 ++++++ doc/_build/html/services/compute/server.html | 443 +++++++ doc/_build/html/services/compute/servers.html | 580 ++++++++++ .../html/services/compute/service.sample.html | 211 ++++ .../html/services/database/README.md.html | 274 +++++ .../services/database/configurations.html | 381 ++++++ .../html/services/database/databases.html | 344 ++++++ .../html/services/database/datastores.html | 338 ++++++ doc/_build/html/services/database/index.html | 424 +++++++ .../html/services/database/instances.html | 422 +++++++ .../services/database/service.sample.html | 211 ++++ doc/_build/html/services/database/users.html | 347 ++++++ doc/_build/html/services/dns/Domains.md.html | 451 ++++++++ doc/_build/html/services/dns/Limits.md.html | 221 ++++ doc/_build/html/services/dns/Records.md.html | 245 ++++ .../html/services/dns/Reverse-DNS.md.html | 268 +++++ doc/_build/html/services/dns/Service.md.html | 171 +++ doc/_build/html/services/dns/domains.html | 545 +++++++++ doc/_build/html/services/dns/index.html | 404 +++++++ doc/_build/html/services/dns/limits.html | 340 ++++++ doc/_build/html/services/dns/records.html | 402 +++++++ doc/_build/html/services/dns/reverse-dns.html | 394 +++++++ .../html/services/identity/Roles.md.html | 273 +++++ .../html/services/identity/Service.md.html | 194 ++++ .../html/services/identity/Tenants.md.html | 201 ++++ .../html/services/identity/Tokens.md.html | 287 +++++ .../html/services/identity/Users.md.html | 358 ++++++ doc/_build/html/services/identity/index.html | 395 +++++++ doc/_build/html/services/identity/roles.html | 364 ++++++ .../html/services/identity/tenants.html | 317 +++++ doc/_build/html/services/identity/tokens.html | 379 ++++++ doc/_build/html/services/identity/users.html | 464 ++++++++ doc/_build/html/services/image/Images.md.html | 273 +++++ .../html/services/image/Schemas.md.html | 331 ++++++ .../html/services/image/Sharing.md.html | 311 +++++ doc/_build/html/services/image/Tags.md.html | 205 ++++ doc/_build/html/services/image/images.html | 366 ++++++ doc/_build/html/services/image/index.html | 397 +++++++ doc/_build/html/services/image/schemas.html | 440 +++++++ doc/_build/html/services/image/sharing.html | 417 +++++++ doc/_build/html/services/image/tags.html | 318 +++++ doc/_build/html/services/index.html | 177 +++ .../services/load-balancer/README.md.html | 262 +++++ .../services/load-balancer/USERGUIDE.md.html | 959 ++++++++++++++++ .../html/services/load-balancer/access.html | 368 ++++++ .../html/services/load-balancer/caching.html | 327 ++++++ .../html/services/load-balancer/errors.html | 336 ++++++ .../html/services/load-balancer/index.html | 493 ++++++++ .../load-balancer/lb-setup.sample.html | 287 +++++ .../services/load-balancer/load-balancer.html | 433 +++++++ .../html/services/load-balancer/logging.html | 326 ++++++ .../html/services/load-balancer/metadata.html | 339 ++++++ .../html/services/load-balancer/monitors.html | 337 ++++++ .../html/services/load-balancer/nodes.html | 415 +++++++ .../html/services/load-balancer/sessions.html | 357 ++++++ .../html/services/load-balancer/ssl.html | 344 ++++++ .../html/services/load-balancer/stats.html | 354 ++++++ .../services/load-balancer/virtual-ips.html | 354 ++++++ .../html/services/monitoring/Agents.md.html | 439 +++++++ .../html/services/monitoring/Alarms.md.html | 286 +++++ .../services/monitoring/Changelogs.md.html | 200 ++++ .../html/services/monitoring/Checks.md.html | 456 ++++++++ .../html/services/monitoring/Entities.md.html | 283 +++++ .../html/services/monitoring/Metrics.md.html | 347 ++++++ .../services/monitoring/Notifications.md.html | 513 +++++++++ .../html/services/monitoring/Service.md.html | 203 ++++ .../html/services/monitoring/Views.md.html | 203 ++++ .../html/services/monitoring/Zones.md.html | 263 +++++ .../html/services/monitoring/agents.html | 470 ++++++++ .../html/services/monitoring/alarms.html | 407 +++++++ .../html/services/monitoring/changelogs.html | 309 +++++ .../html/services/monitoring/checks.html | 570 +++++++++ .../html/services/monitoring/entities.html | 386 +++++++ .../html/services/monitoring/index.html | 517 +++++++++ .../html/services/monitoring/metrics.html | 465 ++++++++ .../services/monitoring/notifications.html | 628 ++++++++++ .../html/services/monitoring/views.html | 312 +++++ .../html/services/monitoring/zones.html | 366 ++++++ .../html/services/networking/README.md.html | 310 +++++ .../services/networking/USERGUIDE.md.html | 1021 +++++++++++++++++ .../html/services/networking/index.html | 422 +++++++ .../html/services/networking/networks.html | 465 ++++++++ .../html/services/networking/ports.html | 529 +++++++++ .../networking/security-group-rules.html | 410 +++++++ .../services/networking/security-groups.html | 378 ++++++ .../html/services/networking/subnets.html | 538 +++++++++ .../html/services/object-store/Access.md.html | 246 ++++ .../services/object-store/Account.md.html | 211 ++++ .../object-store/Container.md.cdn.html | 257 +++++ .../object-store/Container.md.storage.html | 393 +++++++ .../object-store/Migrating.md.storage.html | 289 +++++ .../services/object-store/Object.md.cdn.html | 201 ++++ .../object-store/Object.md.storage.html | 480 ++++++++ .../html/services/object-store/README.md.html | 259 +++++ .../services/object-store/USERGUIDE.md.html | 1014 ++++++++++++++++ .../html/services/object-store/access.html | 373 ++++++ .../html/services/object-store/account.html | 333 ++++++ .../html/services/object-store/cdn.html | 394 +++++++ .../services/object-store/containers.html | 513 +++++++++ .../html/services/object-store/index.html | 465 ++++++++ .../object-store/migrating-containers.html | 405 +++++++ .../html/services/object-store/objects.html | 644 +++++++++++ .../html/services/object-store/rs-only.html | 283 +++++ .../services/orchestration/README.md.html | 270 +++++ .../services/orchestration/USERGUIDE.md.html | 921 +++++++++++++++ .../services/orchestration/build-info.html | 306 +++++ .../html/services/orchestration/events.html | 334 ++++++ .../html/services/orchestration/index.html | 426 +++++++ .../orchestration/resource-types.html | 330 ++++++ .../services/orchestration/resources.html | 331 ++++++ .../html/services/orchestration/stacks.html | 667 +++++++++++ .../services/orchestration/templates.html | 340 ++++++ doc/_build/html/services/queues/Claim.md.html | 323 ++++++ .../html/services/queues/Message.md.html | 430 +++++++ doc/_build/html/services/queues/Queue.md.html | 353 ++++++ doc/_build/html/services/queues/claims.html | 392 +++++++ doc/_build/html/services/queues/index.html | 418 +++++++ doc/_build/html/services/queues/messages.html | 463 ++++++++ doc/_build/html/services/queues/queues.html | 390 +++++++ doc/_build/html/services/volume/index.html | 388 +++++++ .../html/services/volume/snapshots.html | 338 ++++++ .../html/services/volume/volume-types.html | 320 ++++++ doc/_build/html/services/volume/volumes.html | 364 ++++++ doc/_build/html/url-types.html | 303 +++++ doc/_build/html/using-php-5.3.html | 294 +++++ phpunit.xml.dist | 4 +- 502 files changed, 79965 insertions(+), 2 deletions(-) create mode 100644 doc/_build/doctrees/auth.doctree create mode 100644 doc/_build/doctrees/caching-creds.doctree create mode 100644 doc/_build/doctrees/customizing-clients.doctree create mode 100644 doc/_build/doctrees/debugging.doctree create mode 100644 doc/_build/doctrees/environment.pickle create mode 100644 doc/_build/doctrees/getting-started-with-openstack.doctree create mode 100644 doc/_build/doctrees/getting-started-with-rackspace.doctree create mode 100644 doc/_build/doctrees/http-clients.doctree create mode 100644 doc/_build/doctrees/index.doctree create mode 100644 doc/_build/doctrees/iterators.doctree create mode 100644 doc/_build/doctrees/logging.doctree create mode 100644 doc/_build/doctrees/regions.doctree create mode 100644 doc/_build/doctrees/services/autoscale/group-config.doctree create mode 100644 doc/_build/doctrees/services/autoscale/groups.doctree create mode 100644 doc/_build/doctrees/services/autoscale/index.doctree create mode 100644 doc/_build/doctrees/services/autoscale/policies.doctree create mode 100644 doc/_build/doctrees/services/autoscale/service.sample.doctree create mode 100644 doc/_build/doctrees/services/autoscale/webhooks.doctree create mode 100644 doc/_build/doctrees/services/common/clients.sample.doctree create mode 100644 doc/_build/doctrees/services/common/os-client.sample.doctree create mode 100644 doc/_build/doctrees/services/common/rs-client.doctree create mode 100644 doc/_build/doctrees/services/common/rs-client.sample.doctree create mode 100644 doc/_build/doctrees/services/common/rs-only.sample.doctree create mode 100644 doc/_build/doctrees/services/common/service-args.doctree create mode 100644 doc/_build/doctrees/services/compute/Images.md.doctree create mode 100644 doc/_build/doctrees/services/compute/Keypair.md.doctree create mode 100644 doc/_build/doctrees/services/compute/Server.md.doctree create mode 100644 doc/_build/doctrees/services/compute/Service.md.doctree create mode 100644 doc/_build/doctrees/services/compute/flavors.doctree create mode 100644 doc/_build/doctrees/services/compute/images.doctree create mode 100644 doc/_build/doctrees/services/compute/index.doctree create mode 100644 doc/_build/doctrees/services/compute/keypair.doctree create mode 100644 doc/_build/doctrees/services/compute/keypairs.doctree create mode 100644 doc/_build/doctrees/services/compute/server.doctree create mode 100644 doc/_build/doctrees/services/compute/servers.doctree create mode 100644 doc/_build/doctrees/services/compute/service.sample.doctree create mode 100644 doc/_build/doctrees/services/database/README.md.doctree create mode 100644 doc/_build/doctrees/services/database/configurations.doctree create mode 100644 doc/_build/doctrees/services/database/databases.doctree create mode 100644 doc/_build/doctrees/services/database/datastores.doctree create mode 100644 doc/_build/doctrees/services/database/index.doctree create mode 100644 doc/_build/doctrees/services/database/instances.doctree create mode 100644 doc/_build/doctrees/services/database/service.sample.doctree create mode 100644 doc/_build/doctrees/services/database/users.doctree create mode 100644 doc/_build/doctrees/services/dns/Domains.md.doctree create mode 100644 doc/_build/doctrees/services/dns/Limits.md.doctree create mode 100644 doc/_build/doctrees/services/dns/Records.md.doctree create mode 100644 doc/_build/doctrees/services/dns/Reverse-DNS.md.doctree create mode 100644 doc/_build/doctrees/services/dns/Service.md.doctree create mode 100644 doc/_build/doctrees/services/dns/domains.doctree create mode 100644 doc/_build/doctrees/services/dns/index.doctree create mode 100644 doc/_build/doctrees/services/dns/limits.doctree create mode 100644 doc/_build/doctrees/services/dns/records.doctree create mode 100644 doc/_build/doctrees/services/dns/reverse-dns.doctree create mode 100644 doc/_build/doctrees/services/identity/Roles.md.doctree create mode 100644 doc/_build/doctrees/services/identity/Service.md.doctree create mode 100644 doc/_build/doctrees/services/identity/Tenants.md.doctree create mode 100644 doc/_build/doctrees/services/identity/Tokens.md.doctree create mode 100644 doc/_build/doctrees/services/identity/Users.md.doctree create mode 100644 doc/_build/doctrees/services/identity/index.doctree create mode 100644 doc/_build/doctrees/services/identity/roles.doctree create mode 100644 doc/_build/doctrees/services/identity/tenants.doctree create mode 100644 doc/_build/doctrees/services/identity/tokens.doctree create mode 100644 doc/_build/doctrees/services/identity/users.doctree create mode 100644 doc/_build/doctrees/services/image/Images.md.doctree create mode 100644 doc/_build/doctrees/services/image/Schemas.md.doctree create mode 100644 doc/_build/doctrees/services/image/Sharing.md.doctree create mode 100644 doc/_build/doctrees/services/image/Tags.md.doctree create mode 100644 doc/_build/doctrees/services/image/images.doctree create mode 100644 doc/_build/doctrees/services/image/index.doctree create mode 100644 doc/_build/doctrees/services/image/schemas.doctree create mode 100644 doc/_build/doctrees/services/image/sharing.doctree create mode 100644 doc/_build/doctrees/services/image/tags.doctree create mode 100644 doc/_build/doctrees/services/index.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/README.md.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/USERGUIDE.md.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/access.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/caching.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/errors.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/index.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/lb-setup.sample.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/load-balancer.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/logging.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/metadata.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/monitors.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/nodes.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/sessions.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/ssl.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/stats.doctree create mode 100644 doc/_build/doctrees/services/load-balancer/virtual-ips.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Agents.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Alarms.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Changelogs.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Checks.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Entities.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Metrics.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Notifications.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Service.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Views.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/Zones.md.doctree create mode 100644 doc/_build/doctrees/services/monitoring/agents.doctree create mode 100644 doc/_build/doctrees/services/monitoring/alarms.doctree create mode 100644 doc/_build/doctrees/services/monitoring/changelogs.doctree create mode 100644 doc/_build/doctrees/services/monitoring/checks.doctree create mode 100644 doc/_build/doctrees/services/monitoring/entities.doctree create mode 100644 doc/_build/doctrees/services/monitoring/index.doctree create mode 100644 doc/_build/doctrees/services/monitoring/metrics.doctree create mode 100644 doc/_build/doctrees/services/monitoring/notifications.doctree create mode 100644 doc/_build/doctrees/services/monitoring/views.doctree create mode 100644 doc/_build/doctrees/services/monitoring/zones.doctree create mode 100644 doc/_build/doctrees/services/networking/README.md.doctree create mode 100644 doc/_build/doctrees/services/networking/USERGUIDE.md.doctree create mode 100644 doc/_build/doctrees/services/networking/index.doctree create mode 100644 doc/_build/doctrees/services/networking/networks.doctree create mode 100644 doc/_build/doctrees/services/networking/ports.doctree create mode 100644 doc/_build/doctrees/services/networking/security-group-rules.doctree create mode 100644 doc/_build/doctrees/services/networking/security-groups.doctree create mode 100644 doc/_build/doctrees/services/networking/subnets.doctree create mode 100644 doc/_build/doctrees/services/object-store/Access.md.doctree create mode 100644 doc/_build/doctrees/services/object-store/Account.md.doctree create mode 100644 doc/_build/doctrees/services/object-store/Container.md.cdn.doctree create mode 100644 doc/_build/doctrees/services/object-store/Container.md.storage.doctree create mode 100644 doc/_build/doctrees/services/object-store/Migrating.md.storage.doctree create mode 100644 doc/_build/doctrees/services/object-store/Object.md.cdn.doctree create mode 100644 doc/_build/doctrees/services/object-store/Object.md.storage.doctree create mode 100644 doc/_build/doctrees/services/object-store/README.md.doctree create mode 100644 doc/_build/doctrees/services/object-store/USERGUIDE.md.doctree create mode 100644 doc/_build/doctrees/services/object-store/access.doctree create mode 100644 doc/_build/doctrees/services/object-store/account.doctree create mode 100644 doc/_build/doctrees/services/object-store/cdn.doctree create mode 100644 doc/_build/doctrees/services/object-store/containers.doctree create mode 100644 doc/_build/doctrees/services/object-store/index.doctree create mode 100644 doc/_build/doctrees/services/object-store/migrating-containers.doctree create mode 100644 doc/_build/doctrees/services/object-store/objects.doctree create mode 100644 doc/_build/doctrees/services/object-store/rs-only.doctree create mode 100644 doc/_build/doctrees/services/orchestration/README.md.doctree create mode 100644 doc/_build/doctrees/services/orchestration/USERGUIDE.md.doctree create mode 100644 doc/_build/doctrees/services/orchestration/build-info.doctree create mode 100644 doc/_build/doctrees/services/orchestration/events.doctree create mode 100644 doc/_build/doctrees/services/orchestration/index.doctree create mode 100644 doc/_build/doctrees/services/orchestration/resource-types.doctree create mode 100644 doc/_build/doctrees/services/orchestration/resources.doctree create mode 100644 doc/_build/doctrees/services/orchestration/stacks.doctree create mode 100644 doc/_build/doctrees/services/orchestration/templates.doctree create mode 100644 doc/_build/doctrees/services/queues/Claim.md.doctree create mode 100644 doc/_build/doctrees/services/queues/Message.md.doctree create mode 100644 doc/_build/doctrees/services/queues/Queue.md.doctree create mode 100644 doc/_build/doctrees/services/queues/claims.doctree create mode 100644 doc/_build/doctrees/services/queues/index.doctree create mode 100644 doc/_build/doctrees/services/queues/messages.doctree create mode 100644 doc/_build/doctrees/services/queues/queues.doctree create mode 100644 doc/_build/doctrees/services/volume/index.doctree create mode 100644 doc/_build/doctrees/services/volume/snapshots.doctree create mode 100644 doc/_build/doctrees/services/volume/volume-types.doctree create mode 100644 doc/_build/doctrees/services/volume/volumes.doctree create mode 100644 doc/_build/doctrees/url-types.doctree create mode 100644 doc/_build/doctrees/using-php-5.3.doctree create mode 100644 doc/_build/html/.buildinfo create mode 100644 doc/_build/html/_sources/auth.txt create mode 100644 doc/_build/html/_sources/caching-creds.txt create mode 100644 doc/_build/html/_sources/customizing-clients.txt create mode 100644 doc/_build/html/_sources/debugging.txt create mode 100644 doc/_build/html/_sources/getting-started-with-openstack.txt create mode 100644 doc/_build/html/_sources/getting-started-with-rackspace.txt create mode 100644 doc/_build/html/_sources/http-clients.txt create mode 100644 doc/_build/html/_sources/index.txt create mode 100644 doc/_build/html/_sources/iterators.txt create mode 100644 doc/_build/html/_sources/logging.txt create mode 100644 doc/_build/html/_sources/regions.txt create mode 100644 doc/_build/html/_sources/services/autoscale/group-config.txt create mode 100644 doc/_build/html/_sources/services/autoscale/groups.txt create mode 100644 doc/_build/html/_sources/services/autoscale/index.txt create mode 100644 doc/_build/html/_sources/services/autoscale/policies.txt create mode 100644 doc/_build/html/_sources/services/autoscale/service.sample.txt create mode 100644 doc/_build/html/_sources/services/autoscale/webhooks.txt create mode 100644 doc/_build/html/_sources/services/common/clients.sample.txt create mode 100644 doc/_build/html/_sources/services/common/os-client.sample.txt create mode 100644 doc/_build/html/_sources/services/common/rs-client.sample.txt create mode 100644 doc/_build/html/_sources/services/common/rs-client.txt create mode 100644 doc/_build/html/_sources/services/common/rs-only.sample.txt create mode 100644 doc/_build/html/_sources/services/common/service-args.txt create mode 100644 doc/_build/html/_sources/services/compute/Images.md.txt create mode 100644 doc/_build/html/_sources/services/compute/Keypair.md.txt create mode 100644 doc/_build/html/_sources/services/compute/Server.md.txt create mode 100644 doc/_build/html/_sources/services/compute/Service.md.txt create mode 100644 doc/_build/html/_sources/services/compute/flavors.txt create mode 100644 doc/_build/html/_sources/services/compute/images.txt create mode 100644 doc/_build/html/_sources/services/compute/index.txt create mode 100644 doc/_build/html/_sources/services/compute/keypair.txt create mode 100644 doc/_build/html/_sources/services/compute/keypairs.txt create mode 100644 doc/_build/html/_sources/services/compute/server.txt create mode 100644 doc/_build/html/_sources/services/compute/servers.txt create mode 100644 doc/_build/html/_sources/services/compute/service.sample.txt create mode 100644 doc/_build/html/_sources/services/database/README.md.txt create mode 100644 doc/_build/html/_sources/services/database/configurations.txt create mode 100644 doc/_build/html/_sources/services/database/databases.txt create mode 100644 doc/_build/html/_sources/services/database/datastores.txt create mode 100644 doc/_build/html/_sources/services/database/index.txt create mode 100644 doc/_build/html/_sources/services/database/instances.txt create mode 100644 doc/_build/html/_sources/services/database/service.sample.txt create mode 100644 doc/_build/html/_sources/services/database/users.txt create mode 100644 doc/_build/html/_sources/services/dns/Domains.md.txt create mode 100644 doc/_build/html/_sources/services/dns/Limits.md.txt create mode 100644 doc/_build/html/_sources/services/dns/Records.md.txt create mode 100644 doc/_build/html/_sources/services/dns/Reverse-DNS.md.txt create mode 100644 doc/_build/html/_sources/services/dns/Service.md.txt create mode 100644 doc/_build/html/_sources/services/dns/domains.txt create mode 100644 doc/_build/html/_sources/services/dns/index.txt create mode 100644 doc/_build/html/_sources/services/dns/limits.txt create mode 100644 doc/_build/html/_sources/services/dns/records.txt create mode 100644 doc/_build/html/_sources/services/dns/reverse-dns.txt create mode 100644 doc/_build/html/_sources/services/identity/Roles.md.txt create mode 100644 doc/_build/html/_sources/services/identity/Service.md.txt create mode 100644 doc/_build/html/_sources/services/identity/Tenants.md.txt create mode 100644 doc/_build/html/_sources/services/identity/Tokens.md.txt create mode 100644 doc/_build/html/_sources/services/identity/Users.md.txt create mode 100644 doc/_build/html/_sources/services/identity/index.txt create mode 100644 doc/_build/html/_sources/services/identity/roles.txt create mode 100644 doc/_build/html/_sources/services/identity/tenants.txt create mode 100644 doc/_build/html/_sources/services/identity/tokens.txt create mode 100644 doc/_build/html/_sources/services/identity/users.txt create mode 100644 doc/_build/html/_sources/services/image/Images.md.txt create mode 100644 doc/_build/html/_sources/services/image/Schemas.md.txt create mode 100644 doc/_build/html/_sources/services/image/Sharing.md.txt create mode 100644 doc/_build/html/_sources/services/image/Tags.md.txt create mode 100644 doc/_build/html/_sources/services/image/images.txt create mode 100644 doc/_build/html/_sources/services/image/index.txt create mode 100644 doc/_build/html/_sources/services/image/schemas.txt create mode 100644 doc/_build/html/_sources/services/image/sharing.txt create mode 100644 doc/_build/html/_sources/services/image/tags.txt create mode 100644 doc/_build/html/_sources/services/index.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/README.md.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/USERGUIDE.md.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/access.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/caching.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/errors.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/index.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/lb-setup.sample.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/load-balancer.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/logging.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/metadata.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/monitors.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/nodes.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/sessions.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/ssl.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/stats.txt create mode 100644 doc/_build/html/_sources/services/load-balancer/virtual-ips.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Agents.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Alarms.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Changelogs.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Checks.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Entities.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Metrics.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Notifications.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Service.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Views.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/Zones.md.txt create mode 100644 doc/_build/html/_sources/services/monitoring/agents.txt create mode 100644 doc/_build/html/_sources/services/monitoring/alarms.txt create mode 100644 doc/_build/html/_sources/services/monitoring/changelogs.txt create mode 100644 doc/_build/html/_sources/services/monitoring/checks.txt create mode 100644 doc/_build/html/_sources/services/monitoring/entities.txt create mode 100644 doc/_build/html/_sources/services/monitoring/index.txt create mode 100644 doc/_build/html/_sources/services/monitoring/metrics.txt create mode 100644 doc/_build/html/_sources/services/monitoring/notifications.txt create mode 100644 doc/_build/html/_sources/services/monitoring/views.txt create mode 100644 doc/_build/html/_sources/services/monitoring/zones.txt create mode 100644 doc/_build/html/_sources/services/networking/README.md.txt create mode 100644 doc/_build/html/_sources/services/networking/USERGUIDE.md.txt create mode 100644 doc/_build/html/_sources/services/networking/index.txt create mode 100644 doc/_build/html/_sources/services/networking/networks.txt create mode 100644 doc/_build/html/_sources/services/networking/ports.txt create mode 100644 doc/_build/html/_sources/services/networking/security-group-rules.txt create mode 100644 doc/_build/html/_sources/services/networking/security-groups.txt create mode 100644 doc/_build/html/_sources/services/networking/subnets.txt create mode 100644 doc/_build/html/_sources/services/object-store/Access.md.txt create mode 100644 doc/_build/html/_sources/services/object-store/Account.md.txt create mode 100644 doc/_build/html/_sources/services/object-store/Container.md.cdn.txt create mode 100644 doc/_build/html/_sources/services/object-store/Container.md.storage.txt create mode 100644 doc/_build/html/_sources/services/object-store/Migrating.md.storage.txt create mode 100644 doc/_build/html/_sources/services/object-store/Object.md.cdn.txt create mode 100644 doc/_build/html/_sources/services/object-store/Object.md.storage.txt create mode 100644 doc/_build/html/_sources/services/object-store/README.md.txt create mode 100644 doc/_build/html/_sources/services/object-store/USERGUIDE.md.txt create mode 100644 doc/_build/html/_sources/services/object-store/access.txt create mode 100644 doc/_build/html/_sources/services/object-store/account.txt create mode 100644 doc/_build/html/_sources/services/object-store/cdn.txt create mode 100644 doc/_build/html/_sources/services/object-store/containers.txt create mode 100644 doc/_build/html/_sources/services/object-store/index.txt create mode 100644 doc/_build/html/_sources/services/object-store/migrating-containers.txt create mode 100644 doc/_build/html/_sources/services/object-store/objects.txt create mode 100644 doc/_build/html/_sources/services/object-store/rs-only.txt create mode 100644 doc/_build/html/_sources/services/orchestration/README.md.txt create mode 100644 doc/_build/html/_sources/services/orchestration/USERGUIDE.md.txt create mode 100644 doc/_build/html/_sources/services/orchestration/build-info.txt create mode 100644 doc/_build/html/_sources/services/orchestration/events.txt create mode 100644 doc/_build/html/_sources/services/orchestration/index.txt create mode 100644 doc/_build/html/_sources/services/orchestration/resource-types.txt create mode 100644 doc/_build/html/_sources/services/orchestration/resources.txt create mode 100644 doc/_build/html/_sources/services/orchestration/stacks.txt create mode 100644 doc/_build/html/_sources/services/orchestration/templates.txt create mode 100644 doc/_build/html/_sources/services/queues/Claim.md.txt create mode 100644 doc/_build/html/_sources/services/queues/Message.md.txt create mode 100644 doc/_build/html/_sources/services/queues/Queue.md.txt create mode 100644 doc/_build/html/_sources/services/queues/claims.txt create mode 100644 doc/_build/html/_sources/services/queues/index.txt create mode 100644 doc/_build/html/_sources/services/queues/messages.txt create mode 100644 doc/_build/html/_sources/services/queues/queues.txt create mode 100644 doc/_build/html/_sources/services/volume/index.txt create mode 100644 doc/_build/html/_sources/services/volume/snapshots.txt create mode 100644 doc/_build/html/_sources/services/volume/volume-types.txt create mode 100644 doc/_build/html/_sources/services/volume/volumes.txt create mode 100644 doc/_build/html/_sources/url-types.txt create mode 100644 doc/_build/html/_sources/using-php-5.3.txt create mode 100644 doc/_build/html/_static/ajax-loader.gif create mode 100644 doc/_build/html/_static/basic.css create mode 100644 doc/_build/html/_static/comment-bright.png create mode 100644 doc/_build/html/_static/comment-close.png create mode 100644 doc/_build/html/_static/comment.png create mode 100644 doc/_build/html/_static/css/badge_only.css create mode 100644 doc/_build/html/_static/css/theme.css create mode 100644 doc/_build/html/_static/default.css create mode 100644 doc/_build/html/_static/doctools.js create mode 100644 doc/_build/html/_static/down-pressed.png create mode 100644 doc/_build/html/_static/down.png create mode 100644 doc/_build/html/_static/file.png create mode 100644 doc/_build/html/_static/fonts/fontawesome-webfont.eot create mode 100644 doc/_build/html/_static/fonts/fontawesome-webfont.svg create mode 100644 doc/_build/html/_static/fonts/fontawesome-webfont.ttf create mode 100644 doc/_build/html/_static/fonts/fontawesome-webfont.woff create mode 100644 doc/_build/html/_static/jquery.js create mode 100644 doc/_build/html/_static/js/theme.js create mode 100644 doc/_build/html/_static/minus.png create mode 100644 doc/_build/html/_static/plus.png create mode 100644 doc/_build/html/_static/pygments.css create mode 100644 doc/_build/html/_static/searchtools.js create mode 100644 doc/_build/html/_static/sidebar.js create mode 100644 doc/_build/html/_static/underscore.js create mode 100644 doc/_build/html/_static/up-pressed.png create mode 100644 doc/_build/html/_static/up.png create mode 100644 doc/_build/html/_static/websupport.js create mode 100644 doc/_build/html/auth.html create mode 100644 doc/_build/html/caching-creds.html create mode 100644 doc/_build/html/customizing-clients.html create mode 100644 doc/_build/html/debugging.html create mode 100644 doc/_build/html/genindex.html create mode 100644 doc/_build/html/getting-started-with-openstack.html create mode 100644 doc/_build/html/getting-started-with-rackspace.html create mode 100644 doc/_build/html/http-clients.html create mode 100644 doc/_build/html/index.html create mode 100644 doc/_build/html/iterators.html create mode 100644 doc/_build/html/logging.html create mode 100644 doc/_build/html/objects.inv create mode 100644 doc/_build/html/regions.html create mode 100644 doc/_build/html/search.html create mode 100644 doc/_build/html/searchindex.js create mode 100644 doc/_build/html/services/autoscale/group-config.html create mode 100644 doc/_build/html/services/autoscale/groups.html create mode 100644 doc/_build/html/services/autoscale/index.html create mode 100644 doc/_build/html/services/autoscale/policies.html create mode 100644 doc/_build/html/services/autoscale/service.sample.html create mode 100644 doc/_build/html/services/autoscale/webhooks.html create mode 100644 doc/_build/html/services/common/clients.sample.html create mode 100644 doc/_build/html/services/common/os-client.sample.html create mode 100644 doc/_build/html/services/common/rs-client.html create mode 100644 doc/_build/html/services/common/rs-client.sample.html create mode 100644 doc/_build/html/services/common/rs-only.sample.html create mode 100644 doc/_build/html/services/common/service-args.html create mode 100644 doc/_build/html/services/compute/Images.md.html create mode 100644 doc/_build/html/services/compute/Keypair.md.html create mode 100644 doc/_build/html/services/compute/Server.md.html create mode 100644 doc/_build/html/services/compute/Service.md.html create mode 100644 doc/_build/html/services/compute/flavors.html create mode 100644 doc/_build/html/services/compute/images.html create mode 100644 doc/_build/html/services/compute/index.html create mode 100644 doc/_build/html/services/compute/keypair.html create mode 100644 doc/_build/html/services/compute/keypairs.html create mode 100644 doc/_build/html/services/compute/server.html create mode 100644 doc/_build/html/services/compute/servers.html create mode 100644 doc/_build/html/services/compute/service.sample.html create mode 100644 doc/_build/html/services/database/README.md.html create mode 100644 doc/_build/html/services/database/configurations.html create mode 100644 doc/_build/html/services/database/databases.html create mode 100644 doc/_build/html/services/database/datastores.html create mode 100644 doc/_build/html/services/database/index.html create mode 100644 doc/_build/html/services/database/instances.html create mode 100644 doc/_build/html/services/database/service.sample.html create mode 100644 doc/_build/html/services/database/users.html create mode 100644 doc/_build/html/services/dns/Domains.md.html create mode 100644 doc/_build/html/services/dns/Limits.md.html create mode 100644 doc/_build/html/services/dns/Records.md.html create mode 100644 doc/_build/html/services/dns/Reverse-DNS.md.html create mode 100644 doc/_build/html/services/dns/Service.md.html create mode 100644 doc/_build/html/services/dns/domains.html create mode 100644 doc/_build/html/services/dns/index.html create mode 100644 doc/_build/html/services/dns/limits.html create mode 100644 doc/_build/html/services/dns/records.html create mode 100644 doc/_build/html/services/dns/reverse-dns.html create mode 100644 doc/_build/html/services/identity/Roles.md.html create mode 100644 doc/_build/html/services/identity/Service.md.html create mode 100644 doc/_build/html/services/identity/Tenants.md.html create mode 100644 doc/_build/html/services/identity/Tokens.md.html create mode 100644 doc/_build/html/services/identity/Users.md.html create mode 100644 doc/_build/html/services/identity/index.html create mode 100644 doc/_build/html/services/identity/roles.html create mode 100644 doc/_build/html/services/identity/tenants.html create mode 100644 doc/_build/html/services/identity/tokens.html create mode 100644 doc/_build/html/services/identity/users.html create mode 100644 doc/_build/html/services/image/Images.md.html create mode 100644 doc/_build/html/services/image/Schemas.md.html create mode 100644 doc/_build/html/services/image/Sharing.md.html create mode 100644 doc/_build/html/services/image/Tags.md.html create mode 100644 doc/_build/html/services/image/images.html create mode 100644 doc/_build/html/services/image/index.html create mode 100644 doc/_build/html/services/image/schemas.html create mode 100644 doc/_build/html/services/image/sharing.html create mode 100644 doc/_build/html/services/image/tags.html create mode 100644 doc/_build/html/services/index.html create mode 100644 doc/_build/html/services/load-balancer/README.md.html create mode 100644 doc/_build/html/services/load-balancer/USERGUIDE.md.html create mode 100644 doc/_build/html/services/load-balancer/access.html create mode 100644 doc/_build/html/services/load-balancer/caching.html create mode 100644 doc/_build/html/services/load-balancer/errors.html create mode 100644 doc/_build/html/services/load-balancer/index.html create mode 100644 doc/_build/html/services/load-balancer/lb-setup.sample.html create mode 100644 doc/_build/html/services/load-balancer/load-balancer.html create mode 100644 doc/_build/html/services/load-balancer/logging.html create mode 100644 doc/_build/html/services/load-balancer/metadata.html create mode 100644 doc/_build/html/services/load-balancer/monitors.html create mode 100644 doc/_build/html/services/load-balancer/nodes.html create mode 100644 doc/_build/html/services/load-balancer/sessions.html create mode 100644 doc/_build/html/services/load-balancer/ssl.html create mode 100644 doc/_build/html/services/load-balancer/stats.html create mode 100644 doc/_build/html/services/load-balancer/virtual-ips.html create mode 100644 doc/_build/html/services/monitoring/Agents.md.html create mode 100644 doc/_build/html/services/monitoring/Alarms.md.html create mode 100644 doc/_build/html/services/monitoring/Changelogs.md.html create mode 100644 doc/_build/html/services/monitoring/Checks.md.html create mode 100644 doc/_build/html/services/monitoring/Entities.md.html create mode 100644 doc/_build/html/services/monitoring/Metrics.md.html create mode 100644 doc/_build/html/services/monitoring/Notifications.md.html create mode 100644 doc/_build/html/services/monitoring/Service.md.html create mode 100644 doc/_build/html/services/monitoring/Views.md.html create mode 100644 doc/_build/html/services/monitoring/Zones.md.html create mode 100644 doc/_build/html/services/monitoring/agents.html create mode 100644 doc/_build/html/services/monitoring/alarms.html create mode 100644 doc/_build/html/services/monitoring/changelogs.html create mode 100644 doc/_build/html/services/monitoring/checks.html create mode 100644 doc/_build/html/services/monitoring/entities.html create mode 100644 doc/_build/html/services/monitoring/index.html create mode 100644 doc/_build/html/services/monitoring/metrics.html create mode 100644 doc/_build/html/services/monitoring/notifications.html create mode 100644 doc/_build/html/services/monitoring/views.html create mode 100644 doc/_build/html/services/monitoring/zones.html create mode 100644 doc/_build/html/services/networking/README.md.html create mode 100644 doc/_build/html/services/networking/USERGUIDE.md.html create mode 100644 doc/_build/html/services/networking/index.html create mode 100644 doc/_build/html/services/networking/networks.html create mode 100644 doc/_build/html/services/networking/ports.html create mode 100644 doc/_build/html/services/networking/security-group-rules.html create mode 100644 doc/_build/html/services/networking/security-groups.html create mode 100644 doc/_build/html/services/networking/subnets.html create mode 100644 doc/_build/html/services/object-store/Access.md.html create mode 100644 doc/_build/html/services/object-store/Account.md.html create mode 100644 doc/_build/html/services/object-store/Container.md.cdn.html create mode 100644 doc/_build/html/services/object-store/Container.md.storage.html create mode 100644 doc/_build/html/services/object-store/Migrating.md.storage.html create mode 100644 doc/_build/html/services/object-store/Object.md.cdn.html create mode 100644 doc/_build/html/services/object-store/Object.md.storage.html create mode 100644 doc/_build/html/services/object-store/README.md.html create mode 100644 doc/_build/html/services/object-store/USERGUIDE.md.html create mode 100644 doc/_build/html/services/object-store/access.html create mode 100644 doc/_build/html/services/object-store/account.html create mode 100644 doc/_build/html/services/object-store/cdn.html create mode 100644 doc/_build/html/services/object-store/containers.html create mode 100644 doc/_build/html/services/object-store/index.html create mode 100644 doc/_build/html/services/object-store/migrating-containers.html create mode 100644 doc/_build/html/services/object-store/objects.html create mode 100644 doc/_build/html/services/object-store/rs-only.html create mode 100644 doc/_build/html/services/orchestration/README.md.html create mode 100644 doc/_build/html/services/orchestration/USERGUIDE.md.html create mode 100644 doc/_build/html/services/orchestration/build-info.html create mode 100644 doc/_build/html/services/orchestration/events.html create mode 100644 doc/_build/html/services/orchestration/index.html create mode 100644 doc/_build/html/services/orchestration/resource-types.html create mode 100644 doc/_build/html/services/orchestration/resources.html create mode 100644 doc/_build/html/services/orchestration/stacks.html create mode 100644 doc/_build/html/services/orchestration/templates.html create mode 100644 doc/_build/html/services/queues/Claim.md.html create mode 100644 doc/_build/html/services/queues/Message.md.html create mode 100644 doc/_build/html/services/queues/Queue.md.html create mode 100644 doc/_build/html/services/queues/claims.html create mode 100644 doc/_build/html/services/queues/index.html create mode 100644 doc/_build/html/services/queues/messages.html create mode 100644 doc/_build/html/services/queues/queues.html create mode 100644 doc/_build/html/services/volume/index.html create mode 100644 doc/_build/html/services/volume/snapshots.html create mode 100644 doc/_build/html/services/volume/volume-types.html create mode 100644 doc/_build/html/services/volume/volumes.html create mode 100644 doc/_build/html/url-types.html create mode 100644 doc/_build/html/using-php-5.3.html diff --git a/doc/_build/doctrees/auth.doctree b/doc/_build/doctrees/auth.doctree new file mode 100644 index 0000000000000000000000000000000000000000..8ee18ed4c48b149dc266f544fde07ba0ba1f17fe GIT binary patch literal 7264 zcmeHMcbpW}9fyN!3miqU0Xizmg4`|`q6S4JsF7e+gFMBA%`!XlcHeljGy9u2b0uaX zB++oGrWzAX@4Z*kOz)=mBI&*N`u$D0-8)XB@smGHybs*xX6C(L`Tpwry?s5F?ORdk zxVq%|w$ufFY)LOH(A>5AsVAm+MKS1^Eg4RN<2nu<^Z`EgPB!Vj2`fZ8Ola){X@MBB zq96d>n)ai>lC-cQ3bwTTAZ4?tB9?)(-|+p=^FvwFp&5iUv}3O5XDJ<9)8d$x6jh%X znjzv+)h`xj0uJW_E{nu~r5x7=Q#!0lhtC8=ON-?$&~o5$L`+8lkH8$u#5+pN5q;1D z64TMDNAzXykJ&v8G|B?(boolN<;Ze7@UH|dU2dy(d358jwnxf9yUbFS0v*x{(I1*k z)`C`w{){iJ5(P611E(H^!06bRjw^}*J8JVfO-5DpTdt`!{yx4c2I{6Y5-`FiRK+|7 zXM-PZ)Ca0q6QGj4=aV7>Zo+}Hq4hWnQ)B2)1 zDsh3W(_LU#9j%T~$EX!*rRYtzP)R}Ha#0yob$nHwP*o>Z)k)Pzfv?KAZJ5bymmG)p zZJdq6sn_hM`^2;f0x0HoN>n$a`b+{ahuI9wwkptlA@ON3oz4>927~NUa81{NwG7*r z+JFlg>`pCsmu0%{1V80&Ee*42I-U* zZ8ZGAFui0o6D%2*^PW&S+E8Y@Ej_)S7h6hNBf0>BpdBO(GRHR#?z=JcN2GUf-%Z$} z4LRO+0%@-YS=%TEa(HdGjM{J-1y5QWM8QOf&ge42;1m*cKQ_eX*$gp1H^iB0S8CbELNc>AS|@JV<vI(J zS)F?R@Y!9e$l}^FJHm@a(@4+**=u$bML#DC&)0O(q!@5buNfg>(}Q-;165=*F*x3G zH|w^Ed|eR(?Lcytm2@$OJAyuq{xt3vK9RyVsCGfbbl1T-T^eBamSfc+xHz z+ZzRI#{Ox$Pu;KqyAkkVgm}|aJ3%T`#sn&racNrk3ft1cSL%S8dczC-F(LqB>Tej@AF6703n$8B>Eryc={=P`O^enTjBR6Rd94`M7IVeMJ@7{u6=T2}v`U%HUEQ&v-IgJ?2Kw?i| zSiTnqJ``cWJ&qtS7zK6CD>>>(rP)6l>PO~wd=%%ETVTCikl>z;A3YkL@|c*eVVC&d zPzkoY8WGE>=-QYb%Ze?4t-3sgWA?zI7REl#zmqU7C+j>U9|wdUAJY>+D5;p&cBiDX zu<-6U9z7A%pA^%RSH@kyX(CFCIkVxj!Gf3Q;#vd!dH$YXtu?MH6b$^V=Hpf(Rw^NXOg2{Vo&`%5@tJzI zMZ54mk)BsM4e(CMZ86qZ3<*PlgC*;6X^0cw8sp@XjnyeNUV}V`)8ZPIhAHq*9a@Z} zY~xf!AhR_H%!xZh$oh9(%fEoX=@5e>O0_ z8g(!s?4rqSjc!ZJX*d?nL>#9$ZMnYJEF=dbo`VB(@32y8PM}8 zV$syZGa2taKc*Lm0sP5k)bzrbUL=<2iB{crYxzn|FRs!{#8MphJM+?H_e-nvGI98f z<+bF~%d7MXF=#nB>%yFxURkAAG4x_#KA24ghp)yQc1ps|2=_HrdM%69_5(Dn^g2uf z96OuQz8*8$McuM!dP9}oC>G_+QUTr+)0@Q-$Fni?vcek1yC}Ds-cqHviUBRd5EBg(<9WlMLD3;|=)Oaom!ACT`3t}7w(tcoL7L5vuv4Q0P z!<#E&VbHL!6hrAKaOvG*g$l#==5jgoeGJGQ88%9O&@8L4<(BC^Vh{*el1bBh4`7@v z+pGegCUAtH_f^D@#^APHLp|n^x~BKTj-k&ia%pZ6z4#JL&<8Mk$p$M}Z%g{1=)pw# zLt@ZI%-|^5YSV{V!UWrzD@UbEAHn!`A+*4B-Am`=AH}#_nQoXZ_}GqKu_zNFn-YJ# zqDmF9G8u)`AgONy&|3PakO^Ga?Gs`Ni>R?hfuYNTollBhTh^l{eX1gs$W~plT_;?r zm6RcUdQz#Rlxz>5v|*D?UlL2@xPv=| zWP+1n^JQSuhl5tbY0_6RTfyN*9qojEKwkxAG+X9KO}20rteG~h2$DJb*A9qP(4;jL zXdSE9(jnN_C&fJJjnX$NnO2)%H_qqKT5_`UFtMU|7P^GxG! zPl^TUg+h&;m%alr*+sj&>^88N0X!WPy;0a0K7+n13fXq{=kH;+<8_;ezApxomBSh! z{6H+O*^cH@FI+Ie<%bn9hycb!ui=c-kC2xJL$h8RbL>#jk9Q1+q3M1^KM}oR*9Bnx zQ_-Ja;rvXk5Q`DSAqM*#3z!pz^z#E^uqi#9Y#HodK(xdGI*|Ml&aPXy+^JE7iylqC zniLD!)Kl^KYjIo_pJQWVrS?Rqd@qSlTaK2oQkUsBoD{v#9Iq*-sa*UI>9@H1K#8K? ziIvmQm=OfOSGOitNPew@z#l}9W78iYtd9dZE=4j!{b^FHNW)CJGKqCy6slT&HS=dU zbB1Z+s+#^%6-Ojai;DyqBsXK2EC70&{(3+xzzq`uDSiGMC(Z<&sr2{vN>mriXC`pC zH}1mzv13kp_4Cg-n1j(tQ7y%_%rZq0e60H>E-4+sTr7!$9{g6^;vO3Z^O8*%+9uQ6 p{7MVgI=I5Y5LXQ5Sycz~v9ucW;cnY^8Ot2fr{%-nfz zRDcPPgv3toy%W-V?;*YSp8PZU?#%9LrHJ2hPEK<0v5$9mUb*+X-!1b!H0b%Um)RgR zvr+6TQ{k^|71;`%GxabH=CrLU#v<8Pb}dnc&dq74dTL@|V4xL*%B&fy+KyV4?Ict* zTtBrJPdw@AAX;?1RQWh5kfEWGdM5UmtZ8guaa|m_zG@j7&1p=GCo(n4Z7XBsBJQX( zXuBBqvNXkR*Th-sDcVsN6<>LADtR~UtcwYm*|-(QHj1rsjg_gT@%eMa2wNNYhIZw& zyQ+u9#JZ_7T~qhy5wW*p=uE$%6PXx=`Js=?X>Wth+YFFV9$QeM^MTI=IXwmVq;jQW z=7nNF48tE-P8aDxG2Hq6;#1cFnVK*vHMP^S9jIE8#;0+XsU5{4#^|OT68FCt8#0#Y{_M;&C5?oFH;!rD{|=n3=+kwv0dq zCyc#+Mp4G6r2N0J#<8&@Jr5b< zNXKaK&BwEnYtyg*X(@dtZdFp1D1aR-t8mTHa+&iIds8)o?9n(ffLY3kBS$T(bj`6k zEq5ZR6&Hp$nx*UeLO0ffj-JKgJG@o+&h3Wp27TyBP`3v4*&ynTIUQl5GQigrMAb~k zn{s*%)A4bXjD>hfMTl~$oMjmXzS9CV&+jnSk-EAX(}R(_-01oVz3A0 zpu%JYMQ}oJ2F^gO*sq9D?|9+q4c$GadszpA<9-j-9HdBW}ZmVa-@Cvp6( zWyRG+sl9!}@tsoJJM;ydt6QIR2Yk6Rrv>(fPrYrOt0Jen*oG84=a?q}bEKFDru!#= z9xIGo2Cuk&6$OvWCDWAaV_4}gfk(OWf%aIcR=qIul@nN}mB#I|bl!FB&RKe4pN|E* zPHpAm?k;e5!^i)Ur7r@8FV5*6#*nQR1%{g&Qy5q@nb2(lkEIGV5aoMwav9;r1_uT> zH%}%iIu^#6f6pE0=_Zk$x}j1z=!F4yh#Q?qtvJ0W)!wJWhsB-l-0>6jg}H^h-4pfW zx1XG=FCdT8RIXJk4*pEy_>{+iN$18R&Ln25$?5WeOoDl}Hi<8ybot~2XQ)@IQxBaf|gEmyEfEUwkK(r~bga#V(2tp@04(+(G(pq<32ITXRkrr(mT}b z!j*ES?K~_prBh8eH)I+4sNBnC3FnBG=K&HwFiQm(mv#(#-~p%F`FQ1(PWfr(2x|3- zvQ$^6)&c4|+E=+1+s)B0z)i9(kwT;CA=+~vtpMTbM+-Lrs`ZocU< zm{Dw~c~*>uGFr?q_>*^P8;S^~VKKJa4i6X1CSQyum~2$KtVr>Ih%vF87t2X;Z#T7D zNbM(664(2qosP=$KI$XNrfkNj2p-X6gjG>VW$|Dl^>_?YB`x5o=LTXDMZ zQDsg1mF&0JZc`Zjmv zHjAqt?0#8Yj0S#3fOt6sh@Jhm7FoD==CsTFOH5=A3_US zF@zW|fE4hCy4aDnJhv&KxMXQaZxs8qwaMXH&BifQpg`HyY@9CEv~7nqdXpFf0-oZd z>CI=L)u@DBP*6DV`blr8i*W-HB0+y`vu7qe(7C~LjE(dzwy?m~4e?GI(z_u=?0^@Lj-pchc@MN8EkoN`@ZR|$v9n`D zNAY-HUC-9VewC(i>cZ042cT~GRin3th}-+cZZ^?i3toDY5MI&;#E`F=*&=4pC~Lc`OmO-LLbA)x+KJnvl%dCf z6;zfWYQ>#pAH}lq6`4kCFwRo4O&_a^^BMP|LnCM|L^s%L6@47tQ6)21Tz#!ipAdU6 zZa^>tt25KV=99o?7^6=sSfo#NVgrr>h&ivW4Vw;MV>2vjtUx`tf4?o%=eV*aNcmR@Nj^m*c8sHa@PK*ikj6(nE zc>Kk)Vtc7vxJ+L963k?Z_Oa~CxMCCV^c69b+1AYU^i@&mY-c`y4ZDM=KScC(F;=Xc zF#zEkVwZ~%iiH&(2^p7f*2NeS7-L2&Sfy{FE{$2)bXNl3YWnv4s2JbKBl?aQ5(~HB z>hFq?u0Huay-(~y5?km~oC_GsEj@llj4i5&Wg~$7eVA4dU>%Ylfb6CX<igBb5V?}Ty=G@s6+g-^b_cKakNN3 z75g`$u_+0DrXLmKT(8`91b!|C1D}2YV`JzE5coQQ`sG=%uS_!)sv_5ck=1TjX8aY% z+`+V({Im+?GR3T-+#xYv%sfU0DoW4n&SM;CA^e+ z|1dvL3YmY*=}%QX0daAUzBL2I`c>w#bNo4{zv%s_@EAY;TC~4nknF0n&?KQhVxAOZ Z-CAnsZw>mp7-mjDEd57L|IC`R{{rgZJ!Jp@ literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/customizing-clients.doctree b/doc/_build/doctrees/customizing-clients.doctree new file mode 100644 index 0000000000000000000000000000000000000000..eb198e953b2db6e98c60cb8d1a5a78c3e0d465b3 GIT binary patch literal 20271 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/debugging.doctree b/doc/_build/doctrees/debugging.doctree new file mode 100644 index 0000000000000000000000000000000000000000..9b80c8314221aa78c9b40148cf08d391094d02ac GIT binary patch literal 14033 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/environment.pickle b/doc/_build/doctrees/environment.pickle new file mode 100644 index 0000000000000000000000000000000000000000..74fa31fe93523c621de7fb71e7b61fd5ae963f8d GIT binary patch literal 289822 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�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 EKi+G>(EtDd literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/getting-started-with-openstack.doctree b/doc/_build/doctrees/getting-started-with-openstack.doctree new file mode 100644 index 0000000000000000000000000000000000000000..e2c06eebc0224d6fdef141617b5af7a88ffe2fcc GIT binary patch literal 24424 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/http-clients.doctree b/doc/_build/doctrees/http-clients.doctree new file mode 100644 index 0000000000000000000000000000000000000000..aa0efa026f13b3c3df4783daa5fd7b609d7e07b8 GIT binary patch literal 9751 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/index.doctree b/doc/_build/doctrees/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..92f917534aa891830942b68d5c1400606d9277f3 GIT binary patch literal 14297 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 M2b{U#8dZn>3o-K`g#Z8m literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/iterators.doctree b/doc/_build/doctrees/iterators.doctree new file mode 100644 index 0000000000000000000000000000000000000000..e08b870133f9e4602056d32608759745886efe10 GIT binary patch literal 44714 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 A*8l(j literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/logging.doctree b/doc/_build/doctrees/logging.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1194f30aa28e1cb3341ae905fa7d68922a0b598c GIT binary patch literal 7286 zcmeHMcX%Ad6_<C!~{3I_ZV<-h1!8S3>f8yL;+ROuonm*!Z)%B~?HaOIcxBMtIHtm;{&<%WQAB@Bj6O~QqhJX_I zB``LWi)oSQs@vMgQcW6TS0TTnNS&gq5@`*LB@;xtBB^UoOA$1ST6RoU?>lj)R)6%?Z5zB1Tm#NX``hBSy%T%jqZ78u^wTae-3M*8* z=uQ;?c|(Dfk!Y(Z*K+`hRu$>EnMO6Kra3wuHeDUl8rW3ZV;TIlqDizsPiREz)O^vB zt)DQo4dyBcBef~)w`;B})OEl@O`)#ph0#;`jB}s>j}(pv>f?BUE(VWoIb_Jw6@@i92Z7!;limIomdW(^QK$VF(b*7ZP@*tvj=QP%*-Fh>f z9@7~RsaQ-QD%(+6u{+L;X&077)1-8@fyJIRO)Q!N?9MCJ=-ikd z#2RskvqsZG{5&u}GgVC`Iz4zA^Gv4+mJd-o=jL-h^mu4Y7qA}8^V~#_nHE4S*;Q@V zRgNwMvWsH6n33HFWak+xRMHwAz7ifbb-N;mhpmd|BK9oUP5MrQ;Ig{(_k^IDAlS-V z$E|V@D$7LmdZ5Ex9WjI42E$E3owLt?9HqhbEn91Q+!}T5L}CT6LLF7YK=-f+S34Ht zL}8J--`cBWE@Oa=60Z=8PQuB;))BXAg+U7a!svz@JlR0K)j~vDwr2wT@q#fXOEWV! zj_P0qW4@IpZ2zPWMEit^=wa;Zfw|n#+VJotdH911TV_4F6mA%d=`yzJlywNWC@RnN zhUY><+so1A@WmA|UCFyT!EKro7{#91D8P|(X)ge@W6OlsNC3iBF4S zX3}T~;k|QcIrhxuUb+U`uZ`(C#&RU0R2{{z_r+A4ggqrR820r?gk8#DPi&xJ$hBiC zH(>8eV5=1EiIZsmU93?BOq`e`BRa}GQ*;@LsmeP$5*JK{EvurAsEP|!+OVTRPx}Gl zKulgklaZ;KT+f;u(=?zOv>txPBt`zj#vSp zxh7|i+OB5<&y*UjlEVFn*+I2sprcW^AIXJ#0|MkfdW3F5Jlq`9qu7T>wFBE%%L6*0ZC5e|2z8HyNC?CiT@dKD|N8Di&j_6)(h)r7B9 z;-gnj`iL>nYt-`TK6o zmV#OHb5@Sm_=@llFNmCd6PJava=AibvSUD3U{#du`_jv8%36eT5{IUC$@lO%($kA= zo3a~i`g($4zK7EtU6kvf6LZ7y{@Qq70KsUVDhHCLYIJ-5ww|r&-8G-HtR7&~3191+ z-Bu4qN*F}594U++;p>GuF zo9sN%(U^25W43Q$usMNnawhuiB7KKp@jwQ+zdI<}TqhfPeh)*>&Z$@oeZNRQ5SNm zskAG@k^Vqe3o5L61^SI>hXpEWj-)?{`558;S+qO2rJyR*>hu>znAo=D$x-RiUoq_LfEE~6 z`{}^>Z#jL{~Q$EvR0Pt*Kto8C1t3a(B>Z&>w+k(N8u6J>Uki-NEtS%gnB+d zbn;Btk7}iZ9O-%i*5IDSPE@OxCc?|;g;(PKmq5Z0)lO;4AH#Zb@iY zuro$nFY#n4l(X+LJAP z#;xk(_<3>3aSh*-5zRa^)yF4$+ffd1QjE9<^=c-=#?UU8##|>%W`5SxPD#8jxk zerO*oDYvRT{15eJUS8-0epR2uKi5vT-t^4VJs{biq(&E)%F?8{^1OMz>*!wo(S=-? z>h_bdcv!4SQ(dO>9Rst5s+4_9(OW=*B+C4eB!|&ku~-xz&DF+>eusa^oWvKGxvF4DQRKy;1g! bJ`JQreLDUomr8s;(r4fy)@R}`rdR(0r!mLT literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/regions.doctree b/doc/_build/doctrees/regions.doctree new file mode 100644 index 0000000000000000000000000000000000000000..4de01f90458cb5fedce380bcb85731731ce65522 GIT binary patch literal 6172 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 Gho1w9a?O_j literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/autoscale/group-config.doctree b/doc/_build/doctrees/services/autoscale/group-config.doctree new file mode 100644 index 0000000000000000000000000000000000000000..e0023bfb373866ba33b53b95e2cfe2d801037209 GIT binary patch literal 6395 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 JKBLO6{{p0%oZ|oh literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/autoscale/groups.doctree b/doc/_build/doctrees/services/autoscale/groups.doctree new file mode 100644 index 0000000000000000000000000000000000000000..bdc6ef66d937686b21c0d5d599b8def3e30356a2 GIT binary patch literal 7417 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 do9!Q3O@Ote1b$f;rJ1@Pr80mq9^Ix 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 I->^RUPejHqIRF3v literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/common/clients.sample.doctree b/doc/_build/doctrees/services/common/clients.sample.doctree new file mode 100644 index 0000000000000000000000000000000000000000..035ed7cde4d2661f63f4758a021eff77247e8717 GIT binary patch literal 4522 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 Wq`#Z=4>qWIfnofgDg7&HO#K_KLspUi literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/common/os-client.sample.doctree b/doc/_build/doctrees/services/common/os-client.sample.doctree new file mode 100644 index 0000000000000000000000000000000000000000..145c43ce8dc5499b61aba77a36c6fa0fac6c1fb1 GIT binary patch literal 2801 zcmd5;{dXHj6?N^}mXz3W(j-kt(umSvrPNk|0wpDdQYaLIT4-J|&}A9zjx-zZ?(CbH zwJpNZ23oNB{wm*Je=2uYE64FUoWqa&pmVfm-pstY_rCkyJM&SjqtYd*-OP0?ZO4CR zGIt$*zuXv$H?ObHiO$urG(TW%8tSyJuB@X*^9>#KHd8y)MS}laJGR&m4VvOKc zz-?NMN@H+fXmx2K$(;dpVi{>8{BAxqpmU;hdZe|>wUeQB!Z<#?^$0EdYm?aWO2wU9KD16?yly zLgsHc5$$wKE6pvwKub6X+44m-PfN|-$G2a{-aaA3_V0=;k^RExyE1aNU#PlqNWL(~7J}<)*QYFVV6SWB*9LOzoow@CT?PoHNO= zbO7;6#aDaOip#=BTD}(0a+C^d@&Ac{T0>-SJO$SSTJUIf16rJhelRHM(KN{=KM6Qn zGiv!mYF+`vr%qycNA8378~+nhAAavXuUFjbsq^ZBx~R^TLs1S-o|q<18j*&>R7X1< zz6oZYuJ|L^wEaFHeQF9y_jWgzdo>v^dkEh;qNTTfZ%0n7)43#*R_o7ng)w5%>vX&8 zo-zN<_3rH#yX*I6p9u1+1rXZPCT>KLO%s4gft}vn~A>opMIxcBD-v4S#Y# zD@UbV%kondKTj?Eooe#a72l#YJIRJR4QG*-UkLa!boL=5sCVBE_(eK@B0Q{@J{$1o zs2wG)HWJG(1^juBCY_!+PA&bHVK7G-KJcb51pGyxR-p|f%C8KlmBfwtdwmdjyRU@Nws4eciDbf;V z%dcsEz2dL;=-dp7(AP5(-?97+r12PzhKgab@Rt}Clc4P17|>}miVzC7EltXI=!$Z# zxYh4FtzqSfbfZme#(m|ow9iCsAP`A^G{1QOpO%Gxf`rq-r1+ZyTD35oBJ_3zm$JMK z9-a1A%(D3wEn>w?_}eIpM)Qb|C4YzJ;mU7OI~Fi32ZQ6lXINt!rgB%N{M{|^f>)qz znp1pGxF$3i={i^O;)g zRJb?wr5Dp;EQjTo<$%^?Hk6*;Nh+<9((&k!uIbVhrTcGZj^Y0RC338g85qvXEZp}x z=LF20iOZ}AkCbKkE+B)&CizG=VLJ$0-4iDF3F9m&GEN6{!NWgF(we)_Ni+li#~G|b z78^I7eJ1y}!= zmS-c%yXq31K`lGwr-PgghL?~_3^JAv4(YU~ z`lw6~>3UP9dwYAE#l$I{*JT>Z-9931pMU1PF?V7=RLNMS_&NSLnz+u#{0qAJFd8R{ z;Fn6#s<*B10Rq3G`6T9FBUv3CC+atcbY)s?GL?1710$!x*<|rsh`GVEma6671^j!u zSj#ra(a`GI4~-Py$NYx_I)zyar8ND1-#ciH&yf%QaZnEF!bvBePJ4ganw!opf3Em1 z5Z3YWa@scjTJhi1W!O79@ZWpBh1R`vHlm?hPg^RrXGUuI9|8Z9mb?-$75%H?f0x6} F{{RIE0Qvv` literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/common/rs-client.doctree b/doc/_build/doctrees/services/common/rs-client.doctree new file mode 100644 index 0000000000000000000000000000000000000000..236c72484cef765049d3a9c15b40b206ed566e80 GIT binary patch literal 2983 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/common/rs-only.sample.doctree b/doc/_build/doctrees/services/common/rs-only.sample.doctree new file mode 100644 index 0000000000000000000000000000000000000000..c087b50e295fd92e6cbe9e2fabacf7fda9ec6632 GIT binary patch literal 3611 zcmd5<`Fq?}6;14n?U}@mlO|~!Cqyj?o2{sk;L!wQ9sNZmc~xr@Yk$Fm9CoRKtzB-6(7%YR>qb zSG*esVm?utjqjk^I?3coFz zO|%eoV(M|9%|;D$_uH&e9DS*svVDOc3Oe0{g|g^$Mvh2;__ARPkt^>cJeD<@1(5ZO z&er8IdAGbr9+CIT<8p>o2WRNz5|O>4E>GFgvgLiYyx*2^C{Hhq8tdnVtuKoW@NMq@ zF2?+Gx2ciQ1O1TdX^ZFoh8-zX0yL}9gGl0hMi1cz+k+%V$|T0i6ZEi2;g!2mpjXLh zjy&kqa*Uf~#$*ZCBmc!Yv_k;Wy#{U;GP(dadDGm;o94A}c)oO4$xqTnH<*--y@fnqX^L|xqszwOt(@B6uMaX=N{iT#TSrz5hFE@R_0WQr zn+Ojy_IaQz9(w2lZKbirEjRG-_j&7Tl+=dkf6iRB*8L!`LLt1ukZBCVt=!6E*B7l- z(OaM^MXtkzNJN~Zq3>ek)adHUNrZZbhA2ZYz3@2leIld9mYk6}z20~yu=e&VvIMYR z6#2gialbjErm2&`;?f;?8TeWxJaAe;s2Vv#`K!IE$CBG?y2bpLVV^FJHg5*jzqm@g(8Bdd;$C`*oYO zE9>3GhxC=(ef6X z2zb~@u?dK+j3Oz_Lss4B1`GLIrKi|LoCre#L2oq!17o9@AI0R@*Dvh~O8X)Pg`P&m z-G?20b{f(S&10M@`5aIUj zzG>5{2`i2gFqH!A4Bso4a~I2ba%U`xLYpEs*>_e15NAYe+7CTH!4!6|*E>QhN^HUg z$JEh&*ioc+jM8=zg<6DO?I|9lkS3Hyw3g9&ogMB&;g}VNz*iM*AdI_UI!Zi<5OfR1 zn(Jc|_L}TK(spqaY9vhpdKa6KTE`2GhK?c#A7AM9e3Wz=Qg?#}JT&FFjdf4&HxcI8I)PXd0exTzbwL)8Rbe5Fo`X)3JkaHV4=z>MWEn)c@ITa)7n|&a zNRlXV5NYHAP^Wm5nCt=Sw#lYVhzeIE90tgewphgzt+YcQZnA07Z3$D?ULcedLeodK z*vTl>ajO59&5uI;01a2u^9Tv04b!f(VF*qiLrD&LX*YHTlv4C@UJ8E!m`$1kbM+@j8AV#0Upb+ugnFk*B=+o?w*zh4H!Rl0%*n9@q zjJZ+R_B-_1vQ{A6ny-8vCGM$CeO zIWFIBvML%F6SwVe(04GGs+zZ)b>GvHzPkkRzMDt%Jyv1MSK<2mY`m}Ce;|*ugJ@z6 z`DS_n`dHHsH(9kKLaZ|b_Ky%+PJnGlehjj!E>yjPel{xhCtK`*DfOT~f67jk{kgur zJ|FkAjKaJ>J+aonN!6gAnV}eJzTrr}BLn=`^m7Q>DD2QL*ooa}3^l(PknLd9MQ_EuFr~{w>H{VwzJ`(eG?_EGMlWVwvTi zL?SfMd-VHFwjbXfXr$u#4`y)Y=nTf+ADd~59UUIv#YfPemi81X{?8fxMNZ4}3f$WH z=~b+Zzh?9|h@>-S1^+!4N%=DEf1^OFLz5f%U+oW3MgOqrpKQ#K0Urtf%IM!|YyLmK C;T}@} literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/common/service-args.doctree b/doc/_build/doctrees/services/common/service-args.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1027c1f6079a15a42febfb2b974bd5da3f4a0809 GIT binary patch literal 6027 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/compute/Images.md.doctree b/doc/_build/doctrees/services/compute/Images.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6002d5bd9aa7c9688d3559f56822e503bc8f71ba GIT binary patch literal 20348 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
    1. lLN`BC-2m`DK1vzw{aU(KF{{05tvp60gc0 z3{gkxZlGa5{qosM{S`o0XZLi=+tuHMm==62bZZ4jIa*SGm*|DOGzbQayB2YgRlXFe ze@OgdY7ZN$tHmk=)hhg_(3}%iYHG-(E7+#~CBXAp4`LSlZ;4%yC^0n!S1M$u*Q&$a zC#pU8oHC?Xd!{0yFFU_jju*1plVGz2r5opzscC#)&|fHqJcJ3hE=rs(xU=D5#=u2! zhnhh!R<-AS^4H6L+TXXny<>P}+;;tFs5{YL5vN9ylLDtyQmo$T)%h0D?+n z%0ntgsP;$XR&@YL?km-$i2JG(CzVi%S$qZc1SDRSoul&O^{4|8#nwUeS@-A%A-1nJ znB6DYhUq5dU?NS)?pZ8^>JU8R$`t%|dWY#7tPa=rc^|9~ia*rLdP{UisZ-yydxT!Y z+~Hl}9f=;>L3#n~QttuJ@ow`DkJ!NM?uO7hl=eaxAq(GDbf` tKIp?GNp0~~cqeRS*DE{QwltwS9H3j(QvAv)0WyCCULtiQertp2{|DnYI*kAT literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/Limits.md.doctree b/doc/_build/doctrees/services/dns/Limits.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..eb73f8985e05c742a4f65187f266ee6e95210f7c GIT binary patch literal 8041 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/Service.md.doctree b/doc/_build/doctrees/services/dns/Service.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..d1ec67dacca70f6b86dd065a29c144f7d6da317c GIT binary patch literal 3823 zcmd5<>3`f-6?L3=nQ`nmX_jVd6qpnm z1rTk3Ld?F0ec$(e-^#xKO`NCE*p8e2$j5#d|3;ptd(S<0JMXdKAXGtNqFB$RDipeD z9}|(9CT}@@nTJc>Izx@rPlQ>?gywA}k1R|K4Go2KY^l@> zMOX7!$>TJY`MDNeBDD5qHAAXY`# z0)ed>kZu88>s+6uxg?y+wc-6VX8fKFgAdSHjff9Y(>ErMI)wpAhe|#?L*rqQ*>jq2 zacL}weXa5Rh)d%gKe&`ziXE4>S~iZl{0uqm(@6jPtu7yvqkci#;wTk-+x#$$)rsY2 z%3%p|o;9AVx zOg8!XzrRu1T&D#lQMHNmrLOz^Sh|n<# zCrpt!e)BdRJ&kc90Vkhaa%eVzxe%Z|?n23(F8Pd&<2d-f;oRCBzGQ~B z*%n>cgEy~R6f_E7MIP}>7bbD$nhLS4g$C<)n(Z-QwXOUzr2Z)l^2d?J zr_oes-^N-@{EDIj3tOx^oV};ydu?#lbDs`@1Gd%fEBTf7rvo_pFgj={iep>g=~@@% zMS6-~g}3S{A*<4zzh3E%4@&pdaQ=TRz6QA3CEpL{(ptaox`QE(jL7}i>%=O!)a2I! z@dG74Xb~R;;$!t#c1}CT`W-!c1_QdDZ8OL3T!&4Q*Vy87HhJf zH7WR^jhW%sA;|%jDK9K+go-reD7CY?$!`GaM@n{Y95tZ5u_l;3 z8c|3{&KLX*vv{9;z}ATmqHl-@?tt&0Sw9JVUz%RCso~1NbG)Zv%{*U|QuNBo)?Ch#3??1iZaX+w*RKqtI4G9`ifspfn~s z)oK}~Am$=rx^pV;wWLYn7C%l6Fc1i9n%{W^s!U`yg#J*|{H``lX~>Vv3shp|pXMvb zqfyqy0L?0m;Fo-b-;MV0(_R5Vhw~`(_s}pD*L$fEV(Vi4CKV@nD%Af2cY_-ADjBX2j@p# zM2%M@9L9)}KSCp+=oCHvXq%=*(h)YVE3wd02*V#+rNgQ)&;?Ir^W)HdU?VVm6_AkH zv`D;VTj2Z&q+~}}B$>BXQkp*ruCUsz45*KN3dg3F{5-XQVN3W4f4WV3EbrYYu9D^% zkpQm|{29y=kTS`;DTNMinqrEfV? zD6$A;o2P!P`KxFr%LxR3r2y-QzqU#feZlP6;^nUcrY+G8RrU?I*hD;klST^DoxOv< zMa}wkThHId+flkPMf@FVR3~RONcb-8@WM#@9Z*^G^1U`S(7+hV-DsJ=kGa$^e#cvi zLL>PH^SFv!TSxpu8lm&|!1a%4Y;Y6#vD{BP(8LA`)%F5}py8ifp+--n*e#aqp8{G{ z0PB?e48^V!+)+F(a9`2<^HthzQ@z%ozn~*^e=aR8&1EY_sAcUU};b{Fa8JkbeiT3b!6e`#Mp-U!{Zn zHWRU^dL0}Y=?yLze?T$UoK~f(`3aZyRz(YKE%NGuCsIp&$bY;-6SzU4k^0YnvV*f? zXKnobxm|Q<&*lM+^2U|!FY`nF3(Q|j{+pbZyESTS<3+N+GX7rjKjZ<7cC5O8R;#X3 kLW3I`t|Pd5KoC)5Fhn)~%jJJpV#?l+{!{XQi_YBt04afNfdBvi literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/domains.doctree b/doc/_build/doctrees/services/dns/domains.doctree new file mode 100644 index 0000000000000000000000000000000000000000..87eb580328c5be8670324e0f1cd0527b1b3b6894 GIT binary patch literal 39316 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/index.doctree b/doc/_build/doctrees/services/dns/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6c0a009207aba4148d9af07ef7f5b00878f1a6d6 GIT binary patch literal 13517 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 b&?1nIP%nPfA_9M}L4Ej$X)%7I(d_>KFN=bh literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/limits.doctree b/doc/_build/doctrees/services/dns/limits.doctree new file mode 100644 index 0000000000000000000000000000000000000000..23c8c2967c5629bb2429c8c0c7bfc0634a1a7bc3 GIT binary patch literal 6702 zcmdT}2bkQ%6+WMRZGFwgJ{xRfx#2xvZ*60WZEP^u5M%oQ=Z0KDHi}l7y^*bz_RUCF zAPP7MiJd|kq!Q9dBR!;(N*d{;k=}dny}mb+Zg=mFgnS8K$oJXzNl#C2Ui;sS{=K%< zb^<#JT~7`9fx}gfZmZl6b1-l4D73~fKhL^+tIETv8dtC&hPM3ZKub#tL|jf8mZ56g zb}O!Jcy84Vp?!F?AD?=z3KNNuyx1{3+Ly^K&r6;u=uE^`Sp{Ac@=V+{Jld|c(WHXh zaKsj=sH{RaL|+5nba+Llr%+S!1PvtSjnI15RfOGF3j zrzn&ALK5Xt(aHL>6k|-DC>Ri3tS^f&6D|`u)~-z{SRS!XTezNsKEsJ6IB8~46t~Io z9IU{atc+n5)=#E7S@hwz<^=3Xl3oG+!#F1>BQpK96 z6~{Lhlyjtr6)&R7xNCD&bbM7zyc&`!gf*-qw8khWu$FbCZooR0v%*liWvrNj^)YP7 zvrZ?fk)DE6O02{7ET!;zV~KT^E&GV1dgV)OKBacB1gDDS7HdoNPb=h z3j15pSlWd!Bw7V#q?n(X5^M#J=U@{iWOEE#^5SIOF$bs#6`UbfiMsaZr51=T>8s7J-R zrdNf7l)T}&0_@0Ab}0t<-}mP-OwZ*p9HR7)ZGtKN21vv7Qkvd)_1WQ4Pa~rN4osUZ*s^OtLZGxUGN2Qr{b$vow4jzUcn=w56-g%5}S!pex%-RC1 zr3Bk*fYNF)LSS{UCjT-4z%j>_7{+jMjTjSijA(67oC8tEl^et1Oe^bsjjJO~tpT1` zrg2pjT^d(?Spxh7S3wLl3YBoRd&Y(XSP-`nSF*`=n$l^DD#g`IGa&{Q#W0S`2r?NY zQ4o42Vwj}WZ`A1Fk{dX&4HZ=JqS+b4t=0mC_y~Ms3OBF}Cq}_gku3~M86ud-RV{zY zaDuAk`ik@rj=o__Zs9g+`3~tAT1V2(=giU}JaWqxxaE_L?J(7l0}Z{a=E}jMEUQ;z z4(`9fJOV2b$M8t11O+ycRDwWyO$^skC2l5=X1Px`4gza}(Za6m$&OsESTy$NBqisQ z9JfVa%y_l1E3HMMe@G`i(Kl*brj@#fSFhff6{)VeF{UYzHKiU4NAqG-G*5s>B;l&gYEq+sdT-*}FODLiZG%j>DN9a>%_P(yg$=;qNS)=x)vr+r9M%2DMLv22b z=M@^Yx5n_we{W#~&#N-NA z*1ZbXwWPGB@@EVhSLY1l%1tF>=PpCxSBgKD-@IwiSbJ#q$X@fnzJvQl){?^JCWS4i zu;ARzGD1EePi8OW7hCha>`zO{uj$wTwhEL+6f-K)zZ0F&htGY90@F(RlbW=J>wI5_|=#^VJx>h9T-na(Ck-Np?ev9ll8+`f=BE@kAJ!NKB1!Bs6|og5R-D#lsNinS$Rp z@F5i$_#yY5+!fS`DhGcE;Eyr z>y{N8Rgq4Mqz8XxD?}L9wikevSnUN%P{Mu30yPnph~2olJm5*Ck$wz)F;`BLe_@Q<&>OH*FB!)k44Dm;foHJ z3rN<=ec47AT@HTXAY)gp$#%Raw{^lzkB@UtcAyL?q;8B3`9fdUQFdy@oUptwRqG

      e1)%Unf$Mvi3E55JHiws^ zG1)_wSU<0pId#C4$Ccnbl)WfF&Q{@(s1}9yWWSFLwUcXhDEsNAhqyOCs+yC83AqSm zSjavns@BZuMpSY!$`LF`pSgY|NR>}WlzS&E=~FCdms?f2MC&f4vQ=D9*W3(w6+^+^ zU5XEpb>OwahD}=?IY76I_@s-JDncVtX%?4}={DAG2Y$sJlgqV9U32W+aaXxvAmxc# zy43ap>xjwmThlWw2f0pS2<1um@EBW#p;o7nTW=#%6CiR0DjjF@xj!yfYGX;@HN_(b zqpW&zl@|B&N&0d&T?B;mElo*ozZXmTT{YpV$x=x|{KGSg(@nqQRI*A+g zyInXqko2jzlQK_g_+n_4%?Z~Dg&ai5{!Z50+@$hUQfy-*7okh1(RD{kfXmbIPOM>l zI7>q0GzpK$EQPXw@?)%PjQhxBQLTnhfT-1dRnKBkOS@GSX>t=Hq|BAvgtFtTo7yKE z8FDkq*y?m@OiWA+)uuub_p+~BwV=Eze8}JD(A=@`1BZCh@3n^IuV_VA{zNRmD%8yJIQub zW=m)8KKdsJxr<(E(KNX{53gf+KK{*%0VI8w;0a?Xe!eY7W_y`%BeuBdClS39%H8^< z3-GU4i~H94SYD_NAYG_=!|cZaWX6#NNB*02Wt)VOdr-9`_u^mHPOaxCc@f^k@?!iO Hm52TT^Z?#Y literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/records.doctree b/doc/_build/doctrees/services/dns/records.doctree new file mode 100644 index 0000000000000000000000000000000000000000..38ed0eea4a43c0dd61c07f60187ce62b03445e23 GIT binary patch literal 15192 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 PAL->NV9^l2VX^<;U4|a1 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/Roles.md.doctree b/doc/_build/doctrees/services/identity/Roles.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ec1b2c3deb8034787babd4ca6480b736b535f000 GIT binary patch literal 14598 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 D+9J-d literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/Service.md.doctree b/doc/_build/doctrees/services/identity/Service.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..57afa352739437c0c5b3844148c6b60f74a69fc6 GIT binary patch literal 3531 zcmd^Cd4C&M6?L7;mK57@oTMbpHna^-MrQVpFGE?!HO0IUQ)QTFm>x3=^U0)|@Jh^~%i5OcRLiN(mJ*(7;q`^q zya%TTU0PsEewL;%;%SwoFtFHW^+5P49r;;mvlX5h)m6$^ES@$zHFRk07B+9;hk>T$ zoDMhS9IK45ugH0Jf;a8yriz;*KZ+OqfPG{JB35(9MIyWW_JDCBqPEJp6 zuxgMc_ME0WTsH4VTxaoK;5W&%4x+Ri3e1qc9e9 z_u4Fsl@&zKl(P~HJ*zQI5mFwBI$Z!k&74*n^7f)eu1wcROP-X+VMvd`a$`e_qmjlD)oL*ru{2Lgq zOW{m&?S$G%MGu7=q3bRLXy|mfhH*@eM^I}z;l4Ppov@!o!j7S$ zxgFJY9B71dCZChlPzRN|CiLx`U=JXmo`R~qn?LIc?5qYHOjB9ef z=#0fNuh)ylU^zW%i_&h8OHa8$UI+JA_cqJ=J||_fOiaHXDf{0B-+JZ__62TjUjjdJRXl-xF3oNSe17`{0tk5%z^Gz8z06KBhM*zc&w8=utFami(G-;O%t7>6vhil6AO!RK5 zVk6?9{wR+!=m*+VG{|YI!B)mpcy@~b_=ct-U>t#Il?K@NXjP1SKg6fKMBsK(r&FaJ6JKAhX zW6dR=uljb7XnH5|Xq0s^M)Me};7K~9n`|-SaW8|~!1+MXyVxw&`@2~!;8>G}_Y-=L zMObj_MPgG#^f;E?0-}I|iANgby^ti5N2WaRzO@QlEJ2hzb-OJuw%I9>rYiM-R0SZ^ z8(m5s2NCkN!XjaMWbMu?K$&nkiFWIg&on=OleN7%d$F{-5yhCaB%POHo$nRzOk zAA-;T8@{0@012s$v%a@w3!FZTlpG4Oe&S6^O4CQc74~=-cU5`pqd2xS;Aw0D!gvu)|_2gl zq2M+{9U7I=r(qdVhi`bI&%?;$0rXa(N?iJgEKD)yXhCMH*vy&==57mJ_JaLS&Q?e{qK$9H}$U7B77XFl~wMsj@G_ z#T4=M6;{biw|PH(mDS7FZ9RVtZ-?>T6w%jNtvK1#AmJPAkQaoScR*#y%QxGsh6YaH zFx;YVVJ_7S?|6eSFp|E#R%J{3>xjO?D(w1Yxc)AiAIs$L$Pdh8n4K;Ab1)dJCPOUvxaiM7 zY_@Pxx9BH!C}M?gc{1$D2tPwVg;G>;kAB8Z?Pp`E34Si0U`uvhdAkh!g3X2j{Ssgm zBsL^unW$gwu#=-U6C=@}!I6>PSjhew#awb)l&YrRxb$0gTT!+UFCs0zKSXTF59oK> z>>va?T4{9sdpkM{ekKF(5ACeOj!lnn6ga*({CX*$1iNbmV(+Lto}tY4 ze(;R8$6s>#t2~1#4>9t$VzWWgLT;B|1`xjxx{$c6HlC-N{_fI0*qp5kNb-N?^slV5 F`X4Ly3W)#! literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/Tenants.md.doctree b/doc/_build/doctrees/services/identity/Tenants.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..4bd673d1c276677bfd9febcf850272ec8a409eef GIT binary patch literal 5239 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/Users.md.doctree b/doc/_build/doctrees/services/identity/Users.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..7d113d58553c5861eb508baf6f86fbb5779f8b23 GIT binary patch literal 31154 zcmeHQ378y3*$yG+pBDPldx~?W zOqo)4s!qenHUmfe83V-(vZ|G!p&5ZcbD+2oIirrr$d<+{+0D*`KWm^k9r?rx{MqIv za2m~VKQmCwnv#)bHCrF@oZ&`xTqbA_Bja4xbq9MBG&#=cR^vH z3YN6{f$qY>9AkAO|3U7e!W?6Bld;TQTv)6YZ&#tGS(r8KR;p!K(ON3i4pxur4;2d$_#H6{#lZmgV>py3}{nAs?evd;X&3Nx)x{^5m8 zsnL+iP_qG#I3o0q>?zDDH^&JV_(yFl%p9)5p>cim#=@+j((q=XKOM8Nup_Jf*p2>5 zci&QBx`AJ{(Lc_eU1}DlRVy{eU)?_y#B3b<$GcNq*yn_vw1MENRVU-02=lB7{ipW0 zISox!-9O3AyDQv7+(X^N3ey$RU+Y57bv^D;8{MNfy2osEkKO35+Sqi#&b3TVPV?B{ zY(q=Zmmqw~6_@$zL;qB;bID{)bEwoDa#>0Jp??~(3sYLW-NVWB^fspcfO|j*oc%N0 zc_p%+9vib{aCs$3bZG)mR1IOBBcT0`SKTOUCg zTDqWKTagVWf`&7;B3qZNb*}7Ix1JfU*8()nK~{Y)DhhI0t+A|I+Tvsrv6U^=%Gt5f zM0Uu@!j&qcHK&|u)Hge|U{!Xg6gcHx^G%L%21=0%J))ly{)$aTs$R@zKJva|Rr zRD31PTt#zBWCoDRsM`Q$rZkMWvw4Osl!95cNsSF-ZBVWVqPRK{8-u({8PpqeYgS8= zZ8)`3t)YyE95p8xRz}DZSWpKw&?q6o%Gs@zhFhWaN{x&`%lQ|yoUAZAb_ahWJ@^^j z{B36Bw}bGvCmrSgjiYQrgHj6pAvP#Nx#NpOCI>OovUc<>&u-Zn^wt_L8G203n~#J9slqK&6JE3rATEgw2T5$25f3+rBbt#d6t`(b|pG#la{DF;)%G3_6a& zZaT;Gu-W761-jPbHw_ME(ZvP1giac1K{uUtifq&9fl}xEd*t++FwvIK-%4Bjn|MH5 zzd4lddoVU|Fp?QYZ@vxnF%kL~QU(r_Co>)!rHgtB(>Q=A&QurCVN78Lh&83+U#uhg zmc`lIqt7>;Kf8TM!f{0igC_9(OJIa&hW@1xKwWqOeu6Q?>{v$sGB|o)aRzvr8uTv* z%CkcM*_64JZB#{6M1gTd=szc-YKJCfcyMJ~mHSt@`)OcY?atA_*e$C3=bFH{CiJf* zt1KjdyFT{Q#`7S?70H;nu8o#Qha2mkG95>nrq@!tUgcZB{sV*>B$M&Nc<&AU6Q1_L*l$oTI8 zjXOjCy)ljVb)#_yX}sTMlYUp|e*nPx@O-9u2A}Xhhzh&Y#)7uBAL^vCx_?hnxf^l7 z2k#j7!|?Zyg#JfC)Fqv})Zf`MOjjBGdryYgmq5nMMzW!!r)=K?)d!QZeZP~2mF)*sHjDW~$o6pP|A?}Y%0o%n z9MtuW@M!ihDSpGXR$6n@07zv&M$gghRLI$|H~Nq zSKXi=p>zJaM?LK~q5s<$`gh%+A0_nf)lPp1{XYV@I2CnRDf@qdcE3s5=+B+_tNWJ_ z|1YqS#riAk^S99dI|#X?@Q0**RHlD~G6heJkyx^W-lU(n^DZTmsVJmyvLh}+nPw1+ zdqKnvd}gXu@Ftm(%h)^k-ZgjpBNKj?5j5&Vi-rs zqEHRDpCayW5Ut^Y*l-6BEboze(r`-^JWbjf?m!fJ)<6g0I+SG!p7WF%B!uB`3R;A!Xt4J%25h#I!;bnaC|LC6DIGGdeX4RC~R7&)&2XB z?y&?mooGyFCGudGRY*cPPO)>IB~JDo5w=<(k2jDe!cNeeG}C;n&xt5keby-MQw?r$ zS2QYowxd3&_T(gDj>QUSX5621a$2~63Qt`E9Y5O6s<$o^$K~a zfi$}I>rGmOmaeCvTy;HNaR&@;aS?RY1|?Eho0T&NocG8kspgrAl;&a8JPU_m>asH=RHb9Hz+XeE6#&RXg*p3$+;+iea=G?N)f+NjF33J2(RnMyx_qhhQI6rDY*0I{O^|*$Rd5=7j8eXdyX$Drq=b&4akLMUWg==8}S>(1etV?32i!F zqzEsL5g@=z^d>ET%Ro1wTn+S6#l6|!S_2_cTLyX=A@d%2Bn|X(#Yi)-26_bwBLlq> z*P+~^;5kpJLB3aYMbCQ_hX%TpsWDJX8hJHxVW8I_3FWo;jSNI4-3^p%G+w88uaEH{ z#~bt}t%hwh-l&CaG~T3`w8$t3O zi6>3_b_Gq#w7P$Pl6?n(&8RFQa|g0um3JZuK8GZf&*L`=KeA1R zACRX3ncB(y2I&im^hJXdmqJ-GZ4N~Wd`ZC`uwV<+WbNjG{9o4Mv^`q#e+A_bfwM;m zT=^=pA^z8pgz|Nzp;O@=nI^@@#5N{E;-|WAD8e^m1W53p-gHV31^>5Dt}gIx#r=-K zwH3k!EwU*FW)BfI?~zNg(!Z-XX%@E9zlXxeWZ%biC_hl}oTtQ4k?wB5#P={YJWfS{rT!iu)1vig)(Y_b?En)H=sVA-bJB3XPwYq;P>HeO;<`FNZ z^9SU?E`LN4%AXWF=UL)p4{O8*(N6i#3i%fUX&R2d>P?zyKGx@NC|7;{uDJg&xWy{` zcpcWY(Z-f^rl=Qtm~^!}z)qFgsBTC=Tg!b(W7rHZT&iLCcqlocNg4$?KreInOHlqefo9%$?ZnJ4>v#G@C`_Id|Blx0T zrXdMsx=NVytO7J+N4?BY$e9Mx)XOZrnY>eY5y2iUtoX(-lBN7=>D*;VQ6X6QT?0*pf0bR{v_ z;O?#j_AmrY744}vX=AlQXD^g%6)jQRy$x=0vsMurxR$o)bg{n#S=nkGkrM4>oKU55 z*$t*_+*T>;kYkq~!Jf(**D#i^U<{(yHoN1Jgt6xBQTAjl@1v5W1-G@lFAJku&f+GN z{S-c`<&@;C<^8)7%zIRvwR`~6_=&qEo-9QHYWYAUp&W$YsFuk%RmxWg~FsOre()ol*5kPESs)r$)OCU%p(cq z5G7iwpoKDl&30*oM$;ukdD^-B!ygSAdOAd=}nq*KGy7Hl&fZaihGK| zEzUs|tTmcpYZ_JK2DJJxH^gv5Ac7nl;?V8lnycuQk;$ykJ z>J^dVmbf8@eXHH!*FHDf+C-;p2z~Iu17~bF_~5LwZM<3;#um_th%LJCQbbq#YtyOd z7HbH}O|EulkJ%V#C9iU`Hn+v;W}OHy-s#gWFqHjOgmM(TohB6v23gYH_C3g4=nlo3I5tOS9AU zV<2Hd@$ht?cDo%>nK<;Qn8Eq?`vSATSqc zLwOp0(P90cN)*>xtG(8t{`o55(~X3tQMy2HI_oBCZa1P_8>MF`?x4Zdbr*eNw;^TP zT%D-gM2x&gUdbRWDQ23H4bmYLM#GC?Tw}go!E>HcgAX}f(eoa~q7RKQ?bFj080e;? zl2MeRhFm0}RPY-ODab6@52l`tJVn?XBS4U<-lX+twc=waSA*3Qw{CE)!7xlpiH7Iu zabn~>@=6-aSIjgcYcN4!WUv6&p)?dc=P5PF+3bp*_b3((wuPxNSf^C76{RrPHYA}; z;5RZDnRPcU;$0NuL7t2CCasQrx(>CFBZ5m5^O;G^OS@xwWI|0YW6BZ1<+upt zSqg5RuFH7Ntkl+*<=KSEd!(MU@f8Z27HV~W59vOKz~<>XrgJ6oV3(_qgmSfF=R8ZC z>^sH*&sE5445SIUYxO40G#~5pJd~?G*D3Dv4Q_E2#{sGjcVztULjWT)9-J$~Zgwj= z?zo57z8R0{cIbd2b6y5d;F2|C$Z{^b2AeBsy(~84V;Z;;V52{V16XpwHhr8A;z1)E z7#ONIYvqzm=(wSj9c~7AxaSN24iDhgZg`heF&1#luJ<6@gH8CL(Fm3+4hIF;dEckA z0+=7bK7K36`f?UKP$+l6HDrj*>v5@!k-gn}Ux5-M*$Hgmx2GH|^_*N!v*tZkNYVvf zpyo~2hIN4(2;BFi>9^DLFN7HpH8&y&&$C4me2t38itLp;7F^P}_M!mBBckW#!Yz zD9qB+Z?eUa&k!zgwm?3s2-tOAcI0!o)&~n5yOhu4#Wk(o9_rN(2B8TkUr zp#K+-~^zKrX_^g3tIjo=7?5Yaj_{ z`8twNzJcE;vZ%U7R$+!QihR@LV5c01Tn{S7w+u#cBU~dohJyor@B&WrbHW0nghtk_ zZ{=Jad?%|rBO7&Tozlr{iVmC_l;d)_{Ce)6B$Pd{!-xMwcyJ53d|P>UhR0D+1%F2Y z`ijSHk8tu3sK7$sMH0&Qlw@xENJ1A{4~E2%8~MHx{(&WY0Azg}_MrYhB#J$T!%bdo zR&*~ea%nPU%4D=?I2cLjTMvVpZF}1Y$&XL~{eO%E%WdRoj^}t3Ts_3S465{*2}3~s zR3RTRknuF4(HM`V_|GG|@-qedxdB5@A0HYvZS60#AW^&WD01AT+SdM(m-Dr)T^ZH( zuM~l8?XPi#wKlysPXm4S(7pVYhMMBymb)ic==dMQYuy5sv0I>W@>jz&p_K;6-;`meR>wsq`#bUa{tW{C1B79kDLc|M zJKz^S<5?+b3^k2sJDI5pwWEP5?vIBZwi9RIr2I)dDStt9;O~Nyuv3Ev0)q?K-|#Vj zspg{RLDv#hO=Pc(c0Gr7>#cg0=?VR1?6Xps>(?5*!@A-fdf4Gd9C_4=7VDKOPd{Vr zy215pyO1*Vq*5H-m70+v=ay=<5*p3&iGuU`+S%oBD0*2Zn{}tJS%2E{Rc$`(MIGie z+R$S!>2>st)3vOvSDb;Ys8{Tdd}1atJ=^`w!gVOKiHYv-$(ngwHG`HpqZZS^lR3DK zdiJ@vZtvOY`)qyZG3|TO#E;AQWj?Hh=4AmAtSRF+>XPWN+9i4MU?u8q7Af%J7I6E} zekUzRw3V_ma@=Lw-R#24McUn*9>vM7ioot>H(Z6XyWX4gWGA&={33f0H}8>svd7s| z!4v#!j}z11iwI_iP?FpdWJC46k%Y3363Ka%2L)+$I{PX})_@d`Y3p=K*%1dLGQ5Xi zR68AU#+SK6IAoDtdR0OIv+AMd500fi4lf;C~~ znDeYaR8bX9595cLRlH$nknfDi$TC2ontGJNazlaRjYORk<|HuWV8C#I^%TMuW|$&b zfjb?|^mdG*R`;J3soYDPv^bPw5Ch+_jO0)bVe&|@E=ypvtgq6_!4wzYSY!sleB;L! z>;UFAOtYE6jGqM~1-Fg?b+yb%une~5+4Jx#Sy#O}M~4jrQm;DLD!`Llnq{^+8ow;r z=|4*+$Z$3-L8xrj96^O74XF(5MGG1qF3gTql0%sbYaOP_ba+E_kXQ~U9G@JHz(puW zDtNAV%YVP_P@VN%!x=sS);Vfx*mlv z?e!84`}h=b7-;EBb|yQlzt^-j70V+BXQqoA~@wVgL}H-4j5eBT$%ui)#B11{J^$EXDH5@ z7H1TM@oW+=3upsy@WERv)hZVL&5Ld9n{(>ib%ZShc)T|N+*@sd;@9icb*5g&@mUPi zv36U{o>{6n)m(dn;zg~}{f~(_J zXyjvk53qcTSGnwzCyTjp<&ZXJE9QKZYs?8QLJ1T+S3F_+1(F6yV`-X+zA?_#?zO2b zIwa~Z(sj2GxbF#JzZIn565Ehqv0ItuJS!8G)s`~RSYD`r7a73fX)pkGR$<98i#PH) zW8-|Y7)_lmnRb^WwNe#>uJQ!7s9+F?Nl(DLTGau7Wpy>PmL&;V47=To&&s!H#vlgRTS;(XL!j(eXSd!AsRud>0&EatALd zV=sKo!L~iLN<$9bztw$5JO#3Q;j#E_HpszI#xVuva58)mv`N;(RjOTQJ#35~Os-Y{ z)8bQoc`h^cj^COK(8Hp$P|va|$-4xQn8l-njb zGQ7M1d63}-B%!=e3796C>`ap!4-EM-GOZ`M(O|tuv0iMj^j&3I+#&i)6z(PqH(N!w zHBGr+ss)qFeKX1-H`yzeybKu-`sGMMd4*ydp~)yE^hyf-FT0bx(y+QkS-r}zDsCUm z4)ffq#9wWRo0@HTF)j2OEt}jzuSL17*$7cyhddbR^+<4pQ3)6$kzLA2Pp6UoebFLs zG_t%&Wx37BQv5eFjm?ZkD4OVgvkLGQE5MkY=2& z8~7u(ZAKPMA3V8@)F( z`31fz-fWkYj}kZUk-Z&x=`Njn6g-LGlJoow##)>AzL!V{C+_!++>oNpoLaCSnURJQUx#dra- z+3mSfJ_WMvTImzmrSmAcDC9#4$V3?NMeXukElW6@d=EM9{u;L5=jB`t+e;(6{y-5J zwm-yGC=ctsd3CMESt~T+FFeSPNGR`7oMa6DSaB1~Z4AfEenNykeJ$>!w0{a1808Tp zI8v?@bDm{L5nBQMa|QW@0Vy5_H<*kl4pr&1Cns{M*qX+N%rYht@Xqu`)_hiBr928k z?Mi6~|5E99Ds_2e!Cw)tZ~OWG8g!ueZ;*uYTjiSbtWZ?7EogtIP`@`&#RJnJ5RccE zMI$K83iCg1F0um3i5z!{2Hq@Q&d|WSIx^U7MPT4%a23iNy^r3c8h_bE<`OsWk$p1O z<|%lBpN+Mc{(K@tZ&q!QTYzk6zYs|%iurs zaNX`Z)Qi5eH&e5alqM+qpbUoE7fC2t{6=mmwT+qzLqpWw^qd9QEEw_4vC=->&{ zyL`309x|!bYrRn$#q~0)PF6p5#Hqpz5L2JPuuXPiZ9gZ6f^)m}+Ika|EhS%=>!lqvWmp?tCe_qBlAL-`afNcfekM~=IX zhVrSroTZ_Bb7Zf6MPMkOhASLc)_W7m@rFyjfC68Kk^vIPdz2v=%4aA}f~gJVn8}$$ zFrnN*^DLCX2xlX~OE8sE&a;dtQk%bRP^f}|((zhqLTJr+rQew_9@Y{w_ONq_If;jz zr)6y(#tHMt!)}Qz_B3Qh9(F#i@tRCx!o&Dkg-PXsPhFsJ#-}#oI`XM!;JV$Xs3(1D zkf|A%PDWTZ0R{#vAqiy&zmXSFxJkTdSb@te;C3%^v>;KTGJ+hpN4;p2m-q%5zOQs^ zWD{2r=tUJ=Vf|I_b9!tS6MfM~(IP+EOcHsIQY8JTs#poO){kN~V?;1#Tf0!Lp%jLw zBMD_(Y2`f2iejlJX{X>Th!_yvAgzzwLP^c1x8~uhd?9}fykr|uBGds4QSb?^HFif)*3101~V&K)DIDpigbo4hW90ozLs9k(w$SNg9`8h1-O9#v+<%3-O?Dy3pMvf{Ia?VbEEGV^2n9E z2-)~@VXRC1(L;H$qP+yalzzM}I2$B4Dfp~P*?itmUW$8s;SgVSYY7|3&5HUmqRxxt zFcQ8Td04$;$-+v_eBf&kA4fBvQz@@Q*+5~g zj0^(-pM7jfRbG$0!d@;GhgRnEjXFM;lB+n4kz8Fy^KN6TnwK{aVm9nH?C8h--iZ6F z@a0HnoSK@C`vmePr9B^qw=3g=I9kB(bOv&p=FV%>$uSb~&B%1`!J)iTZM2EcP68a~ z>E43e!c?awxAS7Qe!s0z8XK3lDvi0OW(KRy7N;t2Lms{)I2ZbHu0rcy-mZWdw^YT> zTm^dv!KN4HS*grt3*`>NxoZasOLULKAgoj8`ud=`#aW4R6_@V>SYaW>4uI%_f0bth z-^J|dWoM{4D(@zx!a{Csrk_ky9UNtM8uA|GUtQQ6C)388jmhZWNkV+xNM*Po@8!)r zeuA;q92?xmV3YSD4^_FK+#DMpjQuu{_ah%6h*MNN2xo!sGQjhMO_? zD9l&CYFY8CnmaeZI}aLzeiL7I4diRcySgx!?i05dUq@bHU)y4A-MTe5K7p^_*0jYa zJ6rMq4)XF1LZRKqR|&Tby7($$m0w1bZ!&vY6)*Ua2YI=q-Fm4I{1!kw4Go;dYt#o@ z=K3~~r&h}H9bU{wEF`P_A!J@%*vo`Hz6Gp}5zN|f2ko~@<+}hu5ZU_B;0WY<$lO@i zRU-u-hvV0M@wr^bys1>akJ76ObMT?oIO_+v_DA>O4ie_PP+ed#*r~DJ;_f!1h?XJgjUi=Z|jS+|*bdEkYi;t_~L#p_eT48q7 e$prEvFd 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 S2&8}0Y83FP5Wm&&!T$oG&JDl- literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/roles.doctree b/doc/_build/doctrees/services/identity/roles.doctree new file mode 100644 index 0000000000000000000000000000000000000000..5c6616b619ba37901d3ba9b061375820bf0cb911 GIT binary patch literal 10991 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/tenants.doctree b/doc/_build/doctrees/services/identity/tenants.doctree new file mode 100644 index 0000000000000000000000000000000000000000..88f92449332a1dc24f84551038b96a20acf248d4 GIT binary patch literal 4156 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/identity/users.doctree b/doc/_build/doctrees/services/identity/users.doctree new file mode 100644 index 0000000000000000000000000000000000000000..5f42ff8cfaa8fd52eb5189fb3bdaed1b97498a22 GIT binary patch literal 34801 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/image/Images.md.doctree b/doc/_build/doctrees/services/image/Images.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..27aada00d9574b68b70719cd830d6fd93eab2f5c GIT binary patch literal 17718 zcmeHP2Y4LC^*6R9OJ`XwU}La>jckx?EFGrDG6rEAFgC*2EF8h&w0F1Cu5`D1^L9_N zl57$}NKAU~y^!8}@169XkluUm?f-jkc29dJ$ry0{@O=rN(eBQ?_vW>EGxKKV9x}gJ z@{6_5EeCy`U$TP?e^zWS%&22F+^FV9YC(_L?pYN(oT}P^S{SL8o?OSgdGl)3k`>y9 zTd_uM)jF74jWWyde3bc~5fmM}Vj0D<6$C(AG?;5aePD;Rs%jg|Eyu^G9cpZ&WQP_w zQ0;@cR#XVdKxGDVi}6)&R6Up0GEMUI;57Foy8gvB+LhXjf%EfK4@l2cF`w`vDJz}bA?q4{Sn^}S(pX1xVg18v#sd3U4WVwP05 znX$r<+(IpcS$ZP1p~q}1)v62^sEtGBqC{<{51DNRt2j<<)J;R?0@i=;km_?zw9FO_ zpB++XI2o&EE-bsAtv2tT4`Q(&t23SX4zzPtPqTX9FJ(KU&W3W%iPX70PM3I}W?!A> ztaA)!y>qH_n%N?t>U;-`-nYluIOLo@iz527perzvl} z`9^g?q_&|!hg&(PRS!gxD?i`vQe?%0AAb) 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

      CwFzVHpo|(JIJ$QPg@tS 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 bs%|8zBn<;(nVp^K-JZ!@{dy*w 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 AS^xk5 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/image/Tags.md.doctree b/doc/_build/doctrees/services/image/Tags.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6cd78bb2b7844a6c74c89e9d8511f0d2a5dc05f3 GIT binary patch literal 5065 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 E87LP54gdfE literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/image/images.doctree b/doc/_build/doctrees/services/image/images.doctree new file mode 100644 index 0000000000000000000000000000000000000000..49fc9f614c51916a51b4d5c946d18d5754913283 GIT binary patch literal 12799 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 BYq|gc literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/image/index.doctree b/doc/_build/doctrees/services/image/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ea04800dc5ba8389d3351fe1d87457767116c5cb GIT binary patch literal 12154 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

      6M!LD&C2H)={26R#9I=8Lkvr3{Ek!R@oBN zT@0A9Ef?!m^i7NUI)QbVOJe%;{Uz!f1ZQs;GS@)kp2|Z`m^W6(^7>T|!AgA-VCFJn zz(+%T!^=yN>RVLYQ5MB;OnsX$nak*=3$mDLSNOINf%*>0cbIFjyHyK=+3?>@h!zrQ zr9gd`UY5{ZELe8RZzH!;-$NOcy0jctYWcba`|A5BhchCo(MTHu|3CxxOj)W*ya2DT zD(Z&}yNc9CUlEh?^H@CtD=_yTp%UMlidz36M;uvJ_t5J;Vw;0Yb@;>kSc*R;)E(wR zBmtw&nEDB$WSzsf=_py?DfLq>U4;*mt%*D?1ajOwKB>k|Aa0CNKSSkqb1hh{)T2Se zjPjg;sGkF9hq{(f(u9@91|XMnz8adyUDd zrj@MzKw#b6q~C|Q{z&gj^JT}kicml-#6NM{ZUh<}3mSE{sXr4Kd7M?uPdQ~wTmOQR z!7j6>*+|u239!Sw@N|%LAN}r(uRv0N!<&7exfH=Bz-P|nkNBoIP=81HcC&j7DJqsc zp{swOV94xYyJ|@BpIo}wuV4ovFGJj;;H!V3Y=^m+%%>hZ=3(v4JTQ(OQ&Up|wQ20< zR(XpS31bexeopO0P#6L5&DgfQjk`Zwy8lDXr{V>!R~=J()6X@{(ra?wK$2_p>rmo!&a8Psk!7vY+wdeI^j(fGiktjF}lggY#nt6E1fxRnaj5^6M$uT4@t z_#LUG_>0?*8r`y&kN1vblMoK={vFX5W9@q?1R6yfgul>&vI^$l$t*n rPp*r?6-l@b3Ez#I-N}&Qs}%qpQ7iG6&w}9UCbbGLky?$vVR7K!mZ&G| literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/image/sharing.doctree b/doc/_build/doctrees/services/image/sharing.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6a29cc3fa6bd112277af894aa65e8482d5a4bc22 GIT binary patch literal 21762 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/image/tags.doctree b/doc/_build/doctrees/services/image/tags.doctree new file mode 100644 index 0000000000000000000000000000000000000000..b74e3100dc0e9124394f991881558e17cfc5a2a6 GIT binary patch literal 4579 zcmcgwXLKA_6;@=gw7a%sxe{FB3E0uviL^yunX0S(k9hwxz8 zw&8TFHaC>jroq}|885ppBo%9uBY0zTmn*(#WvLdi)iP>G3vQ9&6xEX~Z;F;2o!X~HT8Y|np>4%oyO{;2bjTI@s<@?I>6*r}_LPaAJ zXRtx-gkwuu9n+eM7+{6egSBFi4FUgH0(VT~Unh#J+5gD>V@#XCWN6J~$Tzb-HUJml3~dqpY#@7o*5n>ARb`TgvUBtW`jl2Tcsk3F8w$VCO#CnSr()By) zv{MwVh%I*=kJGsm{Vj;G7SfTb}MatwPbLi=@jOjl^X zbdFq~wvJZbX66au9R77kp%;E*iR*CUD|Z?hfX(-Ma#R$4?t5 zT?1ljG2KV|dHUM{cYoAaN+SZEBO#jz(ffkNYh$`jn_mNTCljMxyS~Df>wHKAOQ>T5 zFcO6h9h@A%wvmWT~u^`#Okie zQP{nms?Y;q^MNrP(r%o_zLXw*GDEJT5JRrnaQ)^~nd)Ha#+XcPeg|M}OZ+u1Gqz>5 zX>4zstHiy^=uOGT19m*RIn3D~R{gj}D>fiv|v&j{E3*HT$3A<^aC+T%{UU%RQ@F^ zT4~L2dqSQ%VqH=*JTaKmjFB9WpVo{ZCKT7r$yK_q=PC_B)RD!tL-ttCYDd?ef`j@# z?lls@iRqYTaD>nXgS6^t@n&P1(w9aiJW}1#^_ZN zr{}Oz(@{x>E9tp)dY-0-t;}2Dqyc+AI^QnBg{Juhb$X$O75E_vH@&FFijJLi#V=8dRLAE>br-Qa0m=UgrY=ZZc!&$jrH3L^EN zY<>tM2iRyTx($#ZtrvC7Ssmc?VMwydjyi$a4Jk<<0axho9k1nQ%RY)_!!uUsX~3|> ztqy&x#@1`zTaKHEW-8tRUd8F-xTSe8w*CBzZ2AOS&1W4xM@c^$`9$&u-tTgYs%yu%W+NIGPtGmyz-e7YzsW;bQ5hsbu;Y{K0D8rac`PF zSIhhgB#&(P(Q)bXn!ee89EE;|^fO^PsDOZw3~TdAe)=I4*u)+|3~W@g5N zIVF5A$xpNZRjia%`iU-zp0Z|5;k1Q|KSe*q4C#Aq`Wf5Y%f^x<__?@^4eNSkE->&5 z*6-N#OMvw;Y-700g!V7!B${&M=GVyzt^QR;ip>w|4@q>Z2i&_p8o#u$BDjli2hSdf5sGz33n|< z!VS3T*NGz$LT&L+An6VAmze%43{-lw!+%S9fMkfse<#3zjA0nF1S{n=Rnp(<^p9i! O(F6NGWBOOr82>k=3Txy5 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/index.doctree b/doc/_build/doctrees/services/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..66ac9ffea29453961a7dca63b0d2fcc0f2dada85 GIT binary patch literal 2457 zcmZ`*iFO=E6_sUcnX$FliEM*yvJp@KX|e$!gpiG}2!<^$n5AiIx@xA%?(XVWRV|GO zLlUxS!oDP|3Gk=f>S-<3Ih><8XIfLsefQmaU%xh=Nt@Ndp#KLWTWGc|JGc1-j_#fe$8NpDk0VhMpHEyhN*&a9*W_@?MOvcsbz?t!A}z zI573LcA4UCpSn_I)`_^A&-dw)sDm9^8%i5g>Vt5BFYKJ5rFgB8o>wNkO3Rrxxx`7l zmhk#%*qJRVTbHuS7Xkaygzo}uC-&M)@1{9g1Ulg04Lwhb?f);|e0pwfu18)u-@7FW zqk5IIw^SBOvX0^0v(Pd3#FdM!6kuZ>sN0^u39`p6DBiz8}@27tWyihZBCJfAi|eqoN>uCUP}n znIC6%6aJij6m%70EL9aWe|~4a)(`BQ0TuN?;4jun=Ytca zA6i#{-Y5FBawNKj<1bA3Nm|C&8Npwi@Kdzz_ltqe)5DdXzm)LPbnys>zB%D<(K-}s9HgDXVIvhh_Y?j$EqfJ$DMy~avw7^Qp|fRB z@YBm8uMrFUUCZB_@b@?A(jgUT6eob+_WTlH+=Zj9lZXTO1q#VxZ>dig+%UsZL8?JL;!&U06+9Q)yANe+r2^|I-LQ42>hS;0G{`fMk_Em6Wau!A zn~tN-`8GPUChMX~kCf#(2W1EhQx0vrtiZC>J>kj-7-uO_aM`DgNPqNL>|GjE0lY!6 zrDgoE_29#gORngm8XKfL#9jNAA_hf^nJtHA#I9`_bhm4~3D&X4$w*XjJ5_}+d5Vm3 zm>KAAIG`J-q&V{1ar0AOMXu^8omJ&7@AlieDz9pZI*Oe4B7Nh^LTd)M-1Tf+y5%w?|q361z=#vPJwV zb()o19wfX*YpFC|3_xYe%j=%p8N z^;@)bI85KxS7;4h4r&~tB|uT&cMhmCQl-L#1=$CH)(GI3k|{Lr3yj&6Ym8ja?;=rS ztB-8@Yr3bk>E7PncC{b0EgPFkwcEo=-{arJY%GHqr`n8kj!)pQvBfw$|#R<|j>Q7U;HglWGRbzQ@6m)txJpT+ax12Uo_57EF zE;phzC5BV;j-tv)z2x@}=seyEV5Hge`!PQoc8*f;ul;&J8>e%8_Ez#Y%)w?1|9!&$ zfUHiw2F-@XKPUVzeHG!35&FSqoOaFFJAC25plzmKO`XFS_55MN|E9&L0}S~8O!&X` GVEcc)l9j~( literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/README.md.doctree b/doc/_build/doctrees/services/load-balancer/README.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..34e690cf0003e8e59d834d1003c3154325a7b38c GIT binary patch literal 14555 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 F{{^#(zdHZ` literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/access.doctree b/doc/_build/doctrees/services/load-balancer/access.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1386f1132b0f81874cbdb9a45b5c4f28cf689dce GIT binary patch literal 12999 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 Tfif!R>5upb=ui0d3z`204UUDh literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/caching.doctree b/doc/_build/doctrees/services/load-balancer/caching.doctree new file mode 100644 index 0000000000000000000000000000000000000000..84d22fd2bed5d7e45ccc75122b25e44e7652fdd3 GIT binary patch literal 5602 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/index.doctree b/doc/_build/doctrees/services/load-balancer/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..a65adf24762aa9f194235f3482274a254d7b7fe0 GIT binary patch literal 23842 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

      qAG%Nb3C#W)Mp)jW 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 Qz)mjD0& literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/load-balancer.doctree b/doc/_build/doctrees/services/load-balancer/load-balancer.doctree new file mode 100644 index 0000000000000000000000000000000000000000..581c6dbf863516796499abe51029495680978c31 GIT binary patch literal 29537 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/logging.doctree b/doc/_build/doctrees/services/load-balancer/logging.doctree new file mode 100644 index 0000000000000000000000000000000000000000..22fc175e6288ea13fdb1f6c71668ccd1d2f228b0 GIT binary patch literal 5495 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 APyhe` literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/monitors.doctree b/doc/_build/doctrees/services/load-balancer/monitors.doctree new file mode 100644 index 0000000000000000000000000000000000000000..d62a8dbb7495011b5d357b9cb9f6b7da6dc950c5 GIT binary patch literal 6366 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 DHLjbk literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/sessions.doctree b/doc/_build/doctrees/services/load-balancer/sessions.doctree new file mode 100644 index 0000000000000000000000000000000000000000..9171eef3e12f80d488e67974368f789a9d8ed284 GIT binary patch literal 11150 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 J;WI1@{1;h?CmZN?@4}-l;5-P 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 J@H=hS{|7TC$TXIm=bb_xEOYcUHUoBu%i> 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 Y3-Yb_wR#D5u99!VOCsNn-?&ozKl$w#o&W#< literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/load-balancer/virtual-ips.doctree b/doc/_build/doctrees/services/load-balancer/virtual-ips.doctree new file mode 100644 index 0000000000000000000000000000000000000000..b368745130cb76d1708ae8b15f2263adcbd937b1 GIT binary patch literal 9942 zcmdT~378y5b(SUVHQHURWXZCSWVdD68p+xn3$KM(*|KF>wik(&kkw*sw9NL*)NJ*t zXL{;&_a2x63v4WLncJKO44C^ckON2{2T4fcBqZb}H#te-gxusJ|Eunv?U`K>Cm;j* zKFxMly?V!A@6~%%)%PwhSNw7mx?V6;_bVdE@oP=g!<@Qk#~HO;s}*^>r|#55INuO~ zTB%h>etclrvSpqdgw~8pVdQw0+Xz(W*mwup0})0I)ipN03NP9I?y>PTXd+SbXGCVC z>KQW(ok}IsIy0plHWIK^|bVTx!sxY+cbU{pcL#tP*9k5YOW|W$e#wIpwN(w z!p!c$pmMr^X(@nY#x0AW;Q3Bvu;h47y)3AZU_6L97@{Clo9(X9nc^u^TkNhNR!LoE z=bSJkw-kji|8lLYyxm=i8cY|cD<~QOV5OUag*)&Kfjm=SZSKQQa*gc5-TD ze5VP3C8xzYQDx5GPVa@xIRg18S zR9*P3MFQ?AurxRpc!zE|@*K*suUo0F@3K7iw6FqyM2#(2X!kUAQAZiYV@nykGWCk* zz}Tls%j1CZPOVNbl&tZQC86|O#>gv{JimN8r)cV7WVGcQ;6|K%q>CA<*K8i({=TMfLeW_5Y!)DgvQNtsEF@R?3qGA(2F1Nvm?& zlf-`B_Od^DI{psFTjrevpyneJ3k8OMyP)ieXlsq6@+vg~qm8W@)1^h;S#IfU8EwQi=3PDC&lnQunT29ve!)#^;z*Ah1`X>kqpQhNYpS0-#`_NeRmi?}3ink-+D#LXf;uY`H8((2U^B$?*} z3vu(HRBE5l_UPUTzjDy9X&R)Ck`RxpZ2JU0xlyt$f~~Q`Lq0n;#T^V5%t=P z`X-QGx0L!A>R$*o-T>?0sMVX;dRFPdg~)nHt2eXt@i1xguIHzi?BPR77ZPNL%Bx8tKH51cqOIXPsVfFu;( zKE|{wFoRm3w+YZ;LF_IoVLLfFoU+>kM(sZNuBCkPniTza=K;TXs?1l9U@qRH)q5HA zr%Wmkc{nPy*P}_PlT+_Q26|Mh_p?qpAh9qgAuF??XhyM+$|xlYi>GparW(>|=# zM^ZO?6mEvy2Wlr%tf`NpL>V4mi2*$I>SG}PxK^KFscB=ZsAPp6)9RDl#hc=co3KWo zI(ql#?8Edi+gx`VJsUHRN&uH~o*77^#m9hJCTKyPv z^{r;-Vne@9m4eVcw)9+Wb`gNAOMEV_8zX3%a&!*qsY!R2F-zLl7g27Aj z`E3~ZJ6ioN1WD%k6(9g_=d$&yTKyi&=aZZ}-I|Ln8t?L)63%o+)bD2$FbVVzmQvul z)TTd#0!uUcBWUo)TKx%Yz)ZgyYrw_j*R=Xm*5HEWm|>FwQyfhS{Ie&O0|94*R}fVw5P@U{-$L%)Zf}`;={O1{imKV?)P`H$GG2( zsY!nyyWcmo`UeJ|XYF`=khf3(s(-{xeLd@c|I{Kd@xLa|e@>=9bxTDl0$nwH0%8Y z!J9hJb^jUa#2-yvhO=>;X;#Z9*`7`7ekgL(jqV+1sK<=U#pA{wf$|61_M+w(M>>3X z6Iz~du#ffYW*@s~8|#Kc)@aon{)9A-#Ut08zLx#%_Q1k{B=+T_$97>pCnpbjE)HD} z5&48&Uc;{k;?P`KG`Q+V!DOnolE{UU{MkB}O^GYoE7=%Y1@#%&USr0hQ^T!*3bk>*gY(R!oNC5c8B(D~o|kD=aR=-Z)=88h0Kq8|~u8SK8DGt)-U0hXuX zkETtAX&kPs7Qz*Wx^d0N*}L8GX>&Y!iy3`cJlf>>cFLy7NF2!TN&R9D+07vvU2X>&#+_5f~V2GbiEl}R0!qycrM;evB&+J+}hSDE*?gt9fhP1hN1)=kHnPBSj4gT&Kf_qSe^(J)9j0IoM9 zahcTN;UM2Tyn-)faD|jycH#O4S2f!w;)|lgo9vam6}LMM9Y22bIPGCMxW2hOHToHb z8&Az%JZXBSdFM!gs1fth4%vFSmIG}cIswxRb1kGG1RQN z-OcTJKG=czRuTFtyR2)iAZj#_b1Fl01NZ@+8}Uceej_;!NLDNk$fH;}P@d9FhUVs& zX8bm2Ff?Q>h#ncq;XIRb|F=04#ZmrDeEBun7&T}5xg6Vkz;Ipas4}tp77)Ph@19gT z2#&D)R{YU)$jFZ^XFFrd$Ia5=*!|^*_*%3WpTN>>pcu1r6`X;P!%3QMH!W+ODDV)?C^GP z3Ljt2&|PRAv)49lNpGbCy4wtU9)4N-hEJ$%=#(Mvb}I><6L<%neOYA#dcGN28IQUM3W4La2f z@BRQcj*Vj3ukjZT0o6@wU+A;MR7IZ|cA%f;slm^E>5Ul%;*wKw+j3DS;)k9JE^Oe_ zwFU)vXZ_6_70rT3A=+>~-V00ldxkhHB14#yju$3mGfdWD_a*wp7xOgBbaMBYy%{s^ zQxT^46--7kew8?$^zb+bGJAjx2ry7Wz6UU9o|`)=q7+Th3z(BVAZjJS9y9NWKnh&H z-h=jY_7*=18&P;Z{x4)kovd0pB+V~<{E-2UoQrcDcJv~&VNzFDqFSTa^x%MAjCKSg zJ`Hf|RX-uVHzw|%b*Rp^fUj_BbjHxG=V`0Do|*h2KF)wCaQBy>6Zev$F?iLr!xefd zzpg<##?ph}ih=~i`lEHCS+?qPZfavmiQWuMqVaI%k;0hBpEcRbXY!qnDfJ0WsI`;m77i z9QPCfy~4DwW%TgbiutS|LFJW(=c;%mEpn{Ts|;~JheO7ZUu{}fx6K$mh-SMZ3af*A z={5YEOIUFby*B3L*0YmLuVb>Z*@|7hzUFF6SqO7H}i8>nke3aH@Vqfjm0L!2O1oYxO)!i zt!O`M_e=?_hYrux+t4s(_Zz=jK=JLSwKu3CkrY*gq!Q3O(00!5W%p^W9S@_;zC2kw zW@l%I8uPgJshhQM4_|B&9r%_}{>K1J{5$IY$iwT0j# zAelCy(a$SzaRILP@ZjZch2F~#{fLF^w7(CH=j=^!*b7gXWeKo`vY33GLyv+4L6ppg z366l?kH!i65)&yd_Qm*m0$)FgI?r5%K7ih{_9}eC)NJ}7nm9igD4T2ghnRO%ve&m~ zu{cS8mh_{}3V??r0f7^mb7$j*<;`*02T1UBGRS&HnnE#G9t>Gor0 y{KxT&&CE_NZa!gnp`cr`WQ^>dG<^j07)U4RllZke*$YuHd~2EBA;&@pS6EmAq+vOh&FoC?_H<@vreDwG0GdPY zOA$om5Jg15gIf_)6af`H@C21x9S*`3t21ciq0M9BHmpcW2Y*+Va0Obn(MSomZ_Y~v@oU)ZE779+LoGTXm z@m<|xP#ZYqO39zl)jbmhMWF^{m8e*9sGI}mL|6AlRS>rrlr1JsgK42Qv8#I`TJt&7 z3zfj1)YUyrSJg-c{^YLiv6@iePwDEOp`juL(gaZhe`;6vWDRGPqsnPr-P2>>tZ(2? zSKL_1pg8DQFc{@Bfj>j3nJbj3=&!GsWylfJ8TQ(st9w$7nIgGiSH+$lRQiH)u8b}! z7J9Qzf8cKv`WxF*N@)t5-a#h_Aa&qx(&KO167S1kbx?K&HBiFe3_UtC^fyP3QhH@j ztWd`BllBOEEU2OH{8R%4)maID3!qw|zh#Td@CkDI9bjaftiM&~dTz?)_N_yI8)-** zq&+sGzisy+P^{e!917aK^kB|uN3VJy9JH6*692cNlM9)Av65|v`?UjdMK0q6?SsWa zu3V&CVW52>+bU=q%(hWb_P4XgmD2<4mHsSyTo8Fwi=9Z9VfDUB85Wrx`rEhI@!Rc5;sdSX#DPCIZI4md^LqRp z-3`+fdvrcmaQyk5hkVnsv@jhGX ztitH+Xr#Z_nWy=ChyFq^cbV6&mA-VP&$XoU_6hw(s`FHbMuNC4EWy4kfxn+IVE0c~ z1@aGI`|ao-n2tm{MvJz%#bt#L3jI!2n1}ljGTaXa_kEP6uBdp(P%2jGbh#Y{@}W>; zN$4NOYA~ULM2)x=^0~4@>3nZrzL;5_@DGO^M}+=TmcxY{+ei;D&RTm~vle7_a(3Lc ze{SpCzWM#F^X578T6f)fZnibE(@vQMyUg7wz2mMs?>2A8`3q+4p4dkGV<`_%pl%bn zYHJ6^z4WEDv}o$HcdsdGPp%Wmx7n~qVkoW}%8q#*BZg6PbINHurIH8{`V?tO>sd${!#4z3W_gXt*ruSsz2GSau!q z`_hIZr`#jhi8JWob=fU`7Ea`Zem_iNk3c6K4=2K~8>>v_4~S1W7#$qg+|bWyz`Aka z)w!*|+@7IAd#e#e{5+2ff6(0^&EvLD_f|m-j$-JSxL^1pw5ajA;6pao94S=CGID_` z(ExUALa7XmE1|yv%v`2gRHL5<#>&uN#WL7DFbdAff9Bq^F%C3XYll*~B~6rG-7+hm z?sM|9tUWudS^F1@eQBCCTSkFaLfn3Ji+hw?@8Ku3xHaECQTw*5t#y*iP8^2*$^TwK z;jyP^k3Cg;>}mBLyOKTj!?5(f&0hZL=&UnB|4b+=?iq@Ey7g{(R_LFt0qWfHoH`%z z&$XxXeUa&ufq$NO<@xS(@k&qI`2yvY7l!^v*wB2hb8z*ZN?)ix3imn7bjpkB7)DN6 zrTHAe}W|#WS^`B-Jp5Y4O-xr3EUDo>5OZ&)FGt< zOU2~b)`Rs`08bizWPbiS1;GT8fY6{A-;NL=wJ20C;%V+ zl=k7PwGV%~-iI$`AN~wFW}VyDzXqND+0efh#u4AX()8{4B@8#F=>bZY+=Tx*1c2*8 z|MP6r0eHodyps+bo>ByrJmwv)V=a@5K5iK;+?!c@;3sIW;Cebx$z`4P#e7GAM!t>g zSk~KX!B-N4@E3C?VWs;@ z=zocoX#xYr*H!xB2C3e8C;TtNAUB2n%}k>WeAZ5TR^S%gR`C6;+$(C8*>?6=@TpMF zzg6aC!x+-MO_6~QsoA$~gYrD1F{%2uL+d+2|0}xdZ<4OZY(!5d{IBAY+tIxp5ZA1C z{5wJNYoUJ^8)41Slx=Z+*O&OaL;veqs=8SEjk-SeziB7c#J3d_-&Qs8H3O@EkHpn) zxnm`+uF}r%Z53DV4gK%1q&$JP8z#_l2WiRG*S`-U-fhO%`|AXWVr-TE1F+gZC;D9& z>wBU9eTd*PgL`Z7_QBA9h$TA~lC`&6O1Ojynynp5=ja+Q0o1(EcR4F9b=^Cfw9 z>4WU=zd#54n{DU+65aPm=sya@C2~G&M$X8YW%{UQZNFl{{a?$}Nv%Kp#+bD|#o*#D<_9k7Y3rm9)>`B!e}@` zts=Bphv4dvE9Im0sldvl3s&GV?X(qOZ2>L9ug!naP*4K=OG7Dmq&C#=q2LED^B-W> zmqY&*wkym3yx5h)np#MhQojh~zshm{HP*`>lXq4)c`V<^Ow)heeLSsKZLkJ`^@e#p zYPq9xsXdyP|LoC=yY@fS|0B<1s@?o2m;3OI(El?;fLnt6UmDGpa%b`=`72D;f^Oco zQp&5Y%nfobhwHbjuvvUL+RfTZ{B^4~|8G$5@1g$>MLXrbplESb-U5z+jF_k9tA%mtO<4%;T+?mmcplL!2Yd$m`xsYZ^IVlUbYGfbC z3%84F3%9gBlSz3jq!C(Z1Hm+;iY|CnRvR)x$8eN40t8IDF_Mrr5qe24V#nes7h>CO zDj=IF5V)||&J;-uHB;Jm=%LM#sjhBQ5~W&Lr4)+xXtCH9(xR5@&e0~aP$>bHwnQ$Z zt)!e3dSaKYQ2~RnOr}A$5x7Q!R0(g(2pu&#+o25gXCVoxMX-}zM4VYG^)Ys7wt#G} zK)RphC|rmZ^Ep-yqJaT(EG;&|Nctc6P!Tjcl^2P`<) zM?#g|Tu^uSj$3-ZWS}mQO$TmYF-_UPLZz%dS^T6U$Bz@*Xb0$9Z)XnqG)K(PXy@~E z2evX^$Nw8MwLx9DQ9F{5=8AEWUSvCV9C0IDZafY^yjn){1awCQ%J(S^BWb?088)T` zsIjME5w(>cp0q{}PiQApOR(7)8Fz*Ro6B@Z>>^Y+*zAg2NFS1NQXcvo5PJ+myD@>3 z$FiBDYInggB#FXImB}8A&@nX4J<$f!bRY?7FQJt5B1SBd8ev#qqrC-Wp#tf?ecU^Z*5bSyW$!r9AM9Mb0Jg(2h*k*9U_i$m4w)(#;x$69jW zlHUCeJs68b!s)=`N(CVF%X|Yl$%lcF#;VeWkELfyhq;r@#;TBY$ z0B}geQ#lJya28m#PBR6s)rvJ({#sSN8o7R~g2fwKJ7CmazMJdiTP2FZFPB<_C;CG>u%G-c|TP0)Qm{~ZMX9R%h7 zgewLe41dNg(;-Mg>cVd{bN4vt<9^AVReQ!&UBi&oEIL%6G5uPCj4Uk4+?ftT(U^Z7 zjv}vY&8H)zK~29d(pQ(%AiPp1fTMc+BEi0;~ zta26Z6-x1|@G_~bU4_#q>#Ojk+CF`#l&f$CnUJ!AlU!?=U@%8eRj}(vUI)7YG^h>FOBR1)fHL5zGU#GNPQjb$8YPMogc%9vg;3V;K(%Sb}1;8&~7{FU0ZRWe#3 zU@L22GF4k8wX3VOwMC;7q~*kFOKj2TBxx`dq7XIqWVL7%ZI+{xQ76;4Q;>0|$@J|G z?P;e937)>4hFnM=mU2?|+^Mx9P3CmQPI)YnId?ll5DXcjxm%UPnT*ge1i`b=0Mng~ zB&2hMOwxz%{B_C44a>7z;m_pbYIkjwGZ@1v}|Q#F@2dC!YEV0l7?p zbh88R$B)jX6RnKvWR_bzQHT~>FqKM|)BIQt3+LqIu@IlN;Eh(XUoYNf9oe{-z%+(u zH<+@mz{=4|EFa0TPDELjY=Uw+pLekIvp?uLtI}8ocIG6OEfeN#(adt$vPRxf6PAvI zSDUqz=TVt*C7qWQD2S7Y){-L-3k-vjlgahxGSPzqerjA>oDvN{MV7df&~4T}t1(^7 zRQL%2Ps?fq)K1Vj*6L`@%IOE)9G*?&S7Y^>2(v;~Uaet>%@SpvlgfNgwtT9^tDvpE zw`KY1lhPTDy?vK5ZcQ7nJ_8RBA+j zdFb#(X)_#;zJwZkhPk0j1tj`18sutw6Ef~px!T^V!^X`*hp)D`AQ#fDQdZjz%?(}= zt=pJH%47M=Ytik3Wk?fUa;t3aU<9=VaR{nkK`VOwt4Kn+Q)nfAOXW z_}%o^rM7lA{SB1$-Sk7+>fc1A+)dwuOi14n9JPCCUFn11^=)A*y+-#UukXaagM9s+ zIGdfr*?pXLtV_Go{a^{-cmPRA-^H)Kon_NWpd4yuC^z!o6X?bp`R}9NlY6lyH}ZFy zH}ci{sRw~3w?_{l6VeZadUPZILn*`uK+5CWYc|}EIPK`3g#f+=kGT7bc=G#Wpg{45 zk%aUU{A$ISy;5A=$2<0vOojNIMz1LP`Z-!U*R#j*qAyN6&@WI9>6iFzamTv%xZ}Fq@%9vc0#iFHLyw@@ozUeb?3r>| zGum3QjvpP(p+^~PqBn(;N%q8AYzpXCTsPUCCUrH+0sWdArr2YJZa}}`;#7}gm^;lo znUm@6dF~8vC(f?le~Vm5&q_HdXNDHaDHQr0qoq7%ZbrxF1k&IZMaL@X=NX~H zr1S!6(D^SS3F#%lPkIq~X0IY5Un+htAb(IG)o0X(2y(f2S=uo{c?B8Q*zQBGqQV{{ z+bZZaIeY^j zEu-*!0|XM{bQUHc#g&2F$Jd@H7YnU|9zBAz`mo0|%Y+q<%!&ejn4Q2QI+t(i2YF7h zh)*w>f~iT5o+G3S>HO*wWD+zuM;=fM;>ujvO(55Zo-6cIIw(^G6B|sPTJ*E_bUsak z!EE(7Jxw@tv_esBqqkt4`tFe##oJ=M#_k!XJN_NU>-ev6(W}jk`!0+GmwgWjwgTY+ zE~2y-S)84;=CbRdF{F_Sr27?I33!IvWSg<mPY+A_VD_Qo}wADvBr{5}K z{=6cG?Uk6+=CJbtf!@K)H^>bXu*onCsNA0jZ!%>s&Wks2w zVtR~C6xH*cvMa;d!!4IfyS4MNJ8X%-i8y%-*_J5|wr2+0)1_Sdig|5wk;hio6>1kX zf_NF%Abmx(r%8Z9H%~?q(iB!j?Wy(Hvg)o&ZAHh}9?=HKsRB7o zL3T@RwM#Ia(=)p10yaZ|HN8x)FYUNYZ-9(zoTa1#?%2pz>vU z6XZhLRLbT9jLlF3(O44mGCflO8%JT4^5%?SKEOz#0-C3ggtUdAC%uR|GgmQ>r)!pg zY^gxHJ7F?)`bM3voI5uP645drhnMP6N=x=~@S+=z{MH_BM8|g)XL-xGsf$d>wh~V9 zDcROiTbq(?gR-8IZLICLEh=S7wjDAd%@UmC`z|vENQ;mbgVSu}^|WhyWQ7%I-))+5^A3J6TpUbnOdHvRog(`B=-|pgk4G4&k_$;@JIu zQLozDh@yK7r-h1>tc%4bI{QeQ;SRJ2H6BklWoEZ8O70|?*=?>pWIw^-(78Wy*kUB* zq`Wwywpta?fsC8-SO_zKE*5ZuWi*4T(m#k1IvU7zq8j~mFp`iC5h6)1;=zK{!f2O( z9I8ON9dw@xqeCBh9P+#)?gg2tLt$YM3CBQC4CL~*MlIu_#48bFXsS3SEA+JMkH}dtNmrc%t?PyLr z)}54e3>d;Ijzxmq0r=IUn9bG@lzRm1cm=j@Pd}RDKq7IkNYOYBw$mQ4OaPjmeWWzn zp?V*ZkTL>1%ppjRb&sNPR#2P>*2z9Zx=L zJYLC@m`fJ{1LOM|>CB29di&TrgCkaTXK;)FtHimHg+@TgkqX3G74IG8*WtISX zvXF@f&{L$g7C=u$SqIQ|ZL-r)DFO7u$b@vd;3U^hAjH5k1W(1lGm+OZ@GRu(V<0Qe z4tzGJY8T3yYEI_>2j)E&Nl546S35Tg-w?*m7qANy*g6hlhF3`#yHKQP^s2eKA3h=g z&BNG7(GJxwLK4!)1bDbG#=1vg>|#N=BtrSu!q~?d`~3`KEFXKprJOdN-KI~V9_n6( zB&1K`SNAk)^Dl<6%N5Nlgyxls=GuiZWmO}LT_v1{3Lb_4tB)y0Yi&G0>Ul5QlDiE2wwQs+@wfBlNS;rlUcWlXo>p1zXt)U-x zxTBsY9k-AlZ`4AYwS9ZS2n5X{AWo}b@5=%|SlqV|hcN1OmtgoM5hxxEzbv)2V0aVC zIvDPz4SzE#B^cg<3?5qxPV$3N4Po^*kwAsj+mY8{^$z6g!zy_w5bV;cepVuJ%m)JT^fR=A{?Czw^a}=!&T`W)r4XY(9*8S**|OWY0rOz7zyt5k8W&NdM^J@Bff3zD zQ3~l-LMoE`*HUOocWND7ub$j*gxq5$xyOemH-0#BPYAguO>$2SPj2Q=Q=g&m#%x1)&mI^+hQ(l4S1e1ut=X zoF;-ktxnMI(FUr2KoZi+45)qJ6)7}QWdWG#tDN%V+SiZ?>2(2DJ6pT3-GKhc?J18b zN8?!T!2Tp~akBF}_hOoFFtFOyDm4C#G6w5kkc9MC!AyD)VP-3Xm0#gDM%1CdNu3ud z^>;!0heE?R#c+I63UTf!Pb>8n+C{0i1@|3=E2}&EIzfhVB|g_o?=o1*V+N*f?+JLE zdZgQkQKB2au+${A9;%_@NF*VR!Y?|&i|DfuT1Bs3x6uMWM!_rH#!4a1y{6kZw2N-z z1$TnNm9yi#6N(btp7NNosnkS)i<6C%n#91W!-U3Ul%dTOBq2=|%%m3)X0}bVnI`bl z6}-}Bh7{s#Yuc=jcF|@7!QD{d$~Freh;)lt+K3@i9@94U*;pXsgd=@6VPLgcBcie? zYM{|(NJ5$^xJfS}&8%Y@IdH~N7_YRsYGdD&m_SLXh!4YZUoneem_j3l(iTWUvhZ6S zhFeM@E^aMeY{l(f6dbk|{B2D9ZJXdTD;{#&al0Ndvrxb@3_*#4%WNq$@@H0zzdffM z26jLOC+P(|*$vMfk;_smx28O%YxY5#K*fp9?_9_f+Znh+Q<#eq>^~1lNIME%(u>G4 z%b5MOCi4Y;fr3{awUZR$OjDlLWM{O;yQi;${p#gj-pG`oT>t<@cSVAO=Rz$q(r#R^ z$0}&r9VK~K*gC{QKMTfM?twJUjoI{K8EG&ZvibHN+dBFvO6 zau8B4(z_G+kPc>KeLXrv3XOs=Q_`tYLZ>jgsj%@FecCbeYoHrp>XAj zuxuH2&Mn4r`m$xTl)+LSGcX<97Vx;xk)t1pMlafueiZUJZzkYLPpIK?b@Y(vDUW4g zlO4lpNB1}kwn!8@7F8H8#~}%+2fxaIDUUgbD|l+lIo<*czla)Plu8;x9b7eC-@l?KidSKS+O<_r)=teWI`GcaCJw)yHLpG_LRqzO&jL~ zE-u#m&P7bqV_EzcK(xY&_fUVuhD^e;p{q>l)A(i3VJY#$vG zJ>{`9Y_y9w?dYDMhQ1PpK88wo_r*v;x&*&^@H3CK82ld>0+&_^K%P%XA+Ans@L$I5 zdhmZz@Gm#523&W2TX37eE0jVA-Uqn8nFEO$nDPNXCqae%_=xY=SZW6LLn`CckLRRZ{E84Yw zw+Z^~Ci)#*7+ybS%KCi;sn+kS$m75WBWwM>CWS`*nCY78cbAa8+a&w-CSicCm96Y!*rWv7rW6?s$p&lx7=F?G|}zYy5C zX!AP{V7k9#U=26%7lt1y%#`(b8mS&C&mbStZy8w+m1m{UCOy(VN|H_86730bY* zpO`#*49*NpgHI6f zxODS77c=Pu19x;!#XUkqN>ieVXoA5eAqi=+5K(&=hTzG9H3<__1b?c+S7BnB6yg%4 zJgx6^wCiCsL(tbZ(Kq12@WY6ivU(dL)x&5b8Ob z*MzLrZx)m1Fwr8ovlVXl#&9wT6HC;~Gmlg*E5TxWMoD?h%=FzI1U0TePStJ)1dF&>G!M-f0XrfIX+C~+$Y5@35i%ACft{)Z zAlc4Rh$~hL8M|=14jH=&{)bHb-J0MtE7ojxPPq&AKqjO;1w1L^ysuKo<{dL)Fn!bN z9ReK}ZhmJcQ{Ib#)iZlVsTwf$Mhncf5DDIjCp4OIWT~3Oi$#LIuR>SxVm~RwxadP3`!EGO z=?OIq%IuKnDUZcs>p7gNll}-7F{!8@t#JPVBq6!@)zN~PtwprR2?4K40D>)-LR_y} zw8(S2juwN0Uoi2DP4Jl&D^}u^JHSUKBogrYb%7Gh&6G`Z2LcxtYkub;OtZ|ub*m30 zs-O-gT7d+sc!J%?g=J_G8&(P8YK5p`!wFJ|%aHQ4YA2#ykDZeQJv7lz=ECq}hnccM zry$j1=TziFI*pO_*!i#&8UfJ6q7tG11TE!tnYr zQ`YZ1q*}l8kq_wtM%MaWD1}D-nCY78_YoocQIqUNO~`8fKE~wPH7^$2OB8N=^`Rs; z5g%vRl*e35H@#GF;*v*h`Uy1ZsBjtb*yktUNl&O@@LfJ6ddg#g*lt&F8e4sUJai@M z;p|r-!Ga!sbv$4;YY-3kS?U;hKuT8&23L$N%kCKZw4x!0@S>f7TgyAA<&|L$cbYx1 zOzA>^cY|O%0(}MuUH0Va8xr_4kaP_yu?&b`dps)mJqZC_E0v$)%4xys!MkZtxK@7E50B#h3FEGFad;md?N(S^rsr?dud0&t{Nx$-e zzvM?>Ms=4xqpFMkv}Zs!3EIv0W$8;r!W)_C76Bih%SP{jxE1BjF?dUxA#6am3F_^P zI$6u1B)kK4A$^5grsWFR9N|Noz0q4$=&OQuCpV6d&UFR!H585gCH*9ag|mqx(|NqQ zJwbP&y-0WCH>9sKtUW{9vzI@}2`!fg^bNG&T@~wtUy-u->M6D^X7PnkytkdciMB3# z67^?*fG?m{C{OpG&Yl^aFD)0bsi`gJl>6I?G|sDbs-N71~p9>^@iO#UVxh4sJl-k=n`SB6HLt-iJzgV1~~+rV9fVEGh#d>(KpZ zutz!tdVn(%_#3%+0mop8zAI!Vsm|$@PjAroP}hl0hKl&eYe8IqzAu0YH=W1RX8uA4 zJ;-3_w@5Jc5mS1I;oRN3?9I{DMe2otihOvyR~22nqX4_l=m!9^r?KP#5MA)k!wU36 zt{#(h`YHqTBc^0ea|Zhyc9qpQQtmobe2_4D}d=IBbEJ9TX$rpDW-gE7Me9T|n1K57I9L zc0=wjek(QidM_T1LlyY>FVToEXF8?UZRak?(j#1)?ySn;Jt-Jfl_0X>qfB~?Jr;X) z@#;7Fm5`LqhF7o11^lU5`nA+=2}v zP}7N5%hWq5{hk5G*h}|^NPpn`IQ>?emr-)Jv!~*=vWz#A^FYMcQOooS>d&z!V2`r; zj2pd*iY|MK_*Kn{uSxBsU=VM;>Gdmk15QA%qwZXL61z_|X#5d%_7+jlSh;d#TWK}+ z3Kb-1;AIEx01n#ePYi|7Unr+n^}4wMH;?}^y}{L^^Tomd{h70y*IO?(1pfjMPlg8G zHB&C)*^@TcUm1BMo<;qQGgB}Y%x?cXD$liNs$q{0YDqkTS<7y3^d)2Z2S6~0qW+M< z5zw2c?6EhJk&-Lm@Thz|+9`0)W$7)no?}nM7oahuRn^<5!VsM-rmP0@cbImi&)zV8 z`BHBbdY6&pjh6wvhdkbkfzLg)cw1mAuzbEmUZN^5rSDhh#K)UE@Qc?FFW^0bqgWyS1h4EvvSH7LcuUHz30^T8k-%4NG{{Z}{kd*)c literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Alarms.md.doctree b/doc/_build/doctrees/services/monitoring/Alarms.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..a68520008a470fb74517d1552a7398b86d191ec1 GIT binary patch literal 17820 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Changelogs.md.doctree b/doc/_build/doctrees/services/monitoring/Changelogs.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..79439d5dd23157444bfb71388adce94e6c201b5e GIT binary patch literal 3997 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 TgvWVV`YZ||eGb3P$=v?{`Ub>@ literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Metrics.md.doctree b/doc/_build/doctrees/services/monitoring/Metrics.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..13f77e70dec8745ae5634f99d09f5770e485b153 GIT binary patch literal 25023 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 C0G6=; literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Notifications.md.doctree b/doc/_build/doctrees/services/monitoring/Notifications.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..5591d3aea2b3543ee09eb3bef2a45b5e6031a2c6 GIT binary patch literal 46791 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Service.md.doctree b/doc/_build/doctrees/services/monitoring/Service.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..a1a26ef5f6a597d9839cfcf120fda36818d1f39f GIT binary patch literal 6402 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 AfdBvi literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Views.md.doctree b/doc/_build/doctrees/services/monitoring/Views.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..f32696c1d1b1570d38f288ba86f06e808ba1aea6 GIT binary patch literal 4937 zcmeHLXP6vE5j~xBX?1rxon#>t2V1gNpt}{wU|Y5X1}qQ~YXNF;fS0-6o!;${Ce?J$ z1f}K&N=5Fyq=l8usVbM`7fWsH@n+Y^2Ta|$v zm3?g3KpX?au^iPp+0T0N$Mp&8Z%Eg3;FQj*(T2{DDA6#UOLPlFWMfLVL_}gfUBKJK zx>zrG05NTr-K@9xe#^uW#Gt~Ih*jkT_gztm;_w7)sY)ax^KUGSM9}cU#Hk?c6|9_c z8$wn5FmQDkyFsgR+yr#ocgAC-X)Ei~yk$tzHr7|bqV252wT|6-q7gzXrCXI*zmr5} zoucz=tgqp5rSN<~jrG@g<78|Su%pISnpo|u(S>q7XFd7)i)vJs{XAi-JU0-u>u@)W z6`G)1%WjDX-KN|bBIMQ+CEASujioeRmYXsKWaz0PH_MH3i`*)=v7T%t?UBHF+p;{r zCNHST9W}YLCNHWb67FQ-40Hl6o1Gl{*EwsNlr#)Ebg?# zV8LSE%$0KQ!n=0{-n*o9SA*Bs*`ML<dNA2HNrr1 zH?TOK{JI-tbzy}~%i)?^T5@eADuLE#Ef?}iP}Y{8c-oCTvAg8B&89GFwZeL)#&PSI z6jl_wK96UuD2by`31g0DrkMy;7*1vgrMhv7+a;xmSt>dDT0EIS%r^QEEGUnv)Bcc@S3~L6TAmj zB;sZm`@pBL7P@Q0vUm$|!B%8mH^psq<)r+fkVu|UZYUd5!4=G>9%Kbs(0V!Q$Xc&tu-)~Hn)>3+) z@m+@RYbMPf?C;hNBSK~Ew{{eJ``Ce&&>5uiuFJ_@genu_^WGDKUQ zMCEcsN83bQ5WRU0Pk{W%lsuCu;~Y}M$j$^=w~eS5!My>PgN2`qm{Uh zs2nmWz>lWMIWTG47F1TsP7%|)#eROc@p_nOt8GY)canPOa-=r3Njr;A!VQ?dB;O*4e>OmJ}&d~mVMQj8}cbI~c<-eK-)Xi_zeXRW=wm8(4~YEQvjO6Wc7cj`me5It(!# zxm)eO!#!-M08vap@2|=|RklUMaTwd69y$o9oqv?*Lp|i}18l@VRCq(>x9gVAvAer%zPbc_!{n*UOS-YvBX0 z7W7FRIU*X{x#wbsKE>9G85iR;RGp|IHlIdpda+A4-4=bO$Q1-PY@L zauEmIQ}iX2lZgo~39{+1VEpAXY;`_M+eW1(Vf(ao_->Fcai zyl(XT4ZQ6Ji&I43WCPjCF@+#}i>1 zLP&lLu`A4D+NK0|9EyH&h7Foj&)4Tq+4iD7r>CdKqggG(AgfPDOjWQ_Rp@7CQ^cq^ zV@tOsJ^a)3bIkQ&(4t?kElZ=(Q3St~H?U!|U)c*0_!aAR9r`uEhPV*Hz+WWlH)q)9 zyv&3rvRX%sw6xpT4!?z%iH{ znMG%Q`}<=xsk8GsH}L!#=ud~c@@tJhr}USy9L21@4xRscxiNvr7T-eUXUAVt`kUN_ uJsxN9-?Jtn>(AP^IJmRG{RG-HHqhQp75$?||IChMbA9r!l>VL6$NvM&5%nqn literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/Zones.md.doctree b/doc/_build/doctrees/services/monitoring/Zones.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..c0337c4970362552db64afa3c344984f77c147db GIT binary patch literal 12439 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/agents.doctree b/doc/_build/doctrees/services/monitoring/agents.doctree new file mode 100644 index 0000000000000000000000000000000000000000..9100609742dfc5c65192f6980af224b51a9f1d07 GIT binary patch literal 21855 zcmeHP2Y4LSwKlHO+LjB(v|T(HgJok$V7fsDV=x$jY`_BxSgc07Bh8LiyL->fS{5(? zClD}!kc2csI;jLg3W4-SdZU++kdTl<3JK|?zyF*&vpYMIcsAdAke3&IpZ3n&d-^%I z&b{}t$@xMhU-OG4ud`e!*j|cXhwZYTQoAkNt|kX+O1s%wwuWtgv}${5YM`dIXXj6v zG^tede8U<7sNq{QWc!|)-kWVh37(C7rChf2ez8(kGkOKSRxMb*9br_q*k#qyo1F%D z&-QCo)!Lig6E9@@RJ6AOu-pWj>dh`hv1aI0z`Ix;tkAF}JZAQ0XQH$rlc#3&W*5cj z6>Q&vnuaw{sX_3Nx|-daof}~`g!WWhZ+3db&r@@Hv$LbOy1H|Fv-2X<&=9Par{-yz zYzMD0Y#T5h74x2&uPu=$J86mCd$Y3|^fi?Qy)|>5R~zvBq7M&M%DI9)=&3ydwa{#< zTIAWeVcYY-*;9M=slAe(^hQ139*$sAYH#>+pFr&kf08xgRce&CRoa|nP6si#QDw$Z z&>l>w{eZeCP)57M@F}tfZD8c>g4(}lHz(t8`QktwAmw>1hgaCrg{bQS$kt-s_PT~E<)UAqVtJ@b%<6TL=c@zF8NM~d zk*E$bXLw<7Ej3e?59$c&ejMb(*t;ONXz`-esB7H1;8V>QHBaRWqlSie+1^>^T*r!|I_9gOV+Ir&ub$ z66)|ibwqp8eDGVzPN^edoTCDDG>juoUZd-b=5`FMwb)tW9OxWmP7?&R%7GfkwmZxF zoP+zE6@5-epL0lG%>jQ0TpjQ_F3D<>y$vZF);TI)ipli5P+#-YJ-KaP#c|; z#j(*FX?|wA!x48@pw4C=a+`YA1a0ajFz?a9=Sb9;nFIv zYN0{2%$6$I%+^)|T=h@^mrG65b#P;3=RBfW*d7pq!{A zY#VfJ6r0?mTywteN}9rAi-bkV*9EbaeXe*=OxmnXq37!~uY!%nC$t0lT^*>GftABVFK;;hnm}F45)8BB8};0qxT1UQ zaDEEoJlo1UMtc*I*G=M=JBGaPV_Bd`!Nosw?g~31?uf!<}lS8 z8)0-)px(hU9L`}R5zw((>kzgI5)JXDq1PTUbW4%a%|j%kM8i9GDjJ3(OS}t0KUdc3 z-B9a2fqJiYUBE|%gn5i~_Ug0F ztyVOB!LS@d@I(6a44?-y0iFe$Q`h&d+n_$j^`maA&pSMFzAI2)&?12R7spyR=VVdY zGTZVs;`v-IoPvF=*Gv`ce1-H>`Rl#re2m>IgO!S3uK0G&!}Q%(-{{%RoWWdqTJ+68 zeXHG>ZqASQG2tZtZfAzshS69@W45gBaawwvR&$!3$=~awoDI%QbD=cx9Xwg@h)>q< zb7q+{8H(+CoGVxj#-4i6nPW~DKAw8WnQPA0Jfjtf zhn;!Oc4xjhmj&jzct(M}cC$j3~cxC)V#nP6~Nr(Gg5!c|O(hehqZm zy&47w7!Ft^i-x^rD_8=|RWUoqIucXuR6mcIU#5@07|;AuBR~EU=KqgDpUt38AtHYj zs9$qLvT>hmMC8+f`VB|qQ;6Sn6+(Q$NwEeXT!QnRqGt?ZDGWYbf%1H*R^V{7i~+0w z4jPrMykLNyyE2$6`dB7lG6>}2P}!(rbtunk2oh}uR)cC(^heRjk7A)|1v13(Mt@~+ z5Y4JpGS+NdUtimlhR`_9@%`$mE?#@W3KLd_WCGZkuMBtPhr6t55sOouhv2!RU~e6& zp$>JST8ZE^(VA?&jjkvEH4kspI0aIxZyAD=2Y$~v-^O1Ji?4o<25asCI!nI>zOniInD#zhtdpw zoGsmGTUa%yMG&xzMXh)WC?)S{S1!Yp%;1T`@%@vj3BVz_rp^_i{w2m-*y^H79JUln{k)j=zL4?&<}4EG%4zqr-&@y>1~n;kt$A zuhrkr>_WJH5!Y2>p6oQXj%@2Fzdlc0$L&R>NsL6HpNkgfOIxM z>O*B|Kf0tK=2todInIL;M~Cubo5a!LNY#~sz;SdKo&q{t-qZ4>PvR&xg`gvtK*nVm z;=y#J;KZ1Q!Bl5*6eDzxPxEM$LG@#h1hh&hrQMJbi`0myV+G_m4U%o=h>8iv?L;Fm z!l1yXK#r_tnI$N?h0IO_#gfQUtAPO}c7-n;4;(0W0+N8%2utl()}_g>Ckn{g21v7C zPm+R|iRonIIQNKO*YV>F@$2$P+EWC9{dy{%0$MNcY5D4WO4C_0;k@E#PpX4IZK4vi3nK{+5Bu^ zz#uQ27PJWx!0;xLfX)#GbiA>hwbd~ZrgNp7hbKHrxf)jAfKSvi%XBQ>P>{#RW%)1} zef(eQ8U6jc?vVceRAqoaVXxrHC8n$V>5?a^jW5xSxvel=)f|aD0j2Xq(6N!YGBW)6 z0??g3VCOWU3jhx@ZAKDMpCG4q78&xf`Nj^r!|!P+x)8-Ma!#oCht!XRXwS`pYynHC zBrTvN!C;mKP>#r3$@=edt)V=yVU7ZlfNWus-CO$xDKM3lK+&1>fmez&uzi@L*qt6loM$wE;JzdlB9O zx>!hsa$h1Z6VlzciLR^3CB;W=w+bl=KnlXYNSIt2XL6amBu!oyn6{%lNt7xhDS9yq zK=LI>0=itNgmPaYFJnnEcee9Oxjab|JqeAb=t`7<>QzVr+QER4m9LhUu~bHkTkQ76mzvkHdLH~ zD4@6LGS*lkjovO9N$2_ll>(f56AIy=cOVJqo%pRg_g(Um6t@wD@8)th47v9R{(Iy2 zH&1}itk{w74+~V1=*pg>nd0pX+#ON)2;N}-JCFo)r{JaCkSw!I*gw+bqXPdi4X-2W)&%&>inY0$Qx2MY z@DR|w0p&ug?*7*sNfPRYKD0rC1 zE(Fh$Lf~h00+8qD@{&|1<3_>r3oeg>=a+*2R2=_T6W}u|*5}upa_~Hjhk$+~;OT4& z^pkmZ4gvIAhRL{0J#OjW32aidl|9EX-QP2CcQc(oAP;8xBa(prB-m*;B+l$72%={M z{?8g-2hm^TCCNJDMq2$9<)YQ!1o!V6SC%Fuh-5|?HbMVj*o@0u;@bUFaFQ%S?Ixi` zj^b!HJYz8!zwo*%)Zn)%@}5M`xGWIcXDZ5JAKv7QCa5kCO+!9xG#yDmGlU5D9T}I| z>_RBD2!Yl*0f>~6m$4#6p)`}rqfnY9__O2qZ4=-#D^_U^ryNRi@et5F0oS2qmkZTO z5ks%}43lx0dfeE%3v5!fl|8GO?g9qZp;V`{2l8O1g-Ea@E7)l_B+l$72&KIQes2x0 zLuns*NwUtkkyiVnT(n9HZbswEt_umJlS9>{P1=uvu@1|L-nt@#zeR$SoVgXOPLJf{PU|jT!%M!7HmT=lFO(bHeArKvi0$AxFBmphOZ`3X^pIr#1b|J8= zP5?43mzShUjdtl^E{}p~h2VF@@jEBLXI89J7pELdhu|TgLj^pYorC&<{qnqUk;CUu z4@-Zvl0h>rGl?7hFhNO5xU%O2CVw~scaKZ%2o%9+Ml`X{*S{t>Z=V(18z*aL##${%4U5^*kB&Sf<6Hpih z*cvEf950^(_cDmr75y$VH0H0a0W*a!=Fx!ZSfX)hU>^YICZ(?A*cAB7Oq68*82T4HZ3XQZIvS3jr2(qlepQqtam@6+yy&565 zh0CK5D+&H^9KSpPKC@!QDx7kNRq+sz67Z%GR+WfhGA>h(o7@xFq-ZO9PGY(~12>JZ zs#HTB%(N8=7LNow?S{me{RCmPP2fj0ybi029Tney2uh*rX{69oS3^9no#^h$Y8OQn{fq;V=A2wdMd z5J<0LG8va830d<27rI_BW30o^KXq2GW`yo%qx>2`z@D!~642{}UfK=Wv3L@55-P73 zkQ+2e_6hVRYwA1Y@ChQ^QM>?_jrq4xT!9c+>6QEqOd_w)oiecZS<49S@A7f z*n%%aise+n@|=N+MFngr#FjuTP@xDXNQ-{a#zv|)K;LG|%b4&+F~eBPUm7|2jf~g* z|He#jg1QK!HzNt?En=Ls8`_QoM~4yDrnd^n+cXGTtEQ-y@8fTmvY2h@CgeExN`-m{ zKekGRx+1dmI|YF&)VuH$(7WY5EvIqM=F_&VJ%sXCx-YbAHPGSA2Q@)uIx?_ai zhYaX>3lc023VPZNsWWq}C-0SczkuAPL9+X!x-5~pG2SUKuJ8I2cuYe1!1)2e--!>L zS4PJ8ATS{EPBEkp0T}9h7)e043okuiW$8s1ZU)(H9}$o{Vj!``NZfX(l*NomA4QJy zh`8-z{D_U(*kgWeBjaG9r)4mj? z9?tTmmK2KKmKa?T`KIuR(5^aACGKUn#0`Q zra_z+)YQY4+V(Vq1oe8UFo?;_mPo+rQUOGpO?83C0FF2nhDvpH8Rt5dZ>-&DVxuh% zT6oxmXpCT=H@$9DNfz7?nN`Rk-1+ikhC!bx-k=a&yR8cvG~ z-pCbeu>6gzW;Kd~M*En;j@2+~jt_3+c+;lQ-EAyoc1t7W*$rC!B;AhH7K?jqia`QC zM;5P7(%yVgLVPFMo9m)l{3T$Zg8ipXU{NEiKmVx{|99&IeHrnLPT?y^0{W^{2i+xd z9}snkQLp$lf%<-2BaN(3#_@_`8K=8SB|h52Co$}87S5U3D^mUa!n`x=&-(kv z@(zn4UUD`Fd#L;x4pZ=q1mdK$Z6go^40%&W5l1d~F2RyCO;$vM<|9a*#)+for1CYs z_<=iv1_6EEY(;%5l`Tg(V6xIEj>i$clM9Do>Bz)pI4y*puwei_!}4%0Bkp#Yjyx*P z80+?1qQLr&0CZ6a9a#^_LlPtd*Ck)-R+i7H{-RZOgV89B`quBIUu!L#oCwF!7jv7k?g(;U3_OvVZ8n6Mtr z71Vi*Iy;g>OE@2SIJ3khbBpCdk+5Sr7sePZ5VSqGumxiqAHBav3-KD;!8k~A+_I25 z$0}igGDUl$yh3~7H=w;4)|?;NGsjnZLCY;3?Sm2=X5StBDpbIwY1lhlz-?t%ouqwH z)@#n9!8{Offm)49lt!MpZ+JS-uV7s62YD07| zQ!?k;!vi*l$|#m)92-)XVLOLb@Iz>zS^x^8_sRx`bJ&I zgE!|CYQxoBJ#;-f1o^0iIB;3S`cVjcs0MBuv8c?tfUa!~(@KHegZ;%beD-<{SK~ny z#Q9+;#GOxewd24;4lmH*{5sFxR>b`Ys8uyDwBiv=dYU<14`}H~At}yARBtVMd}AFQ zCAoX#OBHKN4omRGQqC&if;gV&($Ofq%FIC3;d(RBh@YG+htM$q+F?$y%Ufxc5YviR zJ-b$bmcu1FRqfbPLUy`gBQ-D+#R@;5M!Y zu?Rk1a_1yGMkgTKoL2J(JC3F`{G19&aqT%#Q!19@PNuaC)+#6+IIvA8@%x+{mWO!h z2eHndFz95#ZAA-%E4K!V+h`qwaaFShatPC-sji`mv3q;x6+PBS;HgGlT7c}95K z1)YXB=Rk7~KC1dy<>88md%b+>LH<={D<(#`eT;o|Ix>3AHi@f-70-~|S>7=2#K@@{ z?#J+`7kN9(SsXs~Mq>l=%tc|NF)}jJSslf`;j%Oun4NV2*z2N=427n@>|5J%PI1U7 z;oqk-IeTiUQXZnS_%YpVy+jo}8z8P!4V-fGE7+kHnQIdxPsWZllONhp7vgR|2bnv} zeRZ|R1uW7Y!K}WM3-3gva{+=%6#7Fdhev!XTA#U4Y6|9$Hp$(Sc9}g_pz~0Al{phP zNTN#Xtn-nDDmq(CS#QiQVA{0-bB|>Ag=$4UZDyoNatkB%;W?lS@!Rg)tG_ussL$hV z!2NXh>$8vBaXr-o`bcaSzBAsV&pX_$haZpVb-9Q2JntbKGuwiD%x=YPH}~mpfHyeT z*Ub4%w`uA=Iw!pQ@rx@|4&%9rC30ZUCPxI4SHaZG)~L_%XaJynl*g}}^~TjHRKN?) Ko8Y%L(D`4EIahrE literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/alarms.doctree b/doc/_build/doctrees/services/monitoring/alarms.doctree new file mode 100644 index 0000000000000000000000000000000000000000..5029ce7ed3e25b4dcbf8d5ca5138a0058475ddcc GIT binary patch literal 19180 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 HMR(;t!1oF_ literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/checks.doctree b/doc/_build/doctrees/services/monitoring/checks.doctree new file mode 100644 index 0000000000000000000000000000000000000000..4d43f2cb92b54618099c8b551272bb48ec404c36 GIT binary patch literal 43494 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/entities.doctree b/doc/_build/doctrees/services/monitoring/entities.doctree new file mode 100644 index 0000000000000000000000000000000000000000..b0173548556525e48204a74eefa51be192e95d13 GIT binary patch literal 13126 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 IZ#_5gKTtB*X8-^I literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/index.doctree b/doc/_build/doctrees/services/monitoring/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1d07fc87123c01902e079bb55ff5689e0e62ba0e GIT binary patch literal 23381 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 UL>2gKuR0ZpMBNj=jme?^0yySw3jhEB literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/metrics.doctree b/doc/_build/doctrees/services/monitoring/metrics.doctree new file mode 100644 index 0000000000000000000000000000000000000000..d9f71a3068636bb777aa74733585d4c1bc9eaf8f GIT binary patch literal 25744 zcmd^I2b3JewG~2Pca#MpNrEA&M`(lMNCJcxgg`7>V#5MnRM>!S*{eEAjRsaQ?91`{W5#}siD8WWw)1eqfT{v%nAHyp+DW~pFd^F zltQkWOOL2rxmL=lVs+e~(I@StVo*)zN~JVNR52g;GyD1%#rUyGv0P0%yT^(uS1ne` zep_GvbaVzzwKnFr_w~;}CGR@<9f64zf|8YTx>`XE<8pR7ny6S#uUVgVD>aoKb5yZX z@aOjRZ)&D5*G7jNm97k@wTOR)%e=mty&$L!1sJY~9xOP+fj>X=7ua*haw>2JN1Y(Z zjX;%!1O5T_+n?v21_I!G#1D@%KO?sHyW-k(~=+%+%%VM|Pp02&J z#7)^VBA+aEXW27j^26QPc3a&aN4RtB1>%j42Ft)tyL0XJ4PSILe9=*@pr(DHNs8r; zHS0U*i;l<_N4oRuO$lo4iH^!}hxWuj!{ex$-IjNYr2?$$A3fk7V}^X-@w2^C!7Q5V{(5(+3(IVH&A{K+T0nHxl#|Q_cm>Zmc*x z2J=&{+vA@a`kNuvrJxgPL%G_JNekcP5bI-Z%0CU`+!FeybDR%joEN!HIlZlJ^t6uR zw5pX?q^qtoo*r?^4knlrFIJuD3tT6tSJQBNDuBnFLOK_uhih2Y8UdVKb<$Wzz%hMl zsZuK>MF!(R)fw$fZFwA8NQoAoVjlBzJgq9F5=7R<(pJ7wE;!}DDP$m`T1n+ATsu@! zcBg+v!VB$nd-z-Fi8Cki#I)EGy>8F{D@&b)klGgdeGDmDZHt7I%fLB1^v_}7+=al| z?2I|(0`DETXN>daPwAXp?@L$L_N>ee&Py!QRw%h*`AV6zk`u}ag+M~CGMd)EIj_D?u;B)pLww%o@%*V z&;~;PGWwgFna$kHoSxXs41)PZdR=p6LEGNMJX+A|EOW5!gnWjeM?Ulm)Pp<+8u8|Y z{xId=2KlYaMr~}YqN+hUz)nTwN{O8oHb0C3M$ATYNz(C_D;FfRYGXkru`$I4u;V&Rr$ zA+h(0eZ_reO=0G5^ed)y$0oAwfw6UcSoeQpm`W+c zu!vi0%D)mq!_a>k-MJW?dSrb}V>XXC>0vChT&SamgR7v&&S<~VUgzOo%^~fXzzcox z2(FQP#I>RSbee-~!f1Jr`ElI@w7my&Q*}MO_m4((11!Ba^!LTAuN6yUuj5j;8$UJ4)JFN(1v_>sYC5+%*IXqv*k(NIDJkdR|WvL9OXblPN7JRB<99JxI^8{MeEpm%k zm=1=~Bo(-=(C-k37uW@j<#$LlRBeuCc z^q)uDJObOuR+JX^YmORE4~<6=DH~DQJ_JsQ4-cltsxDUUUByxBOGpfgd?Pwm) z_as@t`$Z}_$lMqnc*azb`{zc8b^6bTE#iBpOpX^!rP5Ih&Q2hfwv>v&-Lz9 zFM3hnU=P5>5c^0Z4vKUuHlX#)h>pXSn5r)&qq*@^p*TE@#b;PmMkPx1{jivS+)(T^ zqv2%}MMFA{hL^Xj^a1Xw<&l*C3M_?phW=f&4j)46*5TUy%2uWs_YSTFuR?e+#U!Tm zUk%H@CiGtmCUS$i6PCxL>kKW#e;sCSPyaoD|3{nLe?8QEL+HPew)&6P1_5+OZ75z; z?+*PpQLSy*G@M!~2gQQi53xwr?`N0?dX<&ioDTXZv6S2z3^~~QuU)flO?K6)mEGOl+qcVZdHeQig&pg&8`i9eJFL|5<ZOE&8a-&7(|N#2yrMMKYJq%mX_7%A>Va-^{Uw9L~t zw@#^S?=kT_eT#*OVq>B_DL(MuiU59F=)awwe$u2MuBND-)#u%PAw6ot|0u+MEc8Df5A$9b zrmIJ{KSyV!{7(S%$>3Hes)r2KL=&=7|g$6+WMb| zX}%EpUySYdX>C7!rx~REp4@5ee+dKla_E1B9{4xN(|+e_&1B8}q5sub^LS4v$(pY< zImiFHd!!_5zTwW5WXsf%AAsvy*Be>HQ~nQNt%pMYhq1Myfg-EUMc)4rBl_V9##?_L z`?2|CUL8@D+E~i}2{=Cz`ajj2!RBX{@vLo6%U4Q4@6;Lw`4E?_pSSG72!P#F{x87o zm!bcwI=5f9aBBm2SFuoadzX^qBjAWV*35{7{~N>xx%@Wte^=-7`^VvOXoHIj;ubw! zCs#=Me*nuzL;sI;mVausmL81%XOo4h>`M860fWav|F3lhf18NGqqNW8TkM0MhtzmV zO~I}tvq$ZRB2-iHH)f{xmx`H0%eaq$pPGhFZvWMEA)KKJ`wvC5UyNr)y-wkGf%AnD+qWeN2JU|o5Z^p@8i++}&-UpH(<55mh??pmr z3W@YSh~S>bVR|qC(D@J)p*mDpSzaWG1B-R`w6YGg-ycz<4%2-!2KHIGf?6yMW`I?P zDg}hc5_H1LOHqXCaQv-@#}QI7N3Y{mrKJ<`sg4xNqmq*`(x)Ei%O`D z6B2Ga;GAVrX_4$v6zMINjLi%#LbY7rR{u1_<`(;`v()kI&v@jVbjJ$8nQXJY-Q?Lp za8H9rC+e_%7m84I3#H{nj49Tzeyq((!LQPIt<7qwm|`<^ZPuV)v{@^(>ol#5WNmg` zx5~0V6*YiNU5@^l!vbn!_-wn>v| zrB0TLDLqqn@l)6z8>L6+Pf60BIte{R(H)yv(#5CYB2-%hu3fxsYu{RRI{PyoIVX*B zhTzO$WqVhV=T?HXQG~~tsKY3|C_;6XP+DHZm|_j1#Mj@0#f7Lsmy1w@irH&SV=v-Mp@uGo zgD)1cOEj5Q=~Ahf!W#~LD*I!T3<&*YN&3M_=qZZM*v^s;&fy|dLju>25pd3`Zj~n> zr+C%=rby`Wlbyo!7o~>!tRVm#z}3)1ZHxLwDAenE+z|wDs%}@gsLipmKX7*P(v5P z#Wf+@smZiTyQE?YZ@73j`(u-g3;pFu`YR@(rzm>kN|tnSh>K7?P2hDGV`aOFfQ(1h zNuyjXKyzH#-gRWUhv2%4!Q>h=z%19I2-VYt)bb+E6l|C!*5^9GU$61n9ydtEl$-Hl zefFYX^w}r0H)`7cskrT|S+8zlZ^k3vq%t=P%48ZTa|^*eF^5}GgC@732-P!$&hjF* z6lrK;IQW@D_AE`N6?(Q*OxXSaP}c@b-hHVhMM^m4(! zLgTeX?v#otIOD|{-GzS9=#@hIDora6TDeNSn!Op1e3RO|Mo=cxNSoIZtX&KauR{%* zydFiU-XL_A7qO*CLleWrZxph-HJMiEO;RysH(dN?_QwW!i_pI{N&mJ<=qZZMcsonF z_#LUN`bk-RBBqS87wVH}2-TV8T3$3Psw+Nm zk>*fe6Uf&!1X0v9Y$iI?H>9s+*m15yeG~mI@*3&^VfC##E6{#hD$OqwBoZ7RrE^w% zM{r0)sPEz`RNs?&^icGDsTfQed5zDwBx!#-2`$CY6hC8sJjZ{IN~nGzB#||KDV0{) z6h-=9vE;h-Yg~ltHv+f%k22C1`Vl^URZ`y1;Pd=%NuBX1H@Oo0PPmvtv%Ni(_hC1Vcodg3z!dCtS~F0Q0ro>D48RQ0X^?ew81#5QG{xZP+MNaoZ=1R#9FNt{5p--R>?}m6pcI@ z+9`cKOKPdowrjdGgMZ%NXhJ_$WV(I{uIq*1owB2;GzTt5SCSl6+7 z?FQ9LM8+fUq*=}qq&cu`@9E^ajbQx@6mjW83k-8Micp;+w3ZjKrf9=3u}0?#{ydG> z7CB!kreNgK&`#MGu%t#dE<$yo!0V6g$jn?sK*l5Mq(Xd3n&{>r3SCTa{jnWPE1d}~+u0wRC5H-h6vWYt;w=8IP=!W^n~*^3C>cCDS6o+ANxhhX$Bs2Z~UYgw*mP z&J=8zCDvzD@MVqH_NYk3l#7HK+9`UBCH3)f5h^9{x|@+$2?)q|WS!KfDnOHOq)(0D zx|_jdCmNv7E)=2KEu@wgai(BHAH&V#LUy?((`sEI6;pg;K3~cH*es#YKP^dr)g<&3 zMYCMZl4jY1i%?x7aJ|T7J63H_*Rnt3k#o{2PZyjytZZ*Dd0t1bUgU(w^{B%rH=qdB zUZJ$Sh%v<)Mv1lAC-@sRUR&cPshDDsQbRi>-^`NQ+=7cx-74^=49#ssWIXat>hlai znv5fTo=I?1hUQslfkw|p5vu11t>s0mDcaD;aPxD8>~>A26?>jkO!*BrKcD@vVO}8g zFHF+EXcBsgqG4Xll7@K+E<$yO!1bFV-5u-HOWB?A$T(@0mkG=qRkn8GMW{n{zaGqX{~_2}P*hEX0-<@uqM?r-qw(RnKmCEm@7X2*bB( zhEUqbYP?N462nw)M~k~yvKsH;Wt(I*&W*Dg?-T-NHQt4*P`z8~mi*YdnIG1iBVYZV zR{!c(y@yb*jkkQ^l|>XP0^0{tuQ#Ejn$08Dldicoz(*jZjAje`@D ziT6GzkPm4{|5iLkwS0pkpTpuwP2M7mo?UZ!e32_3h9M`z7whUT*U zaBWtPuR*K(L|ChO=f{TpFzI^!#e?|>n8G9Y?oE9A(e9&H=G zC>4WABP}zR*%ZnKqeNQfOTy^ONk(6J!i<=fxnCH4HOc5}QZY?ZABFll`sKms8$$cd zB<%x}&{7Od@h$epOVzhg3DtLmB(lbLrP3;!qDcQemRzd7kBd+}C~&JkgV{-ZtS`lgwqX^ZHh2HWa_8fw-V8xpKMDUMjyk3NV zDiu?B#)~!k8Tv)Dp9}3TG_8y$vCRCEDCA~X=w(Lc;jaX3GLJO-HNjf51gGDi6`K7P zMW}u!^p+Q~=MW6d8cT6&BIWnO;}4pL*7i}Um;-9W&>y9riIhJH@t>2#f0=}sa_FkZ z*dLqvuc(CTZ$c6U(ch)gDx9K7KLzh|imCU*1>R}GUnIjDDTn>ppYg~!Y2;~wGudW) zFDB3Fq)DV4W}pu9&O{NaHlehVWA%n)I1`Pabb}Nn)!k>8AtjoAXsnFA}$Nj0*wwp5vl`)*773O6m4kK2pCx_ z775uwnoKKpuvAR>883G8A?%M0bEwcCmZV=i2|Y#8B}-Uxtyqc+yqhHOM7rS!_9L^z zB4L)a;LKrVdoLx=BMDBV8;(L9MmZV^wxRT6mpITV?K z69_o6rEWoQwi_B_Ty#iKvW6!G>94eR>3cipFbu zoGKMlF5b^VJ4J71NqtVk1>Rf|xZaJgT&q2OIw457ut;chhCofukw#ky*1PeT&6#L| zPQ57b{*n;m{Uzd%Z9-w_WVm{pko9RYt=idAF$d6a^*QX1O>?f$pO>URe-e6%qG>K* zNz>T4KxRqcbyp)>a1jB>EU`!!hHueKlvx@i(~AkNyBbU`K?BTkDGFqkgcO-2qB0%@ z8)k|184&zs8n5j!D3w-w#QJPUzvz<_+96FVziQ({Mp?V_gdpj{BB4=1peE;RuV~~D ztX&#?0QY6Rl7kd<^USb-pl^jH2Z}9 z#w7hslh9KXO>;9#n&uW51<^;rTgdoktB4L_m2-F-|wl`0<&m_1x0r4y}!8FfC zfjpBCBhN%!#-nhP&*zb8 z>b0c8OHwSfQRKCc*9kBoDaxeOIlrC+dL7q{-hc*J<&7vpb+?dPUc{Zk#VX>WHwom; z8q$9O9wxVzahQ#KhFQ^vh~)D8WWDMlLsG=|hE5JAUpV>9*eiaIs1IVp zc{*~87(a_o74ZrD*3BE$TOgoWGkJJ>tH^EDtQgzuZKUgY^2YUc@Q0z^fdX%6$!IJu z8Z1pChB9COkG|;}94b}vJ5uUhz`?2S79Q`>Jo;C_lE*}LI4ONhbZA(|rV*!l8V|i2 zx0YR@ySEm?WtkJ2jAPGf5~1D;rp!6LkIZd+Q&@J&l|a28)qQqb2}fhpaP*wI2X)@> zr{KggyM6a)X=5;!%RA}=XtUeK@QwmbgmKgdQ8hjp;)yz?*ybN%f6o)vK-~*E*z!IU zq53fXMz2|Uv`i!7J|g`e)&2daV7NTG2HCfAy6J0nIo=EB6bw~rh(EW8g8}(XFwbkk zDF?&F5kw)5Cd}b6;){yf`>MM^2u^gqMs2kM8#- zak`2P@p*t?h$k`(^##yC-7liRyJ`3vJDjRD9R6kL|BCMKzaENi!8gmJIEM}E3qD^S zl@lo=@EX3iGGr+8-it0OP zZ94o@eHSe)3z8lZw-euEx4!MiqSW`%1`|DqB2+)X-#GL*K7=0L18uJlUOgm@<}!{W zH?bP0)DO`M&3=R;R1f1XGh(yg;Dkapj@^h%QBXeyrgy){X!6FXa8f@(JN15qJYs#l zNIUgY_8yT-o{FS?#)9pU(m z`u3K9=g@vbMC6i`C$4B5(yD%o-p6so@7S#$AvAOKd$eGbe?SqcNAWjy1IHt7@U-o{ z$jE;b%8LX!|0?fvRP+DWIs_HW^tLLbzhg#w>`3MYK} zNdo*DjH2m&G)GW#HL8C*Om|Ftb{g(T;&8bkZPSN^TA344DXiX34mW124#mNm(RC($ zMJkQ|oV;6!+k`NhHjbp+dmOIt*8#S}b)-&QRWB zo}sLkkVD4fAd;cAREQE%qtL2LIh+JNlQ2C3olriFB2-5TE6a-{aZpXMb(Elv)~NnF z7+dl?Y$LGbus;3_EI%&?*0Ryd3&XoQb31dz5&G z+tp<&bRSRWSjNc1f9PAedfkTX+I6e5>({L9jv&=a3}`^sb+5{9=w7)#m66?e!&>#o zJci?cqLH~T_SvzZhBg21E3b~jNa5OLC_-gX=_s!0#yGAK+Br7cv#NOaJK#RfQ7QvO zpFO93t~KtOfm$w%j>liS4GnUHv05RG9c-K*jE@dgN`o;~pgPe!pt{((py`}!;nyv_ zE7^OXK{_aaRRUN|KzqJel`UeR)=2AG{H06m+3^wGe4JA2(2PU5>$=p>^Hy0QTaUjS zJgr{Zs@HWOAo`Feb+)jXt(`Mi z!WpY2bq?BaD)wxsh(p`U5(4U60jAts2}fKC>^#Dz+jF9U=_9z+`NX-K`s^iebwv%r zKo!0wGN`LA9#~7byZ|tJK1Uva(1rXGAGd8bPcJw_wGnk8Iob0a968TW88113>)-}@ z5!(0JOYvxn^IRw6&!1)GFtcL5sxD?_4v!PZ2StOsIp5SJXoENB6>t#XU_EpLbt&30 z3-O4=2doJER1KcHE2qlT1u8qaQ8gggMfBHju_Rs}#6g@;1#x~EI*~bW#yXDeUQqbSthk-5r{i3Cc`jEuVJXf=RPQVXJSJTYN$a9~sgm0<=!{}rAIueS zj5`lKS9x^qwU;71M(fK!BYp;T9-{!zK6`(syi+;COe>C4wrT}vIa*S~(mWsU`cx3W zas+M}N8qXvXCbZ<{^0IY?DN;B@7JzT#h#Th-lH+ zwQE=B*m%{glx5K4m?_{-{V@Ma~Em7t2-R~$s;^I!Mca(M~I{J z$Ae2fK5@B+x);^#g-!o}g}w{lDBN$tU%X+po==E3izMs{<-M%Q?-fr!)LJ1?o0b@Budd)`mL&3q7!&Z~y=R literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/notifications.doctree b/doc/_build/doctrees/services/monitoring/notifications.doctree new file mode 100644 index 0000000000000000000000000000000000000000..7f150e459bb45454107968c44bd99ea2299cc16e GIT binary patch literal 46340 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 TuRfMb(+(lxoAvmu4|M-OICaqh literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/views.doctree b/doc/_build/doctrees/services/monitoring/views.doctree new file mode 100644 index 0000000000000000000000000000000000000000..3244afd639a18796331f7f17b51ba19b10d5aebf GIT binary patch literal 3821 zcmeHKX?Gk)6_sUcnXx2WPHd9MCY{8FF|jl;gb*B(kOT*e!Vo~un5AiIx@)G|>g{z^ zYauj=fq<2;@B6;*`@a7&Zgux)JLVH#_<)amPIu3%SGVrlZap*Z2dQ6}Fw#p&8c5x? zudz%_n-845#^WU)>`*Hau{4{R)O@JqiO!X|v9Ylz)W#7}%S5K!!?n|EOQ9F=+nhU#{ zXM1!^6eb;{sYy~JJ#9p8cy9RsO<8PVp!rD2M>}eg<}9#909&lF%&RGys2`6NH0`S} z3a}O5 z8Zdl{+QOJT>=y=fy|ColJ2V{>nYGjGxHRQQLTfzV;nH+p_^Y`k`%ae*T3+sQ`LsGF zXri`16ef~iygUwPP0YEY#ua!u+u2VH;uy&`zXY`0UGlk(x}}QtQ;3^-KG}QNt&oEc}W3hx1$_a-QlV`U3HhM&bWobcBu9hj|!B56Bm&Qvq+w62jO5KZOomtU&VCESy9r-@-PmH(51+ZZ6MZHyYl5&Uq;EKIgYi$tCLF&(9R|*NXuw&&OTxH#K~knNb?x@G;9&a z5iCax!3)aRF1Kmu$Rxy)s8cp5kL(EwKdR;hN{$~>lfu^B%qTps=_p&q7fXKJ zmWgfUPmEf5UC*zD>xb)lR#hRtZjbAxkhdLPLcDL_<@Gqja>-BFGgyxoZ&Z@F?1;L-c3wGl@bbS5yBTLPm%kQT7J>$JvEnM!?&HK!~>L=gjfLeZNs#dO9x;(J#(acCkRKslu z*|3dpM{y{ZLklZQb0i;M?a_1?G?HQ!l45o@7R`~%G0l!Ps{te>r*t$-f-uLJ_n=0Q zmYOq{nWnWg28~v8zH5#Sa!et#*Y;%*70_9%Q#K_R9Xd9m!n3m*z*jY|0me;mo#p|g zQ-*ai!GzGf-lLg(;KLLes>mbWpao@2cA?ufX$ln=N;6nW^I=z+IO_5ywZMQct!sYI zb*L|y*(2DA1|7mr^=M9G7-b%2qt$(y--|e!)NV0CQ_=)}$v63ZD1#=)VIN3-n#Lie zo}m^-XPKDazVvkizw$voRrOFcR% z^E}NxAWZ`h>ea6b^Djc&K14??M2#g18%DOqK1>sV>=#46-lL;3?n@ij%}8n`rQxkD zI+YeCE6jiL`5Z(Y*zgTM4@d}YQpDbdO>q7QLUJT1;>_DlDa{`RS5Qx3GDsV5E2+!UFe3JPl20B z2$(@Qdz-X?6aD%^%zSJ_J@2!V{QT(0dX`0)cNBmuypq0n5_4jCMbmREGTA(8+VgnUw zYXL3W@DHw2YbX;;C`kNc@6R2Nr(L!BjGLluTgCnE7(e>dsNalvqDpfWA)}<3w(n2N5yt>HA z#8Myd@2=BfT#Zmj_51H_O_GA z+kI=S=f+=3{;N8P{*ICRw@Qjs!*g{1!o34`49rk!jXJ63zq|YonzR{#E6_hn{#Vgo F`ZpidW_|zw literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/monitoring/zones.doctree b/doc/_build/doctrees/services/monitoring/zones.doctree new file mode 100644 index 0000000000000000000000000000000000000000..813107fd4ef52b02a9982a9632842ae69b5fb46a GIT binary patch literal 10582 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/networking/USERGUIDE.md.doctree b/doc/_build/doctrees/services/networking/USERGUIDE.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..595d7c25996a4720700f90556ba7b701d09f22d1 GIT binary patch literal 153161 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 IGpYao2a{)I5C8xG literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/networking/index.doctree b/doc/_build/doctrees/services/networking/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..fd58e44cb292edcb6182638ec0764037d87a5920 GIT binary patch literal 19923 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 y;b&BP=XOGB6y=?(9zQ_EctPwx6EKCO~l` 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/networking/ports.doctree b/doc/_build/doctrees/services/networking/ports.doctree new file mode 100644 index 0000000000000000000000000000000000000000..68e747bd49ba7aa60ef0eb3e6d91eac3ec849842 GIT binary patch literal 46546 zcmeHw2Y4LC8MZOF$pvG$rNdDz3rlj78`anpM_}rKC`LZ%PQDvUI_=!a1z36wH4r)h z0-?7M2%SJ6lu!bphY|<@5=v+xbi)6>-^}jq?VcUm!Fl-q=l26UJ3H^!-kqJ<8|{93 zMBnyIp}n`1?<_WS7c$vm4Xa(*?oy4r(x@ZdzMi{sZE8?=x+_~+)RQf`t9Wj|+Lobx z`t<4T$)ro!WIEYX&?UG3td;~0w(BepcJ+3a@;#kdX)C$|X0q!>db-t&R>?mlo=8I@fcR|)2ctT5!m9j<#%@WI*Y-hG?xn`EYI`hSnH8gsqO};0Y&49yJ4y(T`K8( zTW<+rCp~wg+SI^IZx3-rcjMWq0qvdXViEhB%uWq#OSjL{ByH2#sg?7YqPy8_ck@&~ z!|%v+s-F?7o$Zdw4Ndo^R_V-lXWhEyzR+pAn7c&|wf0=TGXsv?(X-w97|}REo!J_< z0i-l~?ii3Fr*U^VjblOAI=S_7>*qE|^%JB!E(gcQ*XA~!o!ew~ZqwPh&1UCnXZPk{ zI0th%7?}_~6?ogp3x5*7j)0=+2H`~_c7~FQAJB`8d%9?I2BHMAdhy5ug$ects zy~2=k4*HlHx4hU#R&)tauQQ>wHP?QMt)%0AQ?#b1-JHw}$p8F&Ev?qMpn^$I` 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/networking/security-groups.doctree b/doc/_build/doctrees/services/networking/security-groups.doctree new file mode 100644 index 0000000000000000000000000000000000000000..2c13db9de6bbaba5fa84234d080401a22f01541b GIT binary patch literal 16954 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 N77^@E;#nT;`wtOz?~ecg literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/networking/subnets.doctree b/doc/_build/doctrees/services/networking/subnets.doctree new file mode 100644 index 0000000000000000000000000000000000000000..b1f486be811b2dcb9e2ee689295d3d64fd2a4401 GIT binary patch literal 46302 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 yi>IJB zMA8HYNbDr!CKtIw?u(E+;Rw0NO_JU0z1b^!XYcHlJ%8_Y_h@D$^Ep}bvH8CIp;34B ztE%^Y@2YxLcPuKFykZzQuHRqrO17Wj*Rov+GJ0X(alI(gi?e1&#VXsuOx5=Fl1R5? zN7pP^u%Jk`71)Ml1a`UVkwr5`NUm-j8EwH=-wwj6ZW|fx!b<~%uiHmPjhMvfY53G* zU>d7r1(tEtp3xm6qZ>e{JU?(M6ULNX@SVW+4X`+}dqt#ILTs|8d@rP;t-H+y zW-A5*FnZOT5$qz*o6%>X>*`3a$tuvRRcevhn)F{gx(|{Jn7&Q^z@$}n?13tIlj!IV zR8^J#a$eQ0h+G5E!2l?aIYrwa@Cr@g%Durb0d9T)@(0@Ri^Xx`Z2Su=vuyy=M7_Kd%-FmB?fHcn7P;~`PvxM z&oDP6pyyK1b6zD^2YQcXw#4IY8q=Fq#tO|Pu2Zq~`NNlDvc!h;1%RNvs2sNhYHveNg6(zgKJ)oQIeTdgx&WC)#85Vb$6){m(T zV`}4=GRD-Vu~2~{1wIsJA84NZkUb4UA3U8Ix88kRKP%D~fkDNyUlx%iCENMFw? z*GSH;SOu5EUeT)r7EGS>FeJS?F~*KscYOmhd*eKc7)d7|QEVjpBE6ph<6-v3!zfYW zP4n~x=P=|0P~ej=bP#Gd6lpUZb)TpK=Biktnq} z*GoJ$Pv7CISmNhF;xof=9^f2_w3RY&O9MECNEaELo$RqTV3~DCV@g?p5qN^hxJT@r zWFXmCp&J-ZrDRVVRiqiib&lFbpd5cF(+o~&9sz6M*aFUYq$ikzYt9GP5QB803J#=9;7B zwqsRsP=e=PMteWN&h`rM{VFKCy?s|;sx)|XHjJw^dm|yKPnX>xziJ_=`(`^L1}9ou z2emMQ(4oVuxul5YkUloL7%U(=>nS_QCOcE_oo){PPK8roM$bTk+amq^UrYk9`GQ16 zIx<^4#C+1XTXh+2mGW3!FNC^&Zh-V1(9H2jzX;4pti27IK@x3^nb&uUq7+BuyCQve zN|k`BBmloSt2pqQEl6Y(=$8yHnFX&Fd)00wk#<)K*9i$eCnNn*#^q|jn%!6Bb(7`x z8FO9DDJsJzf}4p?C2m)kNgYmn>#oGGCGfm#9>cEr*2~4W?uqm(c=ql8=h^iuVQMGq z{pwZoOh3Gf74>Q;>I}^I8ffmdk$xQnP(1rRsZViWXRo<8(ywRfPH7ao8PC{j1lqY? zqr^?#kX62F^oTcR)vPbPDfNZDsdC?}*y!(z^jm&024M7WO^yC-snNf^(dh4GEZ^~D zMt>^#^g99FyCVH=FegTTpBTMLjGdG4{gHkTvwj;a;~Lu{#B-yJ(1JLQpf4*f<09h% zW1CU(jEWZ+VVpTRI3ZzfF|gV;u%>~t8h+I-I^#1L%fNcX#>%&dbl@4truH-z7rd8Y zn;xi3E%741U%z)YfW>Qo_swTj*Qc<*A80(y9De}NelXG>0(gqKzhA5pVLtW^v#W7` zr9aGZ_#-Tp+2Y#AY5s>X#mEoN#cP#X!Itw0U^N8o?yjvqk*Kk>mLO&vv)m9X9VphLl7pC(+d5?QZ0j+y z$-A+wPk$1!ek#(RPR;0n*o-cV9ntJ6hQvF>e6s2L%!rvO*+uM)In3$Lj+ot67ZlvYWV8w zM^uNo0iPRt<b&Mn1|AWHfH((EsB_qoA?3)cECJD7R&bOc;t{udkzYVaz z6Y1|_NW}v^E+|W&dm_@`x(>8qT!jVflu`SV zJ+Ncv@WK5%cJ9ub2e%j&ku{UeWcvEJOS0=mpQLwgd+eGAdHa1Z-jip0i?y9-?-rwX z?~YwqdKjCGgHSd?hD8%nLNOd4;loB0n1KFeGdE>bf;vzn+J1j-wEaPSwEeX-+IFUZ z|GGrmA4d8&eu+_lf&FG;4*Iv!kpJ6_A^!9R4KszaK~XPuU*0;&!N3lQR$8 z;Ojqw&Hbp}5&yhl=)@6g1O5dNpJ$Z6gfxE@>A%J#ibwo$?2(*4e-i1x;kjEDHqXs_ zOK(byA2~278U{|;&bdw*EB}(Q7a5c)HYy_}l!>@7B}q5a&=ExyxUwbXFHWYi_Y$8s zq$0$hypVUUrq2ns20#V?B!v`fCSgX_)R@JCYobtwEQF|UR9$QejM&|M&i_0|rndX= zjU0QjMI}@nQG^`Fe$bYDkDr-dvW?mDK&fBl2H$MZB7CMr}QMd=eAGS0V?SnFS1DcDT&k$D2bNyetL@Z6BJCs_x`+q9bh4?Z3I(i%(+gISAz5uJ_SBw|c5l&}`H zu5Rx~>xAr_m~8ZR7|kK9+EC==L{eMKA>bc50;g-z_>m*L%=3~;26BWmZn*Y1_7P#Q zg$wq^!Ck|~l!H>@gpF!3Tn(OwY*zBhhO&Hx3>aG&iu_Vj>~D6$bA_|H=Ng+*OF2&n z21nPPnsr(a8bGoE|03Eb9lD>agIYDoxSXBb;)k*4$U6(U@Ke-ZV{P?85GnFZ0R*_IUjC@W1E>n4nK#x+EJ7#^%# zh<_116Th5Fo|36*dcblqSuxTUnKlz+)eWAMo4zXB%_<5hRrcBl4olRaU|93C4<;514yaV z*Bg)I%3U%_DY}b(`N>;>3ZoS3Zh{=rwQ%` zJU1pd7MP>ejr@P`v?rkv&;!za_!rTB{H9rnnVyrCZW6KsG1=%wz*;lK{btP+IcIFe z!E#7LbP%-jSl%HSVy@*4rr?>}Z}7CHxEZ7n;W_vh(WngEJ(`?P!o28+23}U2a_A&y)+En@@ZBR7Ud{Rruq|hGM0R=gHS9 ze?6!f^KMj@I}8Li2Z-o)|gm~@-MFLRGO zMyM0ew2-$urMP@bGk8Y@wX>dCpKg<`&*!e4DIYP1FF;#F5r0{M`o5fL=W&jOwY*Qa zOSc#D$9CioF4g;V2VU!L35=7J{7adexn%5T=s3Q6^dkI5bSKlA-6@`VzFh+;kNR{M zzJSkFI73H88F!RWvn=6gz_ES08{bCEWi(zy2i#XerI%ieHuGE+1l6H|fxz={LCdj& z@qUjc2KcaZfL_8B9l)(<%O#!@ct3$FPj;0hjn5r?Iw@m!@oKi3FM4IZ+vU?srL{Bg zc*c~)m+^ot_)atQa(?b~t;$4*qY88^*>n%LEwU^03V!IoJuO@eu*y|>CEl5T(NSKi z`t&Na4MW$El#e{b66n=Jm{FD+BxJ8)vKF&5;V-_JM6YE!l^Ze7lXI#(aPm-he-puI6hN?9+W{hcn_Hlv5e^ z65_YS#9dPsRah2`Yg=V{tI)1ywef{ER(?LfCe#JI^fr9NDYaeAojkkLNX_*W`kafMil!ql+VO zeip7rx$`2YL|^2GF8D&d+8;yXN%P#;?QPeVumo5Gl}~Ob(w9I2Crb1o&f(LS(Ktq5 z;UU+EyEqk8bvVtyiKTi-U&Y50=2BeVtTlZNO`IeI&st!A9IYc^!Cc*(%HUfR(AT-s z0=X1P-@tQ3-^6cL_27!h!#LM^6eYn&P*#6P7P6t)-Eavt&b8l?e&5C~N)8wB7Wg|d q29^_YZLRsxGc-HW+~L!AK{`fH;MZ)0Bj9|HzK55HzK`Fq(Eq;%w?;z% literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/Account.md.doctree b/doc/_build/doctrees/services/object-store/Account.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..e40aef4cbfdd99c6703c4387ca3ff3a781bcc60a GIT binary patch literal 4725 zcmd^DXO|pD6Lt z?l@5zYReUx!yt_|Q#Pg2w$h%)5>aToqN8Xmqj5GF^F#?d5K8eb>{eT}g-tqXl3;CH zMQP#)+S+7QS2$6^&1{-zvK>6tQ74LY7-?ZE%@a+Nb62rZZNn_o^v}=Hs(mrcd;N{-J+efjYufrm}RT93qb77=rI5y;Y+1&*RUZrf=$3( z+9QYANcsP@3uk~wgDH`y#swaDq7f(21=v!JSjOg8k75xzew4Zmpxwa8Mb8nc5w*>* zy3$c18nXqMGl4skC{26WsODX>OWMaqOV?;Wt8%RqubpaO_}GlDtFbXRjms#`M zmORu-C6<%rA~$Ue#W}GILf*X2@Dm=mL{H4<7Az;tA~&XOp0+p6^F1vR?%QoYaxPZs zN$~O3jGk1i{ij3efhv+Ku;h|ZgU&KFUx9nJ%P%JYRfJbi2(JQD-X(rpH& zr+{+Hfqyi2i;l9fI1y$ULB|Yi053<8o?2s@9i%VazOV`V0>|jMC{dm0q58Cq=y;)t zbCsSBSY|W2;}HZ4_I{$I2;Iplk(MH%lYAXr_Q+ceA5Y1fA0eTnXMmE=%;;HwkqsfR zv!EnsKT^O<&(0Yt(zZ19WGa z?(U4vnz)%FeBKn{vTj&+mPL3Tp3IdC0o;;kn3CLRd0dvH~8iSWXn;s0dLit(7RXBwrMk7naUZL zYv}NA(BWWtD4UX5R?qtJuFz$?Yo@F=8rDhAq`#HmL`gym&sHb(ed{hT+~t~EMeM5w z%cI>`D-Gw?%1r24ixN=G^%gxhMYtEapCY;g6Z$mEX=3ILd@eY2%=du8A6mHl9_>^r9Hhsd2rKZ7Vj z9>p1<`6`YSFF#Ub|0ogWf3`&@JXD=132;azO*YYQkn?U&Wu(~{9*Zt7&1jKrS1Um~ z^6lP8MN2JOW>W+0bv}EgMK5AIHw?G)PcLrKOW3&M>AXExl(p!khPrHPuaD3B$d{pQ z?|Zn`@!!{?OUA7@O3wQu5iWWVSF!CTn-et7 zHq-!$hoZ|ZT4iGp0NM+?ieA00W$h$5LPY3R&lh=5uZifj8NIH?cJ!dIO`8nIFDiOH z+_)O1qr`=DL8IuRZ}-qmzoE&tCLISup<&X*r#G^_QtS9=qoJb+TFMi;GZQ7PN(83j*>1oj<*$$tD&0_}F%$z^E@pZ$%t+WR~c8^EOt&mt=+B z&L(^wc2fu^j7Qh^4mJ!C^G-I7aws5*f|%ZAJj}7ReX%HfdN;cHt=I*`SXjuZ_ds1q z?(1^Ed*>=_qI9AZ4ex8pnI_vOk|auOcpA9?)Gl5n&Q2e3dq3N5TvV7Mv7nD#(g#?@ z745W3A8fMiB4`T}*A;ZUQV2~ST4npARL7})IGZ1angSY*rU&5?LK~)my=(|hA3;dA zxoHsFeM%|%D6oQ1@xo424*M8}O)l{yG!Da--~oNS$#xmsJLtVl&}=ON_^Snd0*$qZ z>xXW-#idWODY5K9f`ZklDzW(#uo*$K(($_V=`vOz+@hyE^!D@_SVmjVFWMr2^0m1O zRhi4K&t7Kxv6G;mXlqfoi>Cf_t89}97wPlOa$m6$sf&FCK7GN!w;V?xnFrb?A&08| zBGSoV0%4sCs&dC)T4h@b32Gbi(wE_;q3Al6eFaNw0G__eDyi<&Z=Z4kX;41 z3$Q80y?~-0ud=Ns)cySY3A?V$&!wfMnRrFZD9rQI6^jjwR1Ny6DT-)Emu=~FrH`Mc zpFs*oVV8c+_6?%3AqjpVA7qoJUfF91{E`iOF8vD5Mz}|SMlJ*O>s7Y5NHgJ!JlBDd zmUd66{|00(G0mx}=(jEU9owDL)(g=mpG~m1R`^#(#ZY7XP#rq#j>CExz7r;L@ z(>B|+v4j_wClAdH6?XxD%IMEEIR&-8L(Zk3*!p{$;^g>CMt_z2P~mY*|1CeJa}C~8kT-kZI$H+yC8&0g6%du8us&+k=t&moBoOLl)h_V`1i z?y6U>-h1CWs;lb06|NV#N$dxDB#bhdt5vD$ zEl>6K^z>*ECr#BiK6SZyp*cUCvD_$(9X}MLtyof&%t%Y?K&>91+6gMx3o9aYYJsqv z(6cI@ud|o-IMqKswI(CAf@o$2Ox3{n)HXB)zK*Rn3$J5PX?$uB6n>;F9g}bxJZ#PQ zl!eAt!}4RbT05TD8+20Bu^+<{QCRgvU8})V4cS9YhqS0Rgx1as)L%EP*4smFLIe|4 zb(D}R)P`}pHTED(S7}*FZI^59wk4Mzr`3+c?sughcrb>#Y+7AjUhGU-xNMS=x&pprrRq8G zC35C-yA9htb{`B3Q>vYEh2598@0vOd+(vB}M32q_OflM|XcoNmXj3-%uM#yy=mt^Z zjly9^jpSA@-4d)AmY+voftl+{_9_>7 zP~AGU5_-YY)Qd!J)xpB5w=K4gA2QA*^HEW|9b?4 zxIB&j!>RkTJ!8yWw8dDYKyQazW zSBv>s80KqmT!5CZOw~P@Tr&4Kb3Kl%dsCINOpj%eWmaV@Rul#c?al*%4z0YQO`2%+R}L{w?OvuY0Ziez(kgRWY4=%l%njYZP z^k%r&vk4XT7I@KHQ}s5O)MT}Xmdk1nr|RtmL049LM^`+lcgjmmR(qH1H(6~@0o}W^ ztoBH%-ox1OCt*9GJ@qHyd*L(>cc!=ZbkmNh52orv5J&PfkF@jL zqpA8ZOZgC~ z?vx_z;)IOWwt|PnaEK*|`HtbQSAOJRi{+tHznRQS(FoM#mzk)Y4;*ncT8Zov`NpSQ<8YM&)mr5%rM{{A05_4FJD%#3>mBtR}O2h8v; zS7x}kmaM3m{H&Y8_3J)5oko;kp9ZVf*hhJFQ9aShG?SAjvf_!23?~UnkORM#_ zc2?_eE2{N%1z6v1s`ckm^&MY_6sW7;nY)DguA;7fcXwU=EZ5cVfonW7P1X0phToT} z?}wPC0)B3}3i$a{{Xjv`RRMpn%VX6K$t|V={;*taD&P%;IesLofL}<}kFq^@>(s2u zmft#^m^h(+3>bXAv-tgZ*Hn4&YfbPIu)x#c`jgQ1r&9IP5JK_{Uub*G6RG+cmTi;q z80;gOFG;Kcbzqa9eIgbD8K?N!rB3m4olf!dg;N|VtndrQDZZGhU;K|J0`9+5;Qq@6 z?!VHF`xA`&uR`mmlvn*4l>PNo{RSp8(0*|_Xup)I-z*5ap#7~bd#m4;I}EhHBi9;e zZz`1f-3;0CCRZrp@|W zHD({bm!vb7eW=G#6lm-Y^9G%RH7l zjJit=ds_|~t>r;u{~OIQ2+iObL-?1{I%9|7*~~_>>ZPi1vTMo@&TkjNTV3nEw%~M{f6CAu_p_&JeS9f{imlpfMHPUdH9#oHD;3G*<1!z zq#Xm-8j|N{BvaS4ieYPAd?Gu1F1ExfhuRC{c2~D*CO)n+EEmVe^`^BQA2;B=h>yDp zr4ORf#K#yOQhI?IGyKejL*U$Kcw~Wd2+u{}yb#abfx`yjz`2S4k3Hi_=`h9vf}8O# zr6c$)B8rt-6j6ffv%MxJC)7y$4MB7iRO9ww>+GD5S?QQzb_;&(el(avR=U+RzK9#w z>xD)w3aZ5@O}C+WnqJJE8@kTo4Zq{2_XPJ|vS?`45ZrDE#+hKi^<#6cuj!;|y#v3D zn7y{R2Ed1Z^b$1V;J>9y>&^h3GQ%eD%hER^!Ur@>8uEVM%WemdjdxrS80-{Q(`hsG zP98c~$dO4ng|?Jl%027-(8EW#8&`490H;2h?lQw(#-07RU=X8RYq}e+9cRvUGMhzS z=``;P=n_q%Kcbi8H>E1m+8Yad;^fFZ11-;JdIfqQ=f#*WA`dquaO&$}W8N2<9Q2Lb zYpL#H0B%hr6i^Lq_I4S^&4Z((rqqx4BCd}_G&3sWMleb)Qw+dvt}y579^TL6;)Q6k zrrEB)CSiCFVPW7mtJurpD=M1mrgbomc*a7+8D`jvep(>O&x3&z&LlW&z(5as4&1gv zgfz{VimB*0@6WcGSiKx3f9`~i$Xiko{qQA=isn3KI; zG-`r9W&wxZQizx|+Ar9bVO`ct;-}&tGowBh&5bGHmqEVw5GIZ4Sq?jzLmNO{=OvA1 zwdKK@=FyH|#MK2qtVcQV*^GE--XY#jgfx8Nh87I%CWft!GkFHTiv3Qg0(XBUI&tPM znw8!Aukq*}e%*jwQe2@ya3wmo;=Rne*Y3kovF^`MYFHY~;oNh+_OTmEuQIKhaQg3@ zsfvc<2UW+z6%@XmL9a&VdHXV`+Gu5iwi{K=@ju-Mq6_v)5zf&$!z@$0sYT*J%SJ%= zo96Y{-HPDHK6X_pbOKGUG3^`KdbluTu3_X>dBE^oligsca;(sUhIoj>p~I11Yg*SW zLX2LAX1h0u>y_*2JU^FmRvbhZGERQj2{JvzWCLbMMZ;XL=l6BhyiUQ_jS=5q#ttCS z;L1bYKTB_9GLAT>R-N}fTrqkRT8{VIL)}hFZ)UP3v0Sz>#}ZCAcD`=^bdhV6SEO zY2}W0qRqY{&mHsg^CQg#e1e1~ci<~v6vTRz-o;d?0JwN|wkrJ@8Q?FbcXRWqAPQ&b z5q{j!ZM{Vy_#TkVn$Q^H3|wuS>%Ba9h40b(_+bdK&kz zg(3O?NDxFh9ws<6eGrY)^dV-s*@TN9;ylw_KFd}h9zBZA^Y$8CmqJ|gH93zyj3zD; z#-6Rj{t>i}CpCN1;!;E%M33P)rH|paEVp1=X*2E=?Z6Jic6^>*iZhn2 z*#dK$3=?}}*Oj=e=6>9a{{(*Jm8gMOU_5SkVS!<8(G*v>a7_tUl5iQx9w=&vrcZ)& Ynm&bJyN_Mb)AVV)r1Tm5Cbf}&2i=xlX8-^I literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/Container.md.storage.doctree b/doc/_build/doctrees/services/object-store/Container.md.storage.doctree new file mode 100644 index 0000000000000000000000000000000000000000..9253d676a3a602f62774e8c43dc3ff749a0ac7a3 GIT binary patch literal 32544 zcmeHQ37i~N(GP)KlbajD(Llm(;>>OWgkuANkPO#`1(Gxjm@wNr)4M&Lnd#~3nQRtp zE(HM*P*CvTR0I|A#`{3TTSP@g#S8BnZ}I&8Rj=PnPtR^R6+VCLFPYcNfD0+oPy;Kf*s$S6!Qv6!6tM!z>-Qo-V$)Ue}w>hI~RqXm$%?|t>LVrqkZtkQ> zlgg!_ZWO#~-6~aW3jC=9xkZR3yWXIxVHv|7Rjhj5E*b*q4ab3}4dkW(H~?_XpFWVA zhnJ#Vw(E8a%AYZi+X?Yjpv1yN&zWt`Mlr^4_IKu(2Sk)S z%BVtYrDvLjcz`q4JTO*-5KfA4Ji!(^^UNKi0&LL={6)@uu&s+P6BXZq4RdjO9@q{*BfH6Oi=g8?p>`J?x_@e zI0}Z;LqXl|HK*0B5w-|_sW~lT(qCq#P>HED)TqPW4hsE)yUppvMvdVD|By}Qv_jbm z0z5C@WKJKl3Y&?o_0Ub`_H0;(ZSoIy_O{F^3jT;qexEblYM48q-q`+%{>dN~IUN5; zXR-r@9o3;QRD`mf@>fD#M~D6~-A-DZ$9i!dtDKCp#OZN*ou%d!f%K1cz~}02=a5a# z@=ea6o1DWoIY(@2ILOY4GFg)>lnwS)xV&tP@NpMk=pP^YCm=hAGda33WHpA8c;!;v zCaatuDtm>^DgQ+9cv9&1Gmj5%H)&EMu#HntDyz9vHm-wuf|^yZSEN$Cy~b+gy)2_p zE}^&$RZd3TgPW*S8r4#PUC?N$?igDvDtV2-Xi_kQqM^&UC$%sla+FcEM_X{+>sGDq zUw`VV)!DqcK5bY;)>wDSz+V@5BQBOMQi~TS6cLFN(*!H9?$|m;Y*%_VSkJX(y{bapT~e!#E0yh_D{Tmg#VcZl zH@nRY{IzU=C!<<#t(5zu!2JPpdW~$(&Gt`WgM?X%efp<%n>#S`{%N`GQNvM>{dIO^ zuk*FNu20%ssme7d9|0Hnh`2Pu zP8;#asWnNKV5Q^IaUt2}&@Uq+hiO8HahhIqlB^N3YEH_pfU|1od(7F9;A{h$P~Et4 zbJ%1t%_d>E4GdkXHijl>Ytg%gCpAj-K-#rFzt)n%oYBn0_gPysoN$JjEWc3Q{~cTyF6wT8|c<}LT}hA)Ws?t zYm1GcBsy1Wq=>O)z@3zqIXYs4myTzH3v{tu)(z$C^*HSMmqYTGh5i*R+v5Qv|4Sz9 z_@?}qLrqtP{#8tB2J&sHg6qrGoxFk?m*FeGCQku~-uzdBpKC(@T3r%XN=ca0qJEYC zD!8$1Zav`t0}A$Ejohyb{p(qT|4<%EiR_KptX>oPujR~Y5U5#K#r*_Eg`LxojV^z= zR5SvNy{aPtBI={IT{V`%s*1)z%MUdQ4*XvMqmQ5`b(}||76BS#F~^4G!2&VXH=>yV z7+lS!N{MGWV4eIrsg|wo7A8k-KK=IECpEsgZZVLUIp(uxWevLT7WJg#AUU?pT zqc?mDbbD*)zd@T(J zj->pzq8#oF{kO6D*e5jA$Ev@plVHZVoe91@@(H4A{~aj*cZUAEbYb49e8NW@KfM@o0L8cEl3|2*h?A@skmL36q5mb#LR*jifwp4tzw9iK9{pFGsnVnG z(q8VXsz?7|=zon_;5q%Zi8=kRBjfwy9r|yy(T+OwO^Odeb>orwCM5k<=zkm8I85?E z@#Hctc{udHqovR}$++aZZ8`bhbM_M{zVFNuDVAs{exRgyB=mpCQm`V|=_LrBG&b57 z9)5)EAC61(;?qgJS#SeDP-H|ZXt0mzuAFj3YnEv%vp9XUTmBzGjW*vTH~R=&=?+GVFX^q zHtel3HO8HXvNtL~HS{Lx!`=Hgb5mm;V^OfNx_>&B1E^za5S;K-p~f0$RE9A92aT#Z zGBr_%iM1wUea50<2PMqWpCVJxtGz~*IbM+pW5pojk6(3;KYkq_fBZ&|KbGr)|E-Ka z9u589J+I+$H4K0E`^dQcKj`7qAKQmhzu;!~PcZW5Ei`GN{-05={}TFtg?Y*7@KM!b z#YTsZh5p~z`0j?&X|GYL3m$mH>z_kbfM1UnMTbgNi^h0<9}^3uz^h_P-r(LB7C43) zrE)zZwW(aj+8w%Nf$cZAozuNb#zOaA4GIiijx-w5cUynjS}a6#1*wW%DO76Ri;dw% zRhF63JlBnGD5kE_!Nd?2PQ#E6A~NXl{~oJ|(%t*Vc>eHEUFrS_Q~7@^?Y~f@{|;#q z+%d|{Vc{QB-WsDa+78hnO;(9H8-=<7>kHC|V2EEXVE~14#ISwY()7}$OIuzKrCmok z)=L$;$5?OM#lgMaiAV5#MAP8w!A)j`m+?n~7z3nC-yDN}fA3G7Xl3yN^Sq@|Untr$q zjNXGrtp;Bb7$>biWuUbTkE*TaqJkdU17zCe?a8^mT7;)I19x%4&cYRVjQcr2nz)Pv(vN)Paz#B z?`hel!EX8Sw`ouplg+ryS=>6hg;I=_$U2%?FJ^@7lgVibFrcXyAP6Zf^wVyXJ+t33 zkBoryD3IJJ!ZR^Ag;JzOo_Ba0>{Ipt&$&RXH>^F)$GXz&G}tSz{U+js#UL)N~d9D zwM{M+0JR*TRbp}jn!E#vm4v9J$0 zR8o?{DiY{F(qTwPC722arVNS>M+}%g0zpW9f-Ze1m$QO}J4>U1KhFS*J<=6)FS68mkPpa0_DJq*&C7aV!Ho2!ovRyIjgz)-^@c7oSK~g7Hj*0Y6 zYd9UTP|z6&LdppVr6A5SuAoXxLH`whMrSJ4&JxyMs94MW*V$+gO`ridA}78JQ% zu90U;!o+2eM>@)YU4mfIAR?fC3qeRjg00lg8O7CqNLyv(rs#<|b}FbGijsq^a>zYZ zYE5NCt2{I;WJe;h%faLZPjyW(=EL^WwwbBs3Qv7h*vCkt=3%lX=pzRdKs?qtkv6C# zgp>MQ(Z5!=K`!HEb5A|v%^-jRC?g1|BH5+gh#M9TWq>9G6J1ml5Kn>R&V#56c?ybq zUut`B+afi04_Zmx@l9!}0qHXMkGG{Y+vEK5(>dS-26irjkY0q}Xs*R&MXMU#nr@*`z|K=(GA(-wab;Hv@fd)M z+d#!Z{^v+si>cYN|@zkvEw#Z)uFtGk&1R=ddh^E~rPiC}LH!c>C zOB6`%*r!rAx%qgR81D@7nN+;oKmof9eQi(E3HRnsE5%(4c=iMjWydTGjV00`gV`k~^2HZ?nN^Yt=9S zZ8mDyF|3=uyzG|dH66R)cx_88$!Yr)79a;e%57#!+4jlq8!G%@vI%jd+^-uFt~WwVZ4{a?6b`~ z-3>NiSMNg*();nNJq@ck;c0jQls+JkA8bLkyO|G3LacVshY{oKBW~s+{5V70Oi7pN zM+Jf1%*XH)(#PdpZP;9sIAKNiFoBHAJj6ZCCj=*!Y2;~|ncT|=YV&3X%}*i?8n_QZ zNS_i)X*bGHmwf^md;x%9{fh`fx?jkq-6&UPw&fJQBp?qcklZe9P9d`rCz~*rrmAa9w^Cid zEI7&P`W1<7RoAcLU02tdmg7N0N_G7j9zy!M;H0;aJ1WvQ1V~k+hw!W`(l_zkUXfTN zu1MeFFgr0-`ZfTdrH2uO^d0EtoDwmt9%%y5jD)QdQrctD#y-Nbj22;X(;RZgsf`3I1`+0f!g=v1Bczxp--1kM8PJ{DxLH$4gvbpft zmX;m?Cn&2QA_(b6!dCj(XA3%Ivxuf2e=N-XBw~(FOLi6#58!?(5J|NwQf#hPC<27raVt+Z;fZ+X?N>u_bQK*r+D!JubGw4|NNf7#~sJ zu$k{-M#w(lY?c5VdU^o@tcMD5tcNmV?#j~yfhvB)s2C$mB1{}^|_P@a& z9f7Q11APcWT7h3(S6Ef?x*|QwBL(az1(v%5b`bATVsfI@sgzANI3$5%G}s@H9wm1J z^^62J?+SnqMH`uvhpQ#1ROIfYy%kf`{D4y!4L(qTZt7^M#xbxa^j^#MFedOD-0Sp= zC030WqnAX`Sd8Yrrapbq%#iz%vFap)mz5$$XLY(%JrT^N zHgv!46d`eHMB-FrzOY#f;`@p-E0t6mym4Krc9fAfJ;B4q!n)I3Z~^(auRKk%OZv)n z65H~X>+!CAYlB8y9)rK7$8%vcc6}wo%E=BCE!Q?Mb_`;vT>qXuW-Za_jv5m!q!TaSoKu{1`vZm(KhRT6gCN0(a)m#S_*_*+lk*uNI+CBHc>&U(feR6YbdgX>yHQ5WlCnCU@O!aHFUgWAHBN!My_LN z5tjXq7W*M=0$dOG!=1Hewh_gYBF0d|jj~J<%F?Rp*kQu2fIE~D8#9CnW|JoldG9vw z0Z;KkO{<8mrsNo49c-)O4^EBuf#IbNCsy{XA`&sul1m5pR5+Y*c{me^lM1YAsg4r? zj$PQyI}Kx{CLP3!MUkDQc9S-ZO8|gUTbxw%^Ln0G&-79#4d#3)f{-o~6{nxK)hBDK zT-93@|8i00%OYj&gXU&~>Ub!(B$X{Th2KY40CXISf4NZTwD{Y!#b3#I+1wMz;VM9) zn6E|<(kp~++KsYhc3W-bD+S~l1(Nf@Ja=eXS9h((ja*)J<7#wK(8Z|-8`qc4;cL7S zEvlSwJB=^$>t2Q2-M4?e**N+}>VuQE+>G1BcmX zhhw@CnZt5!LJ-o;_|+{wt25r>%ap_|0(Pqclclp&ysjl%lhoD;^p`7r^aenTWASek z#7>L9OBd#CjHlM*#wYwH0D#@w5rlMykW0H!Zp>WE(BCW|Z&4t*)iCt&wk5>u7&bf} zM9h%KHQl#L4oTC!Q({}D`!>95(|wPY_AW$<>AoEgA-zLz($5&LFvfQZNo9=h!m~EU zcjLL;7+GCxjPKzv`%Ljb?*%!O*WCz0dLMqZd9lcG^U@v4_Y33)T9EA>$`49H%!24c zh;jCn4&{gWF(nH3J ziosEA--+O3c?M}5)|L}6d?N==c4FJMD!e zUC7Uh(mD(2{n{8l$9UQ2mCD`p4yAq`%7KY~0YOM#6ji3(NYiYTE!TR#fP6`T$cegm z@4&8FLuDJ5h!u#Dj(4N{}jM5o+KnN#2>X#+9ojrmqj?zuL~vRNWXz+?MNTObGsvD<*+0DCWq?0;ZxTReG61jDBng9(!==G{*wh9 z&wqYLAivv!Z1C*}A`Zr-C+ zDv$LTXYd6{?AfsH&C4t5C~IpVF6%Ir5?_jz!VMJ#KETJbiB-5r46TRx-U}RXZxrCe z)$O+!V+#vvvQo2$mGoajo_4K@zx$1d*{Rk0bm9G$@v{FjYIqdlzzlweAf(@m@M$;F z4HtaNL;XQO{-{85OJN4_I;dtV)^8VadiX$WQxoi?xP)mJdU|f5t^Om{za|jsf?$DeA0PN11^M|jv%BNl2h7^7-5mb zwvzWV)wwN8Q3~mBkTV5$mcr%Z-Z2ZpmE8+ZTn_qoXn-H)kgZ{7hD(xXu$o;+(Jn{@{aq1Y zTMC29#UP-+ySyamPY2>?saSXVcjr8qE#!dAlIJtedO>Wn1ku!wcesOa9Z=0Mk9=W7);rOj$iVgrNsCJ=bvM8R( zf%1|xh^9KJ3+YLsgv*Yo8wnt}7(qx&gi55<7syK|NzR?EAkFDXnwXrXu@q&H2C6*> zLh5BeZ39c?rIRW%z*LuU$OD;!@PL!t0AvOvDhuHeo@TE!3u2T7@eI#W4wp>jU`O9y*z!GA`4P zOEn-=N@?WTw0%GjBUOMyVtQfz-A-jSq9zya2Je`|~?ihEl)#jss zY7CZfIfHm%_yV4!zM}stOqv)t+d(QoG)kn70LNE_K-!JyGZTpt(Xwq3_)!I~Y;3E% zBy-QWTDCEyi)`lz?zsxLgP|8OSjOcH;ktXKu1A1{3PDf15p`xD zAs76Gt6YVy!><+0*C|XT;p^ok$wkK1b@&EO*LHrR;NKL-zj*?D&WbDAEgW(kz7-E4 zy+Ob`>+l;HEaP$paUIzQG-iCCM?JmK6yTa|L!|!0QjLR9sWqYT9C#grWy^Dc6WqUWG zA=`Tpg!En^kai>b%)|t;-7WC%Q}9Z*_sdH%_m*rQK)T5GLBaiy!j(F_rZg zFUE5n{xCylT+Syh;ztB4nMow#M;Tbv;c+N_45^Ux;|M~!N9d&8C>v&J0!cq1@b@Zs zCFv*SCCNlf()*Aul732XKdo@(c!Km5)l3WTc^xHOY{&NGuofmlUSb!vpe?WUN)wzs%{n zHhe|!zZ%DXZ~}bJimT(-IOLlCbv)oeoq%`N^oJNM<8lUZwS7~-liaQ7|2mWY76W(I zhHoPpT6!1(4%i8Sv>VZ9CK6iGvVB+Jzo+1pZoV%s$=oxpmhA^f7ug;W+#f1jS*?+t zNYR5`@I~^nADVu|;5cx_f!Y)=LzEv2S~8bN#-A{-S^yf4WBa zOidu`F9iOV3SPaJH1s#bW0nlR&FuamFP+(G3;8F~#X|lixc^qTa`{HA zrmL@x;7b@)Lneo55)QAyQaHo7rEG`j@$MIu+Ax`M)WsXyBE3DpU@?Z4uJl$Wt6B=v{I2ZhmpM=^;2% zF0aK1-N+9t!E;D2U}Qa?l$MuHMmSTTqYvDy{O&~cJtJg$;$(X#Agjx6DU)YQT_(5( zDO_n0)J%-(HmEbyMLL+laT6d1u@>PFK}#|pmEm#*RxQGKBo9S0%JMJ-AssG+(r%Ow zGnFXIR{h2kCJuOVfQ}G2j+k1P*f5eiG zL}W-u;n$pw2>H$jt(3^4IdWbwRvGfjd5sm&F^Jwot2hxGcw^sO6?(@?>S|8iIf0rN zfHeY;Wq=ulQeD2^8qjePdpv%5Lf@RFKljQX0HhNTjSsvwWodpJmQED3lkm&j^YyVf z-PAAO(@RD56c{>qFTpUsnqFmuqs6xknWL*U!3z`SaV0M&pdxl6jE*u z=nSNQ&z-O`=}{41#8sDn7O+N1d{BD8oJGS0AmHn`_~rzii8ymX^f8D!4*LZ?CA&V{ zfHn&4Ieg)E4d2hgxd`n2qfHV! zyY6w0n#HpbDebK}yKGfQ8rZ%8jG|3>B$$)!Dh=|(4F0Y#_7GNTWC@vBs&MjU9EB*; z5aRGv)L9Ub51@$&P(c7wj#WlqMZk&-HpQGBF^13n;1jH5Gn{kmfVr1^b1JW|`Od3X ze1!%(u8D>LX3k^I<$FQMpTGP`BOE=YXb&|;$YDz6JR8@dvZ;*W_Hf7MYk&}cnYlL( zzSSD_iRimbh(FU(D%5E+zs%;($znfEek<2EDkBc1Ik(uT)bdT!4XA>6)Ixj_wp1PV zBH*e5o`YquDl7`z;LC0m@&tAlE-zj(=2Fk2YXwnY=QSkagPV3OvuNodMe_M|KKiiu zMlxzuBZ#y}OnQnr6^qlur4b5*q!f0cp5KD6Z($Xj>JqyPz5r@%&f68MRL)yPeBDgs8MG`wVQDPJ#+MLp;4`&Xi z^Y}Rxk>c8OzM@pB#tWG)V6Yj2k_n)$3;BI+zNogY@Ym&7i5Cg(40srP?-bWC(u*04 ztC}^Gm-R4u31a%En{(QYlrCn#DdvXbz|tlBJT3a7HN6yXPOmu^UWTu)=Zc80=+@~{ z#9wO87_s39EiS9e5HVoR5xZ(>@p6fs72s~cy82R7KrchwW#%k4pQh8e0&(WPk<%C* z9qp-&;q+2foJP^!(hK0AmtM|LaQ(R1aBJQvjW}ieuhW$ry+awFKccJnaj$mmC92@n z0CA;i;M8{A%eU0^3Pzq>D$*|}sOu2L4TGq%>CCTZ+KnM| zmt^yWYDIN=4I@pGFF(_3@r+B(@Z0U|hfl8TrLJDzUtMdqH@?}tkGgm0Ky~E7z?UNz z;A1oUsy&8_)FSahb?fB;a&ESP&v3`Ti>?~v*Gon>;1{`v}LxQ`wPW)k)a0cC>;Lc$RjHj$)ZL6+H`ncnT5%uLU#?#X7+ zmP1jZh~kBc;)Q^UH;Rh4qN3sjc%gXVec$)z_pf^0Gd(>UKIG#Q@H4;cboYBz^{VPs z)w|!tvx_CK*a+N;-(T}ewx8k8s$B~*YOht7sM(>KlQVm2R@Dxs>$b1<4pmofbiu4y zv&dErmu!ah)!Y$Db87*4rA9Guy_%XgGTH?=-wqme)jcx06dzulvwXweRj*iX%`T~) zkP`_?2FUo@r`n~RDKA~2lyy#^I+wPeK1l+;1w9G&ksDZ2hNP^vAGoV?wnuXLgqwb%yg*Zp@rltXRH}_f=zNcfl&|Ago5~*8wIOTaJbUfhiU_o%~>tpoTHej6Wf?lPjL3L zz?nM9nP)M3T`_wn=N#6?#!ziyZLqg*PI~q!AiQ1+?TECewh@k`ZIV098S12QI{4ol zswXo4OmCy`pVkX3)k0vXI~g?$j?M_xnat6wy=KkYs9m`--D?DnO*UeR>j%D3^9&zx zq+%Nx0frpB$PbX?K6NQ&a+mkZfqG8ucbq{{*_Zufw0>^7iI!s-Ynwa3A zWEtb*8@FsdcYHk4B0k}f;nqs-PPf#sD#nCcv3=&t^^KBeGgE;v<9mQ96sMCi}gX&4B{j)-~jVtrvWd&+HqftoP9W)HPsbti4Se+TFvzgIu=q#>#R`WTX zY6dY5GNaJ&-mGERi8>duo)@YogM_%G2}?(`m=~3y&WGC$jXn+F53fO~F)(^TsPZi9 z!_SVc7#qrA&mRw!6&uUBUc*?uddjU-R9WDTj+?3-M9RprGLAJdr;d72S zTA|Yd28I{Jj#gwxV>4ieRSDX)Lp8xt#EFn`ywmu6bfRjL88r#Holv=q!L}F6%(hqV zEE%-e_I5;tvVK$rg;qmV0|~J`N88?9U4-()^lG5}uhpZfgD({-V$uHf#cQ?0wAi}1 z#q>iJ#43tfbKPP#+SIIeI?JWSoO0$$i@7AO>Rr0UoDS86EE2bvXC?c`PeGA>vc-IA z8{w$MY?2F6haEJY2Ay3Ls*6#8!}O+SY%vRsO2rQH6|{vJbqQp`l zGuWOk%b9a^qI%{iTGv^9+-*VUMfYNK|7BS$?z1}Mr61%GGpiYOIm+A}s%Ntv?gAk; z{%XSy&_1F&U8zWWIsq}=KEuyREup+ORp<+j3y**cA z*_EMs9&6{$e^2eG=R-ZaBV(5^dDRR$8D7T0>IE?Le^)iU5dQk2P`#M_l~r=3_$xZ; z_}}S2(%;AzDqe9%M!f_+bakj+%2skXtOTPk^joWFmu%{l|qc>Z*-q&d8&5RqiLh!R^31X!gZ0wOB8tH3>M#v0&kKia8sz>%-mh@f6kqH z3k>AOq(i=S2KI&zW`}$mYWH8j(c57h?+Dd9p#X=uyD8xt4qN}tp?Vi<$AEU+nw!T= zN^YgrIodYpd}Xh(^0+m}oVjw<2`%$?#NOYXbFOjPP5YLdbM5FH(1Ita_rx}xjqT;V zX@7KUJcE!??*l8hhU)zfm=(C&2O{OF51L&b4^7pFti&`x*14G#{b4Bjfe5EQ0#$u9 zR38Hi;tsb$RTw_Z)hw!yx18p-P<eDRk4Nw+0fk_9z z@YpH_=tx&>V+V%2>@++lgE8s=*#X=E{Ytc?lhe`{=huu>7KYPNV~;3?;qa7;4!Xrd z@tt-J4Q^mepapJV5HB5oGItl)>7vFuW0gwqK#K?8HIJ$oVe_nC5%WeiiSyl-biU8b zVBJmU`>Z(M9ijRhOL_gHMoRU0nECBVC;q|=Vh$g{w)I8W)}u(WFTuj^4Aotz5Qn9_ zBOMIx4%L^rwk|j<`MDZA=k4Un!?i86C*273*Q$ePX_H;C8c2G{l)#||F9YE@^n`ATQB`D!xSd@YVP zN5_u#b%(>sJ)!!>BS$F2t#8J0>)tqSeXBig-OaZ3ZP?Z$q8`+DU^d?k)%T!835@qh zV05B@$dd;5h3fmP;wxY`+j%Z7uqze(Vx~>o29E`F;A+(QeSCb);P|)^xE!Q-s&2x< zOkN2vdhC#KterN#{6-}}vMXH)806{Ow{J!4LkPo&g_lv*ffOufe7sj))`BRgjgP~I z#>aUYA&$?|KmNf?p4=Oo%MW=Pf!9bLkO=BWP|J@)^%EBS0b_xq`!E*f*{+|4>Sv5Q z7bR+aa3AvLoh$-{4dNHzZZ=@o460wE=wF5E*Kv*fRM&`};5r2`4~b^gZ&1u{L-jl6 z@*$&|(7^9Q^#`WSOHENVvI_pVM-}`DD&WoqYFzyp%>5-)e~oqU`=kz7PrZiv8({t( zs{i6bbW)=9e}u%-+erpsK45_MlH~NVg02Rdjm#z+YiSNXFS~^H#v`OILDXLIk0d`l z55+@7IV}i0GS9tD@bgb`I|4vd*E*YG9&(gKnw8+{ubd8(qcgr z{>ofJl0|&Z>IdW!fn2H~Wrl*?2u9nQ#-U{bvyX<6n2?;zp?xJI!5=M0inBjY>Ck@s z+AXs>gOTUa{(^v|5IO*FAsr~+hAeAt<7G`ch_SL|E;wqMc|C*<7QjSF(IBcx`4C1J zN)S2}36SzIJVH8L(2a6LoePIb^h_uj0+Q7rqidm_c&5TC%9M$oW8pc=gYdtvp%_j+d zW`$QNZH!l^nS~ZZeH+k^Xy)Zs0y5uR(3Qe#_Ea;Uaa+u!aRJ$mo8-K2gI+PVo5XT$ zZ1ox|k|l9>C25^JKBCp4)YK4(j+WHsD2aOU9d}*U$JO75MDD#%KVCu_5FFz_)e{@m zKVLnlk3rF*I32CQd({1+WANVA11qyrpv|SV{2cm^+8?yq=~!qGf#^6qLgMHWwbx}f zY~B8}I+FA_0XtrU$*lI~wv{CAJI3?G@ys3BV!@yxP)Oc zP09|QsCx?7&Jc{c)5qfx(t2UccqA|ey|Mx6{xxk7_D+o0TLiteS0+d3CrEBu?V1ex|4GP)|7U>Q zvI9C9sgQpo9wBWK61q>tMJD@HhXL>YZH7+Kv`!UTr)gTF|1(~@sHY3|<_LQ~5p^b8 zWRagJPz>_qA(LDe+ zh2-T!?yH<7Slc436H&xJ(L3SZ1v)9MdXyQ>jz)XCP)|47rlhtS?X&S6H`=GfK6(xk zrP1bYb4ce3jxqXB@QI2$Psr)k>&bYJTd(u+-rjn#n%H{B_&M}Ylus9c6zV;XM@ZxN zjl(#L+7ZSr0V`;*)~tX`aoRP%Isb@BOkN3WZXrP6B|}zBNR|xb8{I4eb%zZ+(SAie zRf?rW$F5*%6kB*CC}TH~O*Kqeo`rcTS%SgTCW1dU1I4=wGQ7K>pNe2?h8SEDe$$>Y zFnnrUzc%9yJ@Vw40AHw_NjySwL<*xE3CUG~`e`n~lPgeV4b?ez+aVbVB~S$^&OtJE ztMco78M_U~N~s9~kKH`Hg;bYsJ*eNJccGGEylk0^PLAG4Koey}qqin)pAm*+ejp|l zAPKTI@Ca$A5HQLSeP%#p<*8?y5|CXQWb{}Bi*uuu<~nvda-)q!XPJBC>GilVmzi*G z<8`yWj;2ANop))lFBH0+yq_8Co+2#QwV?z1 zxWNoYeE*Aq6UI>yvoeHAn1~FaE)l8H(fCqHZAIg!<2#PVqj3d11BnuiFT+bn&lDWv zQLP`e0?!gL#O~;FyvMD;ZoId*0&EuC3Ot*iLyu;;={XPqzHkK|Aw3traqGiX+t&JE zlM!7hV9!f{C9I0~=hE{fFX6m&6;hl7B*?vhUwb6Tofq5E3k89L+>7uQ(u?KWkgfJn zWNK}-rf#7s#%NH4Dj zvi*);fqd}(O2NHW<8tUvu!v373`?&ZpC(G)REBOr z21vdKkC5IgR3bgRPd++Ha_L-ew{m`(CRY3id%E6_JW%}r9wB{@0pnWxkbHDfWd@k) zhxy4(`bY2*(nkecZ|)qy`33qI=V!}IIk9d_9~ZbZ*@59Prg<9!>s_Nl;}iIXdwmj* zkUk}tMmZwP#fp3Jet<3!hi;d&awOEJ1?@8$O`G~>DzdO^c_Jq$`NfYJSCHj3Y-HQr|;@Ku21nJ`ko}DO*bfbC7AAg$b|WQ zACHiJfZwL+{!l*B{I-1TN1R`d9O}n{|C1#CPxpY&MX{OwjPqkF`#C;B`h}oG4*5&@ z=q#U$V*Fq6lU?W6cnRq@0&d9YetcZ|gUt>fzJJTm*)o@ttd!pgR=SXZ;XIe{dj`gO zCj9{~A^lNcjk1tJME=u^xY;uE!{~qJ=g=rm&$P2ce?bna_OEz^^f&y*jXoEIdUaMf zSn$Ni25%{o{k!1&R}%+({6jv{GGxoKqGn-zP!zQnUP79UUz9gG4776`>~7~FK|JGF zz-(d-`_{1e)!`XIKJbFAuh_ApmxOG6E(JJNpij%hQ-oNq;LSMt++Dx1InZZMcvcY5 zXgzTZCf=^YC*?4jjb|0l!1mYics8*&8qyX~pq`Q(AKz+$E6h}uHuDxoEYn~CTu*DF z-!2K7TL_|i+{`#PsP4qH7LPam%62_ok!OJ;vA_s+`u5xXd}PL`TD57ta}R?HhSB1b z44eSME@05W&ynI2UY_ZXtXSZ5c2x*a!;1HB8bCFGN1m1zbiw4h~R!WOCnI%GIsU~y(DD46+6U2QY#N{Y! zM#E=0_Z6T$%efr+;FnL!cDN4hhfIjOKOP|+Ae6MY%s^7ytAPH9b2B0_eWSVe-*gt#Kef9QhK+4Z+Q7T-jxV&1QpxgLDK#WXnuD z8HkP)$TZ<75UpU~p(K@~kOB*N3?5iv7hJ5cGg!9FMMIpVMCf^xb*Cfl4deuKmB!Sv zt(K283)ynq8#tQt(9wD88-?%@(B{f~LP3f_!9xpiSn>b)(gM6fg$d+SOorru<)e{8wB#k?|2Cdgg z6KGGRNh?`_W#}M@#Y1Tfr5|9Itk;`Pdg@#u%33xcC?nV?Ts5`%P-o* z{R0Er-nl77TU?b>^XM*MU6V>&wue?YT1{*gGGY1POA5xq<;*2$F5@FPfrrf$*f%Y6 z?O=_M4mHp5L_tlq6@x)Jl8bViJpkUv@7TOav?ajIV7K&>s?!#$dix&dCtNkq-}y~lq1Sqa7v!e zV7px~O^vA~J6k@|9JGS%Ih-GBhPMx5MK6he-X8F|C=O*$<|hZ+^YIeWn1CCj`%0fB zJ(-SZdjZ2|%Un`YQF*~jvpFzaj;k9!~rK|`AAEVEyv2*fqYR`MR2Pccl2Ixwxw3Y zodXt*2lHuv8N(Fxd1KsXfdl+FkDM4{U^}_7)og`S46O8SJU-VjiMWar4?MKtRmFJd zLjA@zEQnNa+>0Y{0ZWQ;7)_9B4{m)DG-cox9c*gFNTzR9y#@j*qGTJ1bm;A;8cUun zv(A#$?uiD{3X7_}&cH+eAGD(&DXft22+0?n80AQ1Y$_?okKHv8_=bkp?!Hq#(h6-^ z;1u%30(S}Sw8oX~##kbw3pqPmX2Qv8dWwLhsYccGR0h^NmYGC|Pss8#JVLri5RGy~ zmkUkF(h6ccYt(B&Rp7de~-}HExFI;+#Ei09?TUJw+G0_%X5I({m;DO8oL_i@6}aI*1R3(esdu%YvG+H18Rr=L_0Z_+{?v z9-;F?FA(r*INEa4s$VNxfLyNS?G z^6905_A<`w#xXhG-|N%M@tK(MMkvBIgjy+cwpGC?=nP$he2-p%-;iF(u;#K@pLssg z1u1vM8yE;Y zER6KKb}-TJ(d2*=R4W7Y8iweB-ikICL$Ag6<+!ZLuCt`L#syK;r`HMXML137*0C41 zDwj;rb&|R;@VLa7#n&TIrrFGe6{|Mcz(fx)&>OuT8Rl%eMmO+F5BkJ7{cTn2bfb{L z{Y_js`3gdHh2DTP+!nL|BH}VBOjp4K=#2u1Gar>0>`e^TWiE^u(^nJGO$_HecEmgc zRqat8GI)H{G_OA$oHE4<7QGo@<`U-I2cm=WE6@VHg_FBVcA+szZ)Hm65*sH{*;J;n zf!VQfu#4*pok*DRqK$nWCbI=vHVsLjQtMzx-A znyybbBOP9do07O&9|6Bh123Agh|j!(8w`A^^lpJ&&h^F39@lyvr)41u?EDsF;&icH z?>lPn*b=>mKbPWK9^AJjXJaB|zn4jOnRC(3P2jqy_X$a5P>hJlv-ka%{g{$Cw)+eX~FBh-6%oI zYK1-|$xCo07Yf%(5(Z`W&b~S>DzT@dOe$Pat*n2*qDY>;|CDSJvtVdA#e5mVF{Jl6|(s!8fPH|S^ z?Sk8b2!q4j6YehhG=s6LS%ti;GSX*|GTd!0YBy5)ECY6#+fM~cckuhX=yo*v9KM_* z&Bcf^0q&<_N5l#8fIg4(%gvri8-b7~bm-#UA46MMM+)YS8<10UNvx^nonOs z+HP|Jn@=-n+=(>v@F-|ZO-=RJr?E4&CP4#-+Xn#b571o#-CcRdopdVr3+Qf6 z-n)YPQs~S4dPuwWQZD!vfRx2G7IEm!x777jMxO1K=xhA42)>Z4_OByxw|TI3d%L19 zy@Of&P88LLIEPQ)K;jsElS%F;?t*?EhS3NvaAIE9F44Wnyxg3R8`_#l z-$D|%4x-OyME^EYM;ZlldAbdQZ&5(sVWe4dEgF3n?;(8;zd7eXTu-(f$IbS0);s&_ zU4;ke_4R{f<5R<2#%F0Et-e7|H@Ei*h414R%Nc__ZTSPCh8~5iHplnI;odXcafaJL h%$~UU@acyD9it!N*PP47Sn}z|_z3AI_-z#W{~M1 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/Object.md.cdn.doctree b/doc/_build/doctrees/services/object-store/Object.md.cdn.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6f9a4cea6e42d82915e66e7a68cc5e1644f67dcb GIT binary patch literal 5068 zcmdT|XLMXg6_#bI?AnqgCpJ-Rlb2$=PAqK#Ap|=(4i16Durb(U6kvIteQ$Q>*{|I< z^HhKrVo1QE_uhMNp_kBm3BC8;JOBCS?R(Nnb~uM04u^B3bF{0Ox!?Wncki7$H}yMi zjcKqkJ%JB# znskKaxZ$J-dCM2B6}65EM=Kg?q- zBUHT$ZBpqd5%ueHCZ_^-3h1X2rD-!8(!685r7dizKu23ym1~`NtyF`{yJmDwjSahL zY}P5Vnrz7Nxl;K1+$I}tapzcKB5_`mtu~~c-=quVIA;Uh_1l_sH#x{tw#N5DLEHED zb#`q;Je>)obHj)r7$Ork&Dt6Pg^VZAw;~B z@K{#qp1^lmM)xxKZby)Ah*E3T^L;B6kgkp_FNCZ@?QtzEEuriv)Eqn{g|00gx)yg3 zD`k0FS^KVTRNHBo%Y^%$p0~KNj+A@}w<<*^hUzjS$+6KwXj1eGKoX&kWqZ?Bwu6?1(|=K%RRL{wp)uYhcX5+y7OdOhB%x zu{9>+rq=_=l3*+&7&ZvdY2wj+r(5{%=~s_QKO|u{8%-X9Nt5XNAdBVrazWG8i0Az> zx&}nDJ}kZxiHGE_EOJfvPg~f#3h1Y>_5m4PYe>&Ocf%yo7Jj6utk-3RN)G=ce%8QBK2u{M3m%87y|67JimNzSn< z9f3zYqn7b#93EXzw5vUNkFD+4y#$+$EZa+t38#zpIT!f5^0G&Q4abQvtxr(9SAf(( zYB;!*kepr(GpLW`i6SmzrPD`OmU%BU4XaAWfYQ$>xMP%n5*CC~#0DZ1`h;TM(}pG_ zH}WSCdrC(s0e>nZ4I_C1_}v5;?1G?lNdhw&%^E1T!V}YO93EQSSCNG~!3?T8I*Jv= z=94%>1xz2mZ}{)ty$?gGVfMb9k{mJ z&Xh21%5<-`mjw9*$|`PKrpraBhGs*x8$e552GAxe*W&f|PJ{m!NOhy^PC5_uN-6$H_-&n?EzmvZRqtGJ4r77ygO?rue z6-NomHodgLhCR0!lU{}~X`~m6qL(-66>Ow5)&=m&j9$gYywLR$j6OCdtd77e_v$9S zh7Dt=(q7n6^xB^5KiXHQQEjS+xu0T;q}N6C`i$OCV;f2;Y%_8I_%TIq1dKCqG|~j) zAZ8#J7c&o&$(tH%UD9^26mwyk`1EGB*<5>YXVy`KA=MMQJryOLy3~PRr?;>Q7;uC+ zn%;T>6Qzia3*DQZqPI2JsKQJX+opbELR0j1#8F4aM2Y4dYykgB=INbiS3K;b7)G%k zjptpgA2Z>ttm0x2z(f$l^lpPN=hpVcjPU6_n2grJ3kzqLtkhVe#J_pF8m05`l6w4E{-5F53)_ra}|9F>0~&;V?{ok79M|j zfvxRM^tKT%eFZR$M0-?rJ4~z~p1#TkQr(`soW91Y#df3TuVc3t_J)YQ!7BO6N#%9u zn{2)9ddj?^K!Rx79r{*-Rgl2AB(=Rc`ZnrPMe~+D>$zIeclHmn(d9a#@3H}Q=o*;* z9vdoOQNAxXv-L<~jp5nk0wzvPKRCfE9f7Hq8?t{0Xt@BEDESe@t^&NT*p%X}MbVEJ z*g6yH#r*sUJEzFc+1c5tcwWmW%=6O~Gj*&~b^56(iWr~gZ0U8Rk6%qc!?QvZcIfA9 z%W^hWB*8D_ZEV!kD|?B7U$TDBrC$MTgn1k94Mm`Sy}&ki(@gjx&vkI5rCq*y{03q! zIL)Q1=(kO_DHkoqeUapkQ^oTY{q6)?izgl=QvRGpzc+<5XJ@he{h^Vz*v6Fwy!#yW z$NhcXSCv0y^yiw~fJtLqZb~7r-j}d$-}p;Lf0bKM-_b4qmUqj13M=1{@LYnY5e#Fj YQdUw$e{a%1^6AjL-25}6f8jOZ-!9WGL;wH) literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/Object.md.storage.doctree b/doc/_build/doctrees/services/object-store/Object.md.storage.doctree new file mode 100644 index 0000000000000000000000000000000000000000..d45ce3c71aa6dd15bce442592cc95852987e1c1a GIT binary patch literal 42440 zcmeHw2b>$l^}jK=$rXd?WdXyT%@x~7lmkrc!`Q~??0^LVZ0>Z@-mPSn+gZuyBWwam zNFb0NNKZ&5r1xG)NJ4re3F*C&N_yr0{l3{*?MgZm2;|3~zz5%qX5M@A=FOWo?al0! zGxLR7zELlhy}oL#V0#Jvtk~6h!rf=ZRqjmR-M2e4ubQja^{ENlbNBPzS>4$KX3Ur| zZr82aXvxmkEo9~jxq8l>J(N8ZS=CzIwrW+AJ)vq7wyGC_K4&Pq7y$O3y2=4Dr}9p5 zx9!dy${vX1e63n2){C`ju52mWZP;GjbLS0Zmm#lSvlF?(xOlNr$#%bA;59Oz1~(UoJId6@$vn$jC; z%CSx&vkZ-;^`uwyq*v?7anAhAkh%zWxbP${?DS&}(aw{dT-+G{k!WUp}c&n(pKXp&YsOEU)r z7_DME(K#TqMC$~p1v=5=9GIC;jpz+DqSslLIh^_sKzO~yYHzN$sSkaP%v^N9ash4Z z_V098b+=jr58WFrop4vf7_9N#lVA)~ZZc4ZWM)QYHuMG>;-;LLnb|@5$=QpbiTxSR zR$hN8S1H>4XjPgDuYbas;J;p&glfKAYZUrnaryzdyO_7V{-CN}y{7E`t?QA$G&It-RueC+Cqdz3vbcFEG)mOP&eRms*b6Cj6#%QiF+KPu} z%8jesgp+U|4gSve-3!Ry!TZdZF<7hS?TLB=wr;8?YA}rH@Ir&qohy4aYge^43CrSX zW4NNv+FVW4RKZpjtn_3}?Xo6|b;ls}SW~qIQp#nkYQus8rdWlgsMd>O)J!S1O6b-H zS+q0hj~JQTg@j`(+d9Lk*C)Jz{{C?k&=~E@*DC!ONp^V?xjd9CrdOlo+Gu|z=hbaR z9rOAd(6aGHv0xPI0#P#lg=+Of3ffFU-`0){5BEjdYhEZlcRSlG)2Yq&Gi`QZx0BAk z06pNLD_Hj;C{5OPFQyJWv{^+noZqg_=)BMe=xxHi1nPXL@9tn#u7K44FO`PMv9mj~ zAFYVYbealmQGqkF!FWRz-OI9LVD-PF%`_`8F51e_zCfZhV8c*T&c>QNN+)wipc^^g z9fd$MGXQ)k3<`|ayin$DzA*}1n2s5JfGzm0P2tOL-CTBK6#qLMHO72*+)$dS?VocR z<>bUVky~`E9IT&Pa^~b{0u!b??do=DQOmwtVJVzo4oy43tfKs}pcFB$wOA>Ec@1e# zpc;E8=%RwkcRdvCka$_Ft3w-F_uU3LdI~t&RX&PI90*;rw!-Im6e89U%AqP`Lc2oL{efn zF+rhevoL2t$Vs?UD|iDD0f?>I-AMoojBqDut1fv^E8y;d2&a7aa?0w#Eedt7x7!M@ zu4>isg!>q@*%iL)laINmvXLy6@v-ev0xkBE$3Z~*0h+DgUWuw)<-3mu3F#VF=&mta z7vVlZ`b9UO|J~Mdp9qRq`|gv-)O|tJRYfOj9q8-1QBFdhjLKf)yVn{{gHC~|2lL&h zu+rDHS9);BXvXzzgvV8_F_Cbe3d&FO-KT5HAoGmw77LTvFJCKrTV~SITtgW?vu&zG zfC)RFaGwQg&-UHtgw$?mqc#`7lf^>a*>VsmJ`EIMj^+d;+#4ZuQhBcLJ};#5{Jl^) zq)EjAa$7weJ6A}!F969G`tFNDk}qzTmS)C%Nh=9en@qSb1%aD<_hli0mv>DBO8=;?X^4&Lss6#rpi2dVuFiUuJ-vaTI&A0mQ+d?+q-d;e;5jNi; zY`)WX-$gcOqo}kPF8AFa{?;gy?`fwIF!^3E8NqxXSbV?let;~J$~z5<4tTS{>Aq+W z_k+5t&VuF572FSXX!p@t0V4k}>hclay)~@MN89-b+WlkQ4jcJ4-~D(9{fSP{AE9VJ z*)4Lq-FH6~LVvmw^lgOxjL7M;zWX@<%O=1!S!gw9%kJk<*W05K`$9Xl!7WQk?Te5Y zHxk-td zxIaXNf8@JA)>TS5-_>d{Cn&)ENmKw(9tV0Ot^8Bp{aGl7pLZ%$TKN~cmGAQ1Uxv`X z>ID5$viNI}+HZXKw*Z!5W{B(B-$7D$MJ082JB7h52a>|?QArL@g7*hh?vK9vClGT; z<2TXDNNfJtcmERd_1AW)DM$GFoAC8_-~9*q;!c;eCJRAB{u8AC9A)oc?SumM?s3py zY6hl;zS;+W$=N^AY-*=si)S0Egk-{~sriDrAd0!LGiHf}5P1}hg z>tSFp)stnj+MjT#5=)4PQY{tuR>lUm97Ng&5Lhd9jMjljhj5l5@YO*=Az2CvARDb} zpt$V!)gc-ZGZ{$aQ2EhXa4M|VVaS(y9WJ;>Xx!`~qZ{n$7!gKB)RBZtl~_i!W=9D| zYXL#cjwUcw#X`ha#|Uh)B&5LHu^n+!CGtYS9LF$iHg*G6)N-UkEXO18)d~2kH9u8i zA@`y)U4qmdB7un&@}so^DMxf>rDRfPP87_ZC}wYG%n}PB`+W?lGyRD8YL$R%ow)$p zilNS|CS0n-5~7k_Bk-+^4Q@Gvv`-?i)|nWsB+?&$8LqqX2vSg!%(OTA7P+%q(;?DdDE19av*yE5#(fX|wyw+lZ`kh5! zYR;n&@zpwkO_qcd8f|??+*FAiQ9OeT_23wc6Cp$DY~(>i8xZ*F9Q-xMuz zrVt6NY?dFb)d=+GT*;*VY!S@!qL}H(6!^+l2b_Xu_pR zEFmi2^98<@vB52ek@f`y*7_5pwH@gYP6mOmE))vMQcwWdXw#pI1d_W1T8xm$#qy)I z;8a+zOOP-1x>Rs?Xx!}57Mnx9pglVYohq@MXzeZ&tkyz;+6@z!+B1TPuW|yLED0$v zINA|6RU$tWPM%>}j9^oH$c`!?2ja02_-YJ)jqb3Zd(oY7L2^PQFjABst(6FLrzDxw zon3-ij$&3iW0qJ5t59V~-Kinss|f*5%1PCR?YVI)@7UO1ZB&#?z*LDvM8&HFp_Q}2 zEk}^JN8oe^Vs&IdJPicC+ASoKrJw{d(xyF=0=Y*+YU?v4KUzypg*Cez`BJmT2<{ac zw>g{HfMW!xjZd&ti3LRK^;iLKr5@DlaRjE?T#1OUt`gW}Nl2l!9^Vl+RU#u4%M%!e z^BeM^o`@s}i#*q5*bVPjh3;|C{pF9tn?N+5$D2^4e&(C8}^-MyiN-QTT+-C_^D|>@mj$#SV zCUAN>iqAnV1a$)fU)?BllBJ*+vec$I&lSk$X-KU(&zB#q#iznLz5w}B#}^9ji!`nr zd&{`Ril&2lLW5pR@KlKfMJxFdL2E4~sN_otObxmT5nsJbV3Q>wh4y=SN8D723{gg} zV5qlmVR)kas8=Ej0(un!U%eWCjV7_A={1Q99=|d38YHoAt;nvz3Q!k6Dc&!mgJdtnShQWvru4S2vTbWJH-#PE&|x8mbFWF6^!HeHE{}t;F>BH(BI4kT z3%x~-GU|!Z22Q29Im}}+uTQ-PMZ~Mg%^USzDY?Cx%Z)7GN4)fd$=Ul+0)+Je1it#9 z@RKYB>{2`;tkxww&g1gChTR7C?i1=mfI?$`SSWl%Q$XL0HYyIYn;7a=z_9io71*}g zn{7+=G0CTWx(zX3eO!Jgvsa;=R<7j1!2tHsGu3i&7kA+8-NjnNvv%X88t2@e-f}P0 zEbiHx)5=P&Drch!?pPMbrns-Kg44||Pc~#QXSC`V_WLa8g0*hjW_{iXLUpHzbJCh3 z5{?v0tZ~#xd_wqZtIw7$_VQ0kCibn>?TGp6Q}R2RofoYPrSj>HR8l3%fP_B7P#+qL z2-IhhfEN550$+U|e~pn~LE1>@&FnoW>iI<67FAA4m(p+Z$FF8U3Ql#1rQpvt7 ztbHY5?XGCMppV;CIS!`}kerBDv6I2GADj*4^>H&4t=h}O@Ip=%61nn(17*W@M==i{ zsJw$i`#eTb&+7IXuoo~Fa?*_hc_o+|;OkWMoJ13Hg;rsnR#dbiklw z^FzB~)OU1czAKgao~}&x{{zOM4s0OomvUb7ePRCx0sDHJD5`NZTR)WCR&8mv^N)~^ zcBa2x6IJ~fsc7t|P1~7BHyX?-2OvZc^7rjU_Ut zE2mH2>Fl?czNvn%3H?C`{ZSJV55c`4h2m(Q->W|ffj?^kEkm@P?EOXZBCV+YiWFzL zO!of9=zN*%tuhmozY78Lm&5x8`6&RZS6d_qv;25dLpqLi>RD+CMFO->h9{F$l*-7UhDUA z75Jz2RVGgA>an`iiCrrI+~05I$`~!G@Xp+Am*LAr$16A>h~3uCpg5?e0mncB8!mHF zGk<}%m6SGup<-NIfUR!&Y2mbt6V@C&ay5r5z!p6?!B$SG+*EhM!hc=s8t^|*UDlav zt*$}t*ypURqaGYcb6q_eVyh~S7pod#Bb@QZYm;3GGA`scp&*WQXIf1O3=F{WFTX3u z>J~U4EYD+ft5up9nh)l{hpm|$qkEziM`O9cAZ)ANwc2S^w@7zaOuizP9xFn7#py8} z%q)zxznX&yW|TB!pO&qW+cQze=r&YYGpOh2Lt4<}7f2cc0hWF3qEhkE#H<_zrqGH1xl)`meH zs#BU9Ifn_-;Sr=;nqw>d^wbfO7a3dCkw|g6WOzS{(M2-6pJVLO(SpF?-9ik9dGb3c zmn@otukoUv{*r|{mc&yfvKSrgj}v+kh62MFGO(No>HA*Z@xX%|Pe9Jcypmu1K9E7!(@@loLQ$_w^hY4|T;ks=pk5m;)P{M# zps&8C9<4-JO4qhcTZcASo3^Gm1YGO!ewuOD>((Na;kDW7B{QC`q_9gIL`!W$=ZTUR z8#H^6WCqPkjB@r8I~X1N5cgF-F)=zm(u7*2L=PW;CWPr&C8E{i(`v-c__PM``1r)$ z#PR7QhUrJz;(_f>DhaW|vZWB@Z=UlUK3e=!{!(*q^d7!<~{PMxS*t79!k|!VVYikC=Vbl{Rj1 z*lHyf7qojqr5pR^(YtfBso2v3y&Q_8T8@nLCALf9`j@P5G$rxBwzh5B7A{saVzBB_ zwoJWo&x4nmu&PP05;R8KzvW_S61P&CJNMkLZJHrRcHd7Ek+cs!ml^{zAOLA`a_A&8 zbvgjh7H1&v)tLgFoE91!uqlE)m9>I%R)9l4P16D#s2?Tit*mJR>|5)QkG@53F*ci8 zkKbTq5P`4G7G%AT!eT@z=u!oYQ-E)*w61*Y3ieLDqL)yc zPzoj=o2AHegCZ#`qi+<}7D=C8Smz-hRi~S&)-9Dr3dA*pz*k!ZU5kqa=~>oX!-U!< zX&@jz-T$V3t49M1MVyboR~O(f$EEv1A(W16!EF~hnShQ~7u{zlfeR&ldI?;F{6JlZ zq_|}U<}OCyt4jn~*Pq2i)rB(CT~uAFadrsKPK_fn)%kIDFB7QY0IHSU5lNq(-5m0x z?2h6$*v%vGRY8z7yDTQk?xoaj?vgw>HbmK)kuhOpTr-k=kP8h38`;Qngn^=FpyNhf zN%A5hR=bem949mVGNTEZ>F12Cs|W&T`c=eyRg>RIxv<@w=|?VXs|iv`mB>qUO799{ zq~u^qAC{zuklqVn4*+QHIs#ucgj})|lt$*z;;>~rve_*llNuy@EGF*fV06)Q_Zr45 zE46XG-a3ojS>*K_3>P>z>AZoLk=yAzwyRv#!-4Ml2JY3`3S;9Kj&N!i2{EvZGLZv~ zO5g&#!1dr-JH{e?9&GmO>^(Lv<=X;juDy>ns&W@MhrOn$K8rjHAq-!;?P`&C;q;(9 zXf85F20?vKKbUrV7)re&HxPqMLtqW}{!u#&;f|f#J!O@PV{&ty<2XverJ`t)$w0VA z1lZnWd7~Ut0+7!3qJP~d39HM2iMD(U0$*JrWRmxt40vW!2ScYX#2yyx(axx@#7Kr>IqVaHdbUiYOHRfv3fXYlzO7B^3_u1 zC+R9@A2!tsl@A8k>=-;*s(MXO)!E!JFjfGY2GPxEd4@pmMH_q49a-sjHFptq<2rM# zuTtA>HrXQ^2u`hH2iNc0z|0d2vKbH|EsMYCR{puXz8~9i=z)Srsj=B{@0P`Wb36za``1!V(=7I%B6+dJ{&h$) zi~Zfk1YM6rv)F$s;=XzsF)^unMEWvZQTi}zPZz0(SyRtI+^o}|iFkaSP93J9dlo}X zj~`KDzDWZys%JwYFyYTZ;Hw+(*DSHAcw)j^me@B6|fioNJm&6#C>0@~CfN%T& zsaVCdB=0n;yWsm2cTU4nO4AdtLJ>AZ+@N@PhzW#^Quuy@5bfqdnlV}?mMo;mhd4qd zig?&3PiXOdDc0dOMH0e=1v1zLU{;kgENiTRry@uUFH5W&!+|D_HOtr`K&28=TTxxR zz_%(Op5hqZ31YPjw|&+{^t>S{yISbwvk$?wYjM7@Il)VeV#5MLs;fqm@g5`rQon?O z*;DYufIce?OOgu9d@aFtL9KA$3d&MtQzwxts#u5bVudHJ1w5HSRq0dDLyLEqPd#50 zp>=98xMk89vlkFA{fMIc&nU_Z(crKsFGAp}7mIo%OM&*#c*v3_vcP+Z0KQZMa)B53 z$-PN3A_}Hnh7_kyR(CIFbeXK~t~3hw3PIrN?v;r7>Q(YPDNZEkP5;c4dNpaKN@ORx z#=BY2BjpEcys*625Fvf9_j&<&g9gbSfvGbVr3$a{ zD87rulM9-vnk3gUD7Qlh=w<7S>+k|Se6ckEU zg>f@ke7iusLqlaR2JhkVQgo)MnP+QY3`!)hUK2AE9Hh`x3)%|nF>ccJ{Tj* z!{iVPKD>+I$Lbu;zuw8Bron{Fi708ci z$m}3y3G1iu-*982@V)E(!pm)~FV+ z?_CUhow@OaVGILRgAejqcy$kZ6?!`YBOo2p^)v!bD=3n$jp)sXTtdcaJy+r2uD8?! z0-PK`!RXMV>@qUxHHw)=tz6a<7Y^{qMtV;I=Ls@ekJQG-iuoe0dGo!d&?S{W`se@! zKiT}QzM2~E&rKBj!<688Gq};}mz-Ajt?EaWxT)XIPHvt*>vQUra`3cM)H+EozfC0E z-pj8yy8LkgNN2x+uK4h60`&=06AJoC1irdm>YRL-*BSPYy$l;gbOQa9)cn&y%^kG% ze@iX768j9x4c?F9W09suz+~q==d$c}qyP z1~DXurg9LGt>NwK>bxiWmD{Kvc!#VNn-L4I>$CD zI6WTTozsIpypWUdKaN!}oJIbPWT)wEf0w*iZ~F(5OmBOM5yL-`XnNbf5ckzR#6)kq z|IDJZ&DfVjbZ6TKanspmA|CH-ln^`HzQ{p)-0xD=ejtE$nS}t4nBcGJYphmFUz3hD zM1*>DohN7Z$WX5wssfN4iUq4`CdDz*h$fwPYzMjI1^L-!cI?NP}eW zhK8D*GI1xtZF{VHDxAi)Js-`DA3RMOOg~4?KWETdbMoqwgR6XI+A7bA;-1Ik&@2cWZ#-(@0ycW^d&B6Oia`ichYchCjRT(p&A+4 zJl31e)$@4NqDTK4s#WbU35SRT2yPJfUQop09Zm{y3rquNs^%88$>Ts7X#uex2a7=3 zTj1rUsSgnV?Z0t9C8iDq4Ybl>2z+(85KM~y#{DFSdayZz$<`4<_Q-(jqtLSdX%lf0 zbrk6ZlPEr}ZCX7v)UsH?Z0a~<2D7QXHGPN45$iyca5TfTJmGChiR+fK0099Wg8=W? z;IHZPth@C2V3s6)9{;9-spB-&?ok2!dW4M)%c9q@n63BJfp@5YVTZEKXOwc~Ps!QoS0!PvHACd={2*MvoO| zca=b_4j}e7>_+<-d0ry`)AM{1^1(A_w&1ymt&+$9<0%AimlG~jBS7k?jS~hu>54rDq~iK*b=)qftsn?VwHoSeQckM`2DjX74E3Xl zm(D(5e4Gz@ki-QDe6?LlPnH7aSapc4c`Tg~s0%fexO(2p{4exG*M8sFQ)=DCvq_yhQfz@jUPFihkOmD{}Fz#JKw0hgJcP%EHm@;q!M?VeK zy~1o@+IgpDb#21lmb5@1cz*ODulJC0qwwL8w@128PxzKyhTD$b`)s1}a(@(^kg;Y9 zr>_@LD$`6~c{!`Q6q~+Yj3hICeYgEb*EqO`akd+PYo$3p4w)x;Z7a_h5a?z2W5QHL!w-v0Sl?cLi1qs4>uvbsX&@ zJuXGI+q-v|F5wU_{h+W^1ZhaAguqw3gpp(^;FAK0nZB|>RWwv~B}Nc>`E6`metFs6 zu?cr?c5K+gPa|M!d&giIK6dpDyoH0iy71Za>@XA73(xfic4m34T-aQxG~}UW?f|qn z_e56G6S*mc9r58ct5&V<+f(-T;5Ki>nu;?{hcQvx)O4-dW5I%+ZRaf#u-2S3uxb_l z*EW^Nw5vXRTd@$ay7yKAYq=e_u-oO?5;D&>YQ$I?rN9Bw^ais^S#~yb+(iqobFxfY64P&e!B>Kr6}!S)0Mg`$}JNdPx9+JUmm|Xr-;ugMK6ULFB9DC z=Iq7l4NiT;>oq(j<>!L912<-kPQl9|A-h|~+ruoV?1CIH1xU=r_3k>fay=1q;>V_j6s()knZ);3TO7NSrzPXH|Lt+q{U}>o%^7AtmMTeEssNz znY4V$$oxtqnn}x5h~rH=Vq((rAk+fWfhP!cJso%=;$}K^rJ9|U&P zlR+6xd<_CTq=&zzi%}3Q18v0p;wb`uT@yaODSo|VMATe86)DaN*{FLOqf2C??lYzd zo-PR7sCx!tzIvwo)*rygO!tv7^(<0KmB>tVx9-`394S4p#$jpCA%gxG#`MH*01zZ| zBLZJNS4bvHL5XCtX@8z4AkWtz@`(+h%6&L&)5(1+cx>UD8B+u51%QgPEZbWz6y$c6 zKW7B=BH~3@?nwN_00PS|LEx*G3dv+CD3MIY`s+;s^)d~W-DIZcy=!3tg9S;qxjAc| z;=tQ(=Cs+o#eq>*ruFo3>7lo9oM&_sj20fDdHh`*+H zvhppxQ-;Df3FMnKWcGSA>Uuk;_#6rx^WZkg*SMses6TqFvc!!s7zoAfLB6DCp7Fzc z2(b7<96qXt<}Fy=@Tm|FMq8fxBQ|^&xkDPeLK1xWkK-feh%zenskeZ!4%+`#;kRA; z?=bEDHsYlpdi8lb_(J=<0|6dVlv>~+MKYTzu_>g(nS;c43DmnaRJ+UCdn6+w1od8| zINf41-^b`;v6*)op}b!ZXfr>67~Uq7-}(jn!REIb)rW|iDzW&emHe=PN6HJVWJv!b zL`cgMvQcukA{ll5C<0%7Oo${)K^bJEX%}x3kdJGSY!=FhlRf#0WT1K2gUVrlf)25G z{ZKZl^<^aU8ewzVrf;?K8;1Bi8ou2LCmZdBL(?>%K7m5wbriGuNh!O%j^8vgyq$RI z2bZ@`p$rJ@(+J@6ARHx20mBqg%qo9YpgyOevVFA5+U>1(EhUF%kvOEtCoYHA^rMpw z>+E5{Ow6I@in7$vysXKv*Cin&dI3D1yv7@X=iybxH^;-h`?Iv)1 zit`=Gh^UtOE>fJ8(p|sD=>F1Oe`w_QeL-M%{Q+Wl4^DpT$EVMZekN7@h}fwT8Hje+ z9}7aH#Gu25CH#a4dW%0!@TbT?^?!x{{suxOSqe%aGcgVPg+Se{pT()3RS9$!OWnjFf(ZM5ATDMch}vBPO(r521J91e$cWKxj?+J>o`_ z{(yK~lUO%u(jOV>SC`vKsy`tY;`%cJU;PDtjkb`XX|(090{pioa9msdE*X)wQ~yAU zbF^s7KN+1Z+VTq{hkpqIwdEefaDb1S?34me4w`-);n#@OKFEj8kd3I$%oMapxq;4v z6F=k zzMp9>Wyu4bSO;1H#%T$G=x)gy2b??)sCl3oubf!-gcR0ZxnCL~&L>{_0cK_ah(aU_ z5x}89SV)!v4k?6KXId;!OEgqANfi$5w`~1y!%Yu3f!?dTNwZKQnWm&0~E-UJKlt3P>A+sCMFq`p6hWV~*2ifN{6kvj>y~iXBw-7%8Y-Rex_9;#lIPA3#Qq13^e+IRalDFU2QI0e7r? zOifP^s4fkaLjNuN>z zd9sGgjz-L1a04UM9BuxBJ5%CTt`&$UvJkeIzrgY(a&jZAT&wMp^_tE!8abd&L2(_F z?Ns5SUD^I>WPKX((*G?k2T&Shc{&1|^b2pvQouUJ7SqHt1!}E^lF=^IM0mc!FW;F~ z3(q3bG+Ov5$%|>>IwTn_yvImpJra!;4kC`XVu%?Gyq#Eq25t}#t%2ttZZvQs;&BaR z?Wlp980t~C15LFV*^t<|2=I6d{u=EgOP#duJb_GW$ZUT^`^0HX@61FM47U_`k+7*s zL%{8zN?V0wn<}Me>|-Rhjfm;{Rpz6CifW#Z058BuNy$>c2x}BmjO_xI(NJ>#C?Yw1 z(s!?R>Ox{qqahbbUQ9#ydQhMt=~-cgFGi|SkxLNw)ulv*ity6aUYUZf>=0P3D?1T4 zx^fxfab01BsVl<_^^L2&5mzICgEVsp@GT$wHA+J!Ta?DEaqcU?<*YS5Clrn&7@W2i5%X0^eka9eOg2T$a2cwe+C@645?P7PT+4zPDO=B6 zBV|>Hq21A@AzcLoL{dY5kNpV2WGN_*{E33ebifr5r9rY^LqlFrtQ7H>3lEXxIeYAi z%EcbjWoV}r9HQ~BK3TJBa&YPm@cxiIu%r8&Jh~O4?9P=NxMGB3BF=HSd#Gbv+%lK0OV!)OJuht91`*Q_YuW5Z6`?Pa2z<3i zB$6xz0;7h+4APW9U9O?Bw?k!c;cz>Z0xnZ%NVA7!-nYrcnzjcmn9uP9e$r-pO7r2W zBAXJo;PF)zc~_2l5?JrBK@*`FfKG-sFLIO~d!Ble%`4fU7kC~8w<7uMvV5f-FGK73 zM|=an*&W06#$#C3X{_fJk{7d{K9Y>}JS5bj$0F5O&&MI|t1F2L>-q4KAWZ#LQZa4n zACI^(^-n-NZtAH@H1$tphzE-wPP(M7My1fHo`k?xPsU#}F0uJr#wD@E*9hdb8Zw)K zFxGW`={@Ke>?8aN4Yr7L#c>DDJM2BZ=u$AVIPZ|R8TIvF9K+$ah$3W6m zdfN*SkM}kTjJ@rJ4AcK{;p#=8gVuU60$;rZe~p5(lARR%r2=`AhLl6CZDpJe+Z@!P zyPQ!_Z=u&n!{ouK6)g0m0vu4fLu7dZ0^ zCLLH^B2$I)F4?b;c?j-|z`0iz11M=gy$p=S)IdAByQlOhRE1s% zzR*CgLV%CNNi~wCpq6ZlSa-TvpkAY)vK4lxaQ+5A7Bg0A;clyTvnUr*RPuScrse{Vn>C(1-c|NHOP5*_wUQn>E0Z${j7 z*tZ}a@33qVcG$NvO#k<}Rc}MVP@uOX@YOr;*Yt3Tv89KbrPVtH_+3rl_|od#k`d`m z>ODwtj*+F+dl{W8ORK)H`Q9fOTw1*!F<*T^ekZewp`si1VC(a~9E$oN38YHo zA-bmekf1~g4c1i6LOx6k{i(%Pnjb+Hgm5bYe3?!tB}+j;M_5m!%y23^LFpr(MEY!&|$jyW?*nazZzar#)m5MLLvRpy}o)*^^ITN z)zhWA@aT6ybzoroy7PxOULqqwv;T%OvCom&X-0z2OI~aw_yUs5NH7qp&=-+vMuIOP z?yD~o6(hkzO$Hbzz9OvYapDfd%{cK@#N*=xo0H?j*BGWBYIRXx2U}3hI}zXw1^hLm z2%Df~6cIc6O@aKDhRpsN>UnXnxg@)~wF+Kvqx}l?O}ldGt)#%t!v0x!s2lcGd(hye z9NCi^D=O^&ny+8MforquH1V}0{ay|Uhu`+h>#x$r3iA?pmJeTW&FSZU9ei3;o71N4 z6+OpFj1>83U4gqB;=+S{4zivg1U*-fUG+G-Wf6ff?^EAK)jQb8?}$X&ZRF{pf`6BY z>HkHMeGfH5&FgL9OhBj&4L$ZzeEwjL%DBR7vs)m=nRl~{gse)3BJj}#cpPnre%iWu4_ zElTg#NJg!HgTPn66)MS6PzX6`&P{$NAa`pJd44TI2LpNF|4*+nJZABGz{UBLLHZAZ z-p=P>D2P82Q9tjtH~N1982J7(0(hDT)nq9sl$^#!tG^1=-!xSAMy^bR&F!`Iw)Xca zgx}+dx{0mshXdv?dm4Rq&pV6p(N34x-Nu3pT+N0J z2d=-9?`a0Ee@I?z;QA+$%)oVSDAs==)eKzsAP&D6TZ#;|>aOHmj#6LB+! z?TdJP3}Yj64BHPmP@soywbU$d2;H5Hz*lqd*9>QDn3mxT2FK>roy>gwBr;yGz;4D| zAPr>}j@4>)c!Ao(IN<4?ubFj2SN2+)u*W`$bgrc;pRb7U0d0VGiiS z_=K`L0cn}TgEuDXwOZM0J}|=P==xO`A?9(dYO@&Cjo;T~7ERa_tSOF8aI@J{D}?qU zPFN;};bz0={-oz%(SbjmyvnAmH^3D zCRCr$SfE>GxQw0Qvg$|L7PK(x7p^MRDgjJ5_{L}ewwhqGG7AH)aOcYdt7{17oI8{` zLSGb@PfTM^Zdm`sRrx|0(USm{SwglwAUY_&3=ya#lV=s|(Z;w+ky2)fT^Y5hCsSA< zI5w`Zo{aQsGe_#1Qp3~GKZS&5leK(Zoys2zJtzsju06bm&&8De7KIeCD+mFOl0C~pY}xW z@vD*rwU$5k#|~wkI$v*iffUao=~M6Q13`XWa0O zQnf*nm*Bc@4cZHD9DYi+?5T4keJQ1f+ko|A9Tn9g+$c2X>+izS4(w8!1b7iUK}3Bw zOX}j57E|XUIWwzKAL~6;ZDBkSkYd+4Pg5#ZaUCvfWR)h^JVEL8Ag&?)UOZeVdbv^X z6$o*w;Ld}2(T~HaZ3JUK%Z(0C77KMpJsK%n=4KYfl~kQiz*(8?=YXXP7@sq&c~aZ) z%Q+>p7$&BU`8<0eKH*na8Khs6nHSs!L0esjgrUqL(W|BuFOt*+UIpfE*llo@q4>VW zwV4G}pU`4ljI_+rkymLZrm)*xl_!Y`_U?WFd;RJXLc#81RlR4}!QM-m`!DKJChu3S zRmas1MvsU~ucZs_1V~A`2EO2k&-pdQbs3Ro77J>akwxeW(Pke(;b+^atRg4kD zT6c!?ff2|71f3{o59u798b#vH%t6vqu(q+WSy8K^Y1wiGl}GM1nfWR=8738w#8E*+ z8Cr6iv>T(DWvyeE9!kcDG$XTbvEZq3#C_%9Z?|)d-uvs)pY-b1mp_ho);PU-(%n;% zFJJcQr$LX^uXtF_m~(=DaqdLzk8!kiC_YYq5M{ak6!i*y;clfks5df8d21FDYe)t4 zcwH1$O8D!Xit!UOpk2ZiWxT}BO!&5gfs{tl@wA)BR!;%p4al;_y2chc2|;vjqP_%IPp2w&fNL$ z-^;Dn&MjBGay_seKkIrG(@)WB)pUcDIAZm6Vs0poOzWMlQ8k09n(2#qp=e7N7SEY8 zXWR?|+a1^Zz>tAi5%cqfel(wz)ymQ|0#h@zvgZbd?V3^>^Q1QA)urZ@Cd_i+YiYyr zJx!Wprj%wy3p~y6wK3Z<{nc5~o-Zr}Dg_Gyo-{>AzOV{yXJ@r~&GC$iX1EbD>Ks^R zgIZQ}<_jqRyXNiy-OZXWQu)FeXjzlhY!@86fz2!eN6n;$@=gsbO8ojYqYPw@4JBm7 zf_&jT^gA;PVkAowS>{XgO}WcPhltrR=~Wds5rT#Jx_*>jFZqETK+m3Ate9iISQLsb zy{l$O-z-*5-#5mg+{GheiQZMNLnUrd^r5e^DUQnPsftxkp64t=2l16s1o309|msxnH(a3d7d2S#bo5GPtGJw+BlH?qh{)&jk+ z$r#)>Hbw>vCC11oV~AzeLj4?Nh9*MHL?$vrOr6Z2=(8Eb^19wpwrr;Y&5Dyp#N(#B zy$`R1dQ;*Qc)b>iQ{nZ}n2c=Dt@oDS-KZo;VAX09eZ z_V8TIs?q-pIat~8>XjTqD;M$U=bBZR+di;q^Cbh>Y9%ZEK&;Z+17n zNL)bL=$}m+^I~mmo}qa^ke)l95(5xsODHZRVV2Ay4B@_LChis1`DD?p)+`Er5)?WZ zii?3#p97Y+K%ubRd__`RQZJ#`m7sO_6eaTDVJH;aNUXz43Id*AFPRXE{NmD3Y$uuz zLcHOcS+>XQvf(&WeX$Km=BiOKQFSzLO!Hi1dZ|_62`FCU$QXWB8!*aN^rn>!H)R>S z;5p_lQyPxe+nc0Wzqhw9DvlMcGzAiMq=Tq`0>vmAZw)+=gTZ0bz6Z~<5%;rK(7vcf znw1pDP;F#F4R^HI2Zv|+$(q6T*avr{VfM=on_h$tlV28!%Sru*j|^nK6t{D8WPoPl~IYSw&%iqfJu1Vq}RQYS;<6K6E) zy>Qy7HvmaTRXdBzT)hQ%ROS+lyP(q9&`d%ByF)PrG8UnfT+~>KYeMmqSc;euOYzhe zN+PtDu@q0U7O)gcVkw@kq_{Q|*O3&_FnDY0Fn9=~l;L<7ynV(n_!;2onr1Pd*+M)L zGa>mbi211KTn{}wI}|sN9*E|(N)ME(_k?0E@t=kj`Yq4%k%=iAk8y$<^Bl*Ur1?lN z>0wY$gE@b#cuo`Fob#SL8{bP~RXq=U9}xcMgWnf~;)TR7(b~iOP9Kbv>W*UuMaT9r zq+@Bkcv;~Hh@GK<6NXP}9HmB3$Hct9e8!l8 z=oM{@UsB=~AbL|MUP%gkGEi+Dqv@E25v?}q$(^LPNjtDml6D!c?KpOfc83)i5Ydr8N9QQDQRFkgQW* zwUuhvuWis=bF#rtx@Y~+wt@Wcj!pf8TK~mE`etqG=7Icf}!4Yn>R^~B?>P&1PUPUH-^(^j}k1hC`G>=Jc4#jK9PBmEk z|JzRB{H7?(Ib2>h3%`TMP*r?A4G%6jBrJ+IAd21?iZ_wehXy6&xOwKJz(FuzOqLRF zMmXFOinkEK;~~R7oS7{4)|narZqmTppn#*%d6qa3Z-+$h2*o==fP?%NE|q$F#DRDh z52}guJ_1I(8yMabiucAW?gLHCC4l8Oo{hF;#))UenNK-$o`YqJH&B! zpfT68|M7-cBNKf9bM@U-XDzSXh}-mz8s=$OmNCT#n-iw^P+FfyIY@lCa3oZKJS0A1 za_J(khedCnu0-3V*>OsI6xzEZ6d!vmYVYHbgNsk-Z64+dQru}Y&Cyp_w~}f;31@#S z`HlD#;_K6)_)Hw3cc=)RuS_mJt6mqigps&Q%^azk-W`h1kvo|Gq?*#qt9dMiS~;zb zwv^EX^kB+pf+Q-VtLaHIC_3&EysTeZP~D`qXK7FW`HX6EHJNhSTfs-*XZ;tOOW8_~Y6 zN_xm`gp7Q5a|!+8EQ}6n6jNWK61wi-coScSAAcnjUnMyY5*IkX7j=n><9(s{8eyDX z922h@9i`0s^_j8(2g&x08O1T_D!vJp?+?YdK!A(meH?4nBiF=lqvLl%@!goSeb+?D zL9;-rdJlx+d(`3n8C5Ug2%&N)!sMVrdN#${GTO zVun^u9ty>;W6+j0hu^dWy!frPoYx$FXD#G4hfF+``Mp|mcsLY)AP#vh-_zC5F{DtgXS^m**bJ!27NV1(_IuuOdPAa}GFCdw_jcU!ou64=VW%ebj zBUtoxX={)cmee-$=qQYh5n5z?wf>Y+8$Q}5cdD@uP1{P9WM4>ayH?aXOJLbP#(uLn z3^1!rQS2#CVkHI(C~D`aLOV;m5yiA3`n1Os#|F*VWhaX^bEoa>$U|g`0o3~3fyyoy zpffbR5I><@#F*5I;(=naR!op7U=<8EWEbOG9O0ui=Sa&W(GqT+E|Ka_zI7D7!?$Qk z2g}H%cm<0`<0q8Ia9?(Y2_n)w(&$HrLtsUDtRi?E6FgoK~OVNENii;Icb@0 z>tZyYREScZ>Z`e)ONUw9iXkg0!>L(@i_=PyHf^=Cg+2c=R`_tb$gg8dme!!@Fc0?T zCHEX~$FUx3r5cc{Ri>}j{XoMxpgPvWF(Jg(EFN%d1B*o}VIxyWw~eF4B2qRE)R-DAWO3oJaD>2iTg^=MU7pEmoHlTmXK`yXc9&=4JudY9 zv16WtMymDl@puU36Sxm{NAUY^gY2h4V1aX4ZyadyJUqwan6-Fr8OKbJ<5Rg=N59x6 z!q2}Iy?=_9>!B>9mksy{gsMJb2|F@YAJ8i&mPVD`qTdDPF zJ(5y-BPcQ()u})=*-KBOgK1BUktROPn&sR$#Bxo~joY}jksB|?dz>5NM}gX$?9@EwL1%yCET#27hUWLDL z_8{{%XAhp?UCm%43aoH7qT(uT6vsBAS)Pg0&PE9ny=@n5)BLiuYXP55<{4Q$30$A^ zc{b?hl<)G4J}2rnjtA3m=PUw*EB#2Txv1Y-dwz<7`lmERJ;=O z*H8y_`mG7uQ_u>rpNgMQhK!?oA|gR-s0u}^El*>Rrz?=c4ba*SY%rR9kW_QLKpmu{ zlPEYhsZ~6+IFigKS{6{U+OWx^^VDeBqoo>LF`(ld+IZc@`OIi_M#ZZ-ikvJguBZK| z3w3OA;fOWGEmlpMmBC?qEps_j8G5Yi>lk2NVe=spLp}otp~q+9CzQ`(?A?b9J0v9S z*Gm&og1DZ^JUb#o$1DzBkCX~-AdFEe$N}pC8fSuY0A2TJHqtD?PSQ}Y@1fsyg-1b3 z?nPH9{5kju<#X{jj&{-wNBcw);%J$Z1@QA2`1uN$%IO?(9;MnOwhdZu)E5NOaD7bu zFe8;O0BBxclw8xG!}#)rjO<1Dt9PJ*C%E!PZhSE{F7c^!a(#NA( zu6;1Rm+{w^)7N9BbuBW$D;VG=0_ZH;0be2U=9hP&O2zr=mb!?hnNU(4Vfc12x8k*~u$?xHMeX4aRl=dN#{u8U$m6o+p_TPWW| zUzXTz#gaw)0gkWx4gbkj+M~?7j!VY zm+wNGzAT#d2cCxuQ??n5Wj#5bvx2IVlkX;oPUx*{^6iWF;C&D7_?R`4RFx24zL#-# z;c&fODM3E-on1^Nz@A@88(wu)J*kIRn{ zCVh!nEt%voQ@HSDnPwnAj`qF!$zDCE)q{QE{{$hTdkJEUXqmqr;-evnX zu9C=mxb;}9<{LYUX4SBrqEV^f=o0Sm$j{^B9{ps9T1~P+!;Ol(t|jjU&|dvW)7>S% zz{r%~HQ%gPAZ68&U*zT`=I$EK%NA_}3XVTIzWfrmFD32aZW`axiL~-%#<@UUaKe#Q zcc=Uc19wq4G&%BDxpnb0i^==Ythd#JvCO&hYxJCoP*D(lU174_W+TgQ5LhR7$@oy$ zH|c$Gv10o)P=~Lx67T1}ok%n|-Zf_Lmfs>Uia4WGoU|)}CBKc9!4AEv)k)=d2(V4x zu>~xBm!8|ByLIvbyjiQ%Jvoe)C?2t37|8FTeUIKbZXzEVWUKF^A+L9_Up2J&18!aD zS1~0hiaI9IzWgED_Ua4CeUjYqBedzKM7d*faxz<+3M|j%+)*)i^IW?ke@+iwh=pde z{{oGB^<^sTG4moov2n6j_0-Yy1umK4yLNSe_$+s#9!+)R6-19{=^tD2;_s- r@jXA>Wy8HSoVV6H<4WSoKLd0`{sn(|I)giQ@~?Oa<=^nPUdsL-99~Jm literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/USERGUIDE.md.doctree b/doc/_build/doctrees/services/object-store/USERGUIDE.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..0d4c777b866c406f4e38987ef35930b75f8a6c4f GIT binary patch literal 157393 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/access.doctree b/doc/_build/doctrees/services/object-store/access.doctree new file mode 100644 index 0000000000000000000000000000000000000000..7e92330c63ecc90e6ee169e6bee29c97903dbf3c GIT binary patch literal 15118 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 BmCOJD literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/cdn.doctree b/doc/_build/doctrees/services/object-store/cdn.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ac85d5ed21a1b2ba2e968c01652277cc1e562986 GIT binary patch literal 16110 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/containers.doctree b/doc/_build/doctrees/services/object-store/containers.doctree new file mode 100644 index 0000000000000000000000000000000000000000..dd54390bcc0888d401410d3dd6002f6cd7a71ff1 GIT binary patch literal 41332 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

      bPKasZ?o#d757$QwMw$~@3Vp5CW%~e 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/migrating-containers.doctree b/doc/_build/doctrees/services/object-store/migrating-containers.doctree new file mode 100644 index 0000000000000000000000000000000000000000..879bd06ec108fa2907b132423a1768aa8e98e07c GIT binary patch literal 19256 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 b0?-k9DgK&s*a3?^y$m-Y-HX5VeDD7N+@AR@ literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/object-store/objects.doctree b/doc/_build/doctrees/services/object-store/objects.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ddc9b3ca78f7bfc0ec3c2a32b3962f595961dd29 GIT binary patch literal 54948 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 b4y`H}T2c;vP2~dT0p;Q)Q5?TLQ#DK literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/orchestration/index.doctree b/doc/_build/doctrees/services/orchestration/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..fca3efa731bf29c23e534504a2e5c11d014e464e GIT binary patch literal 17915 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 Y4$+->$|wWB`arKk0((q&Hb!#)0xgf3J^%m! literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/orchestration/resource-types.doctree b/doc/_build/doctrees/services/orchestration/resource-types.doctree new file mode 100644 index 0000000000000000000000000000000000000000..2bfa5a6ed6b6e50b42bb86378de611e6ea4047d8 GIT binary patch literal 9052 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/queues/Queue.md.doctree b/doc/_build/doctrees/services/queues/Queue.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..4326483600d3d8c75880ce04f9d76b566afccacb GIT binary patch literal 26480 zcmeHQ37i~N(a)XiB-vbqD;!N`A-iFBmfYbA#w?JSumqBZ0kdp+XL`4%GdnX~J(JBs z8UY1@ARZ_xB8cLFiXw_Ah=K~9h^Q#uh>CdQ1$g22uX_D@dXDwS^{M!M`Ta8Qs;XC2 zuikt0uHN$|WU|FfrJT$AsX{Sp`w31C+l6w%8{cuhHzDvQwwo;lYuGN2mTcdf6nK-{ z)3e5n8@DpG!~jBua;0*vSn#IwrdyG{Jhjv)S;Po7`QFrC0bX7QJWT@|`J7)iyoy}` z@fHQVBxU3ZWh!PX8KIm|*(*{;hHR^BD{yamZ+aOjJThgFU8bD9(>BUZSZJVnD{GZ4 zqkWKU-!TTMI4s3hDc9l6P!w0CmKhmHZTQx3DQ|l-6;~^3MQGK)werqV*~{x_%+}1- zlAfcpYl+P5O;3XiOH+n#mn$W2p5kCdEeCt`re^|b85EGup>2#@*7xQsiYx2t-@7+G z53oy)NEt(Rs9D1=TV>zdr#C$ZMJr^gR4hiWdv-i4O73mMy6&|5L*_>}>_oGZihiiQ5HJ?MJ} z1m1yWD{RNN`-g4cw}xO62laUex7XXKKMG328Ytlzu+&2WZy_v|tP#IhA=HvI$C*<= z4g7g+4HRrKt%P?dP!9{dMePp5C&(VOfswJZUi-%JPMgE!9f7x4%8{O6P6_EBo?Z_j zyG*w))g?Hbs8f z>olj9ts(AcUY9x5SNwS?Ghvls+yj*|l+hh{N3@&MvXv6U`QDN~b804U`99)H`^;$r zR%Qnz@V#Yy=0vuU<$c}?vr{!+m(<#&J4RQm^}5Q%E_9tP^bK#NWlmPqSM_@Q#HB*96{i(4e*_^J&}D z+RYm}yca?59nRrSr_*Il7OLJ_2fBE1yR)RvS=#3;>vNX(Ijj0A4w!VnngfQ`)wlUp zdlyEvo@dF#@fV)&oe+2@LJWr)T2mRYDg(}8Vj=4TZ-cTBWi=tUPKw!l3A5>KY&JS> zvLg1qlgvrE?2;v3uN6vixt8SQc88U?Iq*(lCF)Ujb9IzG6%4Ic8gfK6TbijRBDmG* zX-36_h^GbK=`12s+$804ZLFBSW=y_DOMMoY9D`^-7~LLt7Bk8foFReV% zktssVQs8+kCX;lVwnsjPiPp;Z59EuP9SM)10YC7{%-d4%)~34s8sFM!pA=4t?G}+W z+MY1*Z_!|}xX3tmjj?FJqD6_0j-#v0n=@;OdlhcAofu_z4d;*YOBP1i5p!Az6D&qH z+uOyx5PeZPr8nAcPU6YcJ3BoQd|){A&ap$&zDg@|cfEDx3Ov{(yZ~aH8+b2!4q|}U z^FqCQ=bMv@7~#mfz>19)ZH~_)#mk}pXQwLf6=LrHh!tKEwtB5g@XDAKUMg02gm;;?!i%)xUZt$?^1ypFt4Nv85~C;Pki7={ z92U32*EX}lh~n!aE374R1w?#(;JpFWI85?#k;&n{e`Vmkk!ybf?Yq^<`3B}YvUo;^ zOIFImK77L}jH;8^xVAjS%d<31y9#7_s0e!7Tw4 znw$HDC;-~&%F00fXk!nT9Lvx73Gb~i=W7D*T9%JJa%~lr}b2K~eU8w5af%hKf_j#bF)Pqqf8mP?H zS})tXfmQKdR)sk^kHuCV{;`br-us-(EbSJvX z?+v`K=u&nG?+d)Is&sW#cYol0O~XuxPtISj;o19!nbP|UvPi01M#=U<7jG{_OQ;8U zw)Vd1%&^$ruhxVAx73vVVBmcl_3FKgQ=h@!g{+3YgT8uyd|Ll*4Zop>71jDZ=sDab zdB#|FbZuUcg_oKK@HBYkUd6;?fejKeb*z*r}gnkn9Z+LaL6Yl!%p!22zW$P^z{ zBC>b$Sm6DR>z<6>*jp^vDew2ODrL6+L$fO1rDgads;naa2{k<)cu#OmOz1JGiN{;E z&?f`$&nSr&Ej8Nwi)iz&(IO$fXZ5$3I{z-}T;V;X)p>(v8aIC2xPZpvmj!FG z=Aa2E19MNt6iX8YzHzCosnaAzYPyD@$tb$;e42tFps7MA>8eUx=0>R-ix!$DAT0_c zeIk_JruQn0V-3BG(OofG%5)r^NR$Zsh5ghXA#|-N7uU)fN3CbHIJ|(Ngj6x&)GSwM zy5vS{6`FyxUas5}s*z?Q)4|e&S`i6omf$3xS7yQBY#}T9ra6d*YZsb}c+K*~Wu0L# zrFk6oJfD2fULc8vn~xx%z45E8v(07E(P~bYGsfzM_7UhMeY7tk(b6QgS4{h%plY|Y zKayg)%V>d=sI8o9wb~CrrZV6I5ew)bNoxbHS+3B*D1!mt7dK!-;2RA%s^Abt(gs{h zZy}1Hha`f4+JsQjRh78RO*CH6p#pN40!jD4fE(6CLOY)HNWK|m{|9&zqnW<&LZswYXS1Z;H`ESjQ3B}Ym@qe_lKzVpaf-q!VTedq~| zj%FOz$1#WmbgYmL^|3}04f=4IJyywa96n1wlU@X5Xk;ydfL@GWJu)yO4H{V|U_Di^ zXl@aW94`fp8aV;^&QoJ)4}eaQ+*706$Wx^)QnEEF!6`&Iy0NcCrWC|P zqtlRM&cdQ7-1C>?A37bm>=;lQ5oaH9PH)x&#u-8c+st$(VgbEW(#hy7>BQQzB-+L_ z+FWKWKGOCHT8x*_9gXTaixGOpB-@W-C~`Z3fGi=Fbi=xsF&SrER+Z>%K;SY8PWw)A zrbIzmDaU+dBN91*RhN_oksq6mhLDu$Xcak9qNbzUwXSl=RMU}*SU@`@-ONd)Jc^(u z<}0^%!>sv}Be&090+IfqU0(F#Eqs34&Rg?w?!=qD zOfc%b*;h$!)tkK>Y3CCG->hy7HLnv8Q3YLrxb{q6k9e(T z%Hr_2`UVbFe{VvQu0%2P^F{;#y$Qd1;AEDdoPXkFzFDAQDpw)W)XRJe3Sy>mHIiZ~ zE9tFLqDZJk#c$X*p1#UD2=8P)$tC-)232O z@_H0O74Jk4(7S|C(hcijhN>O=-2(C+1(F^f+tHM*3OW48(R(iAcVfFeldoiX|Jo^z z7&yHj)xIZo>d8)EJ3hKY(P{8L+fb2mXWLn0U{nZI{1n}QnrnrZ0pz{HP^0j-Y6ZWK z@p}FZ48I>0L$x;|2(d%Y#@n?w!&A)v{q zd@~{eeORc6zR5=Y-k~EQv&WK=8^OrPPcM=1$~#}vpBwrzQ^&|z0Mp?E~D>raxTZ0&<{91&zvk*wCIPN z+KWq;(nB1dZzj~WtS%PgN1U}c7q6s;Ilhm3ItTljt$Ob&x->;U1w@4#_Yp+e#rypkVgWrWX|{~Ib-~Z2AZ8`>3*@-0C0Pdi5)mgM z%YX@bi2jw}@G{`nhz0Z;Nvk;!*YY;RuI15h8NbbCF5>ReV}cT^He3Qkb^MMI)Ern( z^7kl$>HPsgKz|fMNjI#A8Iplodc~gv z=qu@l>SXg0W7Er*zYEk;3aYWM{X+_3>ZWlM(ARjZkiIq^DQBMawS;bg3CL6a=0wB- znj~p;{EQ2sTlhjKO=iS4m#dF=vMB-^t1s+i5%H;v*P}0&Dq7Q!2Z3795{B;BwA zW24VpnBI%^QZoE!iH{Q_W(?Z5>bD4*@AtnVOR%vL6 zQ4MX3(9?wAp(ud(ham`Pk&sEcVJ*x|b*yg}kPZcs9uIj=&y^ixxQLtMxNUfnMVVqD zw- zjdFuIOb2>D9=CgOp$RY<4tNd4BXmM6K4r*WKL+dp+$YJ%EhWn^%?&+jSQl2f9z`A3 zl57iG|Eeb?;uLKIr@TfNZbf1FRbbxmYtcZiOcwDP3};GUktwFbVtvV;v;?5iabcE%|h z)ne?q#foctn@nQI3f1~aY>nhrC$Zy@)|1$Qx*xs>nKFs3MFb}(f|E=?7ydAZtrIfR z1*ivcJ%=5Sc`0Otcu#E~VeHPjy zypze}rLlc`tWCCWH%2hV7c02=2{*aASI!xbB9kL62`4re8$>Bc!p0D96h>bRp`6hPFI5d^eZppp$xFccQDaYzgAQ77mW6vH%6 z6*5~wGJFxEPJABKwn}k5v#JViKojL?!0Et_sg6!VHaI;UK|pDtp~f|?RE=v>Lw@KC z$pVpeAo4t@oX!M6RQ^%~0d2!C4=&FW@kqPq;MyIP4Q;5DO?PX??t7Y^pw3Bb(9NTxKHf6b%YStk%#eimDi51bw_y zL(xGA^p`^rkSlbOZdeVoqr4(MFWey@c?AN=<-9P?iky87OF4GQ3y3(eyX90whB-Zc zE2D%Q)(YQRJ5+1K69TLaLM$L((#?0p%P507n6tP#DuU9ej;M~EjL>{nd;~?%$1Vgo zV-i9*V`BI=ml;y};6vZD1>_tBl0FmmTzCC~_m9E_HCEW+C5!OM7W;4E12@?SH~b=D zC`nj(To4|x^%#RqaNe#;(Qd^-Oj7Z|0;#=G(nVUE=Q3W;|2B6oLoHC+c?beJUwBHo zAr1p0V2f`DElbdqjZ4YN}< zqSp$@>l8?u5w`8_MT>9Sy`?;wN3a#!Lfdx7o>^?$ZEV}!g@5Wd48jEGb4g;`?qQ2| z*tBgomJ2kxZTG|HUAjeFc~F)WN)B%&_~?c0i&r%q$J0Ljf|Klwo!beE#0iaMVBcwH zu2}K$xB-h5zualSoC~<4$U98%T?Bg&53tIZI`NPJ^srM^$(Q|9;$(TQYvohIc|JFs zD@R;mf6pFbF6(%z3=d!-1S+Tv^`RELl;q1ys^8%GrUl~aO+?hS3iVhD1rs!uN&G%_ z!(bxz!b30AWZNq;r%n+?SI|HbUVx^WGHNBu^1F|`osEQp%3oY8=54FM4Twim!+2@r z!<)CEYf6Y{(cLWOQ5}{szv@&BDl8QXKDGsUu+TioLy%BtuoH346b7xlZ+EH%1NIU5 z>@93&eYLTMYnX|$m(Xlt3CV#a)piUSbgmFv0 zYL}m`SX5QLI}w_&vf!!#*W1d!%znYISZ~NsXuI|7)EnV{xamSDZMaoMhi3a9X|=Em zHjuHbhTZ_H!`OQzf`HzLUv>!pe_#y<`xlsX%9Nq)^S^Fj<;lOsHF}d6?VFX+j_Dd* zCFO99-hzk|dqhlEBg34|x0kxq7bfVf$kD#hk$QZ-MhLKPbS+}Icp_=-V%J_Mx(;QS z1DLb8bM$sWX`BP1I^Mwu+QqIVc|D5IL*I!27f*x`E}k%ao68I-?}#^4-Yp>SQ6O@U zL*41|`BWf0An01dem1-u_zAMJf{oU^ZL}-M=qA74wWfgg`0X7X#yJTC|I`MIOu}7j z;KH!i)!t#GjLqve_aA@qrlS)5&O6AggbQ9uw^X5Z{*KXc$6Y$#qW)9U|nty2<(1%emtmPvJ0=fmi zddr8^2ve!QvBgW(j|%XuRp8o#i;qb`%y{YJ$Z`4PUKZ)MA>~Y$MS72Jz1szc`^+a0 z3+N6>t3HDd#jJ~cD26`C_-!t85$`jf5|miA;nFv%<4#6UeWsq|T_}PUK8+xt&j_KU z8`i@NsXoK9e^x;5Rv^4V5$8je==Vrjj1T%8a$LT3AmV=>DF-(wuqr=Mi~j||Ve!9+ z7_Ns%T8m$MvFyu?-{vwSaq;gJlvuT)_)#5SVFWFHEy?>(1o6L$AfWq&P|^+SVTP3W zyej{ifP7tnq?3)Sa&=bDCogzwt=1PQ`Uc=@Sr$+C0U^`K@&>JzZ!%ub)2`-Q0EZY4 zA_(Z)LOkh)bu;54hF&LsN1(o|pqe)1_oOVw9(^A5Ei6dZ2I zhY$EEus`!*-0S_z5GZb#WcVpP~fvKY}2jp9!6$ z8&<>YRGaZp0r|NCN#BTOTpJ!m@k@XH00f@q%ftV0=_fhWN@2ueS1bVW{k2ueX537e067<1X#x=qwacj^ZbhOdR~A6 z{~C;=X@7$tpx=s^NjKC2H)f4#KPFJWQ&3G!`}a~7QxyFHIWFIl64U-8QqCMP?JZh& ze-a!v?Z*)d=m|;dF{;)Fd6MzlTxKM0+kY06Shb;TM|J##5%d^UOY*NMf(HJEAfUes zp`;tu!wf0oX1C@k0r`glNq54ytIkT~)u?ewjJSOf?73fD6?MZQ#^;8*(uEClr2%qN&Bz^zr_TAyH+6vSGdW+TVtJ9A=ibC7cO6@xod z>u#>#u))nkETFw4ozx#aJ5|55mX@RWOrgzXKH?^~w;;tT4oxnqWgkZ98H4D)D1;{V zLlDsZLMrKoH8E4l=-4khf>G}yWI!^IznKQbug%nS*;AXr=XL7@g)MhGz8`w1rfV^X17cV z>RC}lxQi`EIh4oG!bM10fei4l52ppvd zq?FX9VTc)KNSEB~a*z2m)F!bdqjZ4YO0V!3_fP z5(Og1sL~|i5zj{C*K#Jdwn@MnIm>F{PhvcEDjO%)i)^rUGJ=3M3z4K7*1?PjTiVw= zMW9YqP)(a=B558)oAe&oUcZLZ^N<9+2GXY@9(_&&GMJu`DuWHntH7Xi#vH&yXXG3PXY@Y9j}6 zJr(B=ubqlnbRH*M4tt(iO|%0@&~P3>K*RXe^Dwh7V@d6seV&I30$i*D*UrNwDTp;V zdB}13HoDjuA>}L(J1c0d`GUiCRz@tKilmeJqoC`WeH4^-GNCq?*@+w6h~UQR4-GD= zY!@T+{Bzo)fPr4lMi9_BLNV!v6*7y;@>m?l*JgLmm$aH=L4dU z^N@08i9$TBkn;tH6>viRLIG*B1TXhub$+^ zD1!KV5a1D_5W*uwhQ}!@0wq4r%C8iVOBIOR(5X8^$A{PWQe<>$CZ}`ivR|!Cm ze0}x#;*Kr{QK;zE2yj9yH6)+c8Xzevx9Uf{R;qekSQUSAOsk09h$|$&UJHr_4-cE;%1;XDWo?G(p3scNOEZd!*3C=t3xp5 z_=N4q9KTfx#^(4Ml!Ie-d<2WGMFv=Y8-jqY6KusYS5(I`I@h=qfZr~_?@+*L$&L7o zc{SGy*gF+i(_Z{8DT{GS??#Tx&v2v{zXvI2Kk3CI+VF1RJnqErMI@m2;TPkpyeH=` zC&!$c_cLUh%WTB^@Qs2It2FGxQ4Jqp1m)D!QT!lEApcDW0{W29NxES*%#L(n{vIt4 zVmAxahZR&)@joJEF;3_fkky@xvm0N%#ixaIy+iRC$*npRpG8_b6z6Lt z-i=IgDDFWdpw9_T^0_mK4)}SYCCa5QAg+CkFCt#+W3YmF5cv{^J7+*eb*~H|aiL^qMoHr-J-7Yx=4Xz8}Bl zG-UAZp-*3v%&&9iY=3lkpqTI1Sa?f_>^^#c3$ZB`dwMAJzA2^O;?lk9Q2PboK>_$S z1GL}^gR-&d(|08IyZGhS#52S2?IVA%mcEDVUUP0Fi~cydPu~}`AK;g{FY&!Hq8|$Q zv|KiP4EYe!8|Rtma#d#WmQYY1X4ILQ55?h+kr&WUxMX&&kj1xCGv$8uAw-{kDrk>z z;WS(fEyIp|`Wce3Pjusj8SeTNvWe5=JEjSG6y-(wIer8B1;d(iwLbg#yW)^?hflvm z2_9|ELw!Zc;=Aqq4OM*O94-O<3T3_K3>wS;0pD=1P@aB`Jad6lE|-q#?k?l@ji1Wd z<-t^uhPs{daK4*xWW5p1|>6DDdfb zk~_0p)&L&6y#8>XYpB7lw1z_L>Kv#f#Jr zJr()weZNXN*rCoN`d5INvsnNih)($BT>2YlPtMu{l_B~&Q!;1UxQ4|RgPUl+V`Dq( zDdb;b9*AqwrAm1$`u|`;{E{J;DbqO2{@@elg1c1xySUrYc;ul`XJsqHrT)l*eVTxL zbVhuuH&+-ehQJdQaO;RAjvctcQQI(064?3NZ2TOO8^0gl=*LHZVeXSri0?$(rLIGd zSe~URoSXwM5$}f4xhj6B#i>ksvN;8>c?NSsG)+iKo5Q$w=KNf_NYo;^^YPfs+R<+h zX%Pe({un`VotORJ86axQ-YU#yOM>J!+Dx1 z*|Y6kr6TMYZ;Wj!;3gNfO8#8d9{zlRe2G5P$}FKdT|I;D=e|O-1-O;FL(GupNban< z7NfbyHYZoggI%j>9>)_QDegpjDN4CQypd@>gS7}smk)LA&FNYF*_>|;paDW9?jyJ@ z7-(=xGnm^&`!X2!IBTGPB$q8av>$RdPBU9;jg68x) zm2rm8{Sn^+FVlg@-($9f7xB?n2O*=^Y!$nzYVlyno#Ep~WEs2YcoFWCfxJu18Eih$ z*l`H*%!9+RV`O9`RT{;MfP#!2S$k(Ufc_UgLep#X8EYhWjo7*1kjv5{j_G z8I9>(OuI5*&aa=v)C>C*BaPz+=00^Jjy+ENw#(vO7TPXfM^Sf49B072m{S+<4?BY5 z_Q-2-dEzZATv@Hj4zz3=%Pyeo-N_-R98JoZkR0U53AQ?Rz6>8C?{(J8TSR%dCf|R< zNBSPY_tBnmo|F%2$k$Zl3lj3ZB>857`rOS*r$cuBWwYKbF^4Y;0A`xix=8^Bl7IubS^p=G!(7_nC5NHCVpjn#N^xKEqj3^V6?@Pb; z?mhS1d(J)Q+;h%7_lAXKCoCti=SO`(=(v$VKdWvK8}hLAH_C;nTvV{y0=w$Q(=|7e zi&NQB7&aCxSWvSSwYn;jONNG9&=R?EQj<%EhCA>>xxQ;hu37eNuPR%IhWpSs=Ei0$ zT+`j_Vp6;0yJk()%&4rqT5MKAgTL6&9UPglQA^B1Z|8qlFNpM zm*JO8Ts56d)jl-bnFE+rH;U{rth-|Ck2!q_cFv8`fx#ID1vrNk}I ziR6)~JgOj;SVj}8juuO;7R{(*M61=2v8h+H=~&Tb^?^yfU{Y^`NxfiFuVzv&n1o(0 zLbqnpal)|LHH$JVtHm;FNyeZxqTO1??8!RUiVo05o6=h+`1r(XEep?gK(IVvM4GdO zB67<_wIQDanK&_(&xK4Vdn#j1m$krJ0vf?BxlSy!mSpWG4WEsT=(i%sNdLH9_1ylN z3deyf>IX&rlN);@XQCex*AI|Muk1$sSZaa~z4|^C#j@L48rx&UQ(3ThylWwlWr`rTh=l@7Ii)b0D-b2$HRP$_$!V!Py&$?+G`9wC$S+ghE{LDv6AGH;w4`JwB|&?$J(bT#v$dcxxmZuL?wB#FJVzX3V`_P>SZdS!E&2TC6$H_9 zekv~@dPuD=XSLo5jN3Iig6X($2F8qzI-Xr(uxao`Snd28B=ym z81fRVVRtGor8QiHHS7$`P&qE_LugKgYQmiIVo}%b9&naW{e4lQl>yv*@E zm|EK}mi(|hVaTh%psQ1P4Ke6sFsMt@IcRoes?@s;cH~mhrb74i+&Ki|)9&?WHc{U( z1FO82I4}Y&BP|HRNM5(!YW3}4EP)M`#oqG?$dGxkt=;&_(zanZrD zzC2&jgqC$bl~rORof@pWb-9uOcvWc@`fvv7Oz7(fYoKu+ED~g>R7RLY5Jq3?E6I4A z%7o^A0(0-7qc5ysm&w-Z(=37nzo+Q&1USxwbQHunBgZK67Wq3_>#rvWCVG zhMu%NpAPaKUc>g?J4yKx6#%_4I=X3Ki#ZgUJHjBw=`cE)5AJJXEbHCme2iV0@6=QQ zoPG456lLC3rGwOP7<-#aZ;LX?K9J3$m^j8FHB`h3|NIdo+~wEmR>YhK!BDG z<`-vLH&fQ+4dCmIseB30vhjE!v<^oxY#SR=`BIpX!QnPcSQkq9GK{$?l`p5I%;?c1 zkmVt*y*ZWp2xR-h1q-eeZU7}M`yK?`EYRWPD(IdUc#&`;6CMxzn#c>r0F`-MJvfZa zNQ8;+m=65p*fmIjvtG00!pU=4pIGPM57#^xSyEy*h!XCDwlebkq0k* z+Dw=@S`1b&={c@r2sm5o*>Y%pU=gkYQB}f=pulE#GGdz%m|le$2(KwenGxK>Y~jO2 znPof3ObZN`=_-Y2fovJP?|i{}vYb|h^T{ZOf#Z=q?K9cF>Y2?kZ<6?lHD_b6gTe;K zh{r1QeUn^9_8G#dAZIOYVrFa!LC~~gWji6duowX^B|}O});ow+Bi~+HbhcNRpil0f zZ3|y9SJXG}Ix4q?uY}Yd61+o#M{zCm@&6!ruY$F^C6%v+DH9}wH#hu}TT}TOvTzUa znG3ufnNy$_NJ_KwX{MeWP>h{eb4Vn@ztD2pyD;vTyXKS-dVZxq|HYa`Z zwX-$tb@OSOnQPkXp=k#f1Kxlge`6|dqa7z^+}haj+f(@_+VNYk9xxDEL(e1u|@#Tg&uN5Qj?rSjv%oV*eE z?u7?Q{_cIL`~-C^1$@Rca&kX8cLt0hC-+l8z6iizh5R&de-E~d9_3P zAb<`I_RG(K&d;av3&f~{hb%1xc_?vzD!)kdoq4nfX4kHNfk7Xyw93<{P-_Z3KiMA}HUBdMGyq))DVtg1R93z zj3ecVqj#nVo@8$4bJgsr(*E(LJzKdthxwM>&7~KODS4QZ2zmH>ct$ zS;X1y9-LbSer+1a%~r(5pK(&y1bba~g`FOqt4HpyVkbUqJ4S9CU|$z!6|(r0v%_r- zf@2yR)+m8x`*ZC4BXe!==3S@fcK*>BcK-X&oFAm}heUIeoqss9^N*$SN7SY5e8vj0 z^FN+D$2##a+4-MjcAkOd?*aesr}7Vggzfxe&368dr*cZk&j0rLDu0T_|6wZsh&b>p zUn|af>={WhT><_qm48f&z8egt1mq<(H`w8aiF56auv!fRd<3XW2;%74b5KJ;Zqk{C zp)U)2x3M#GaaJNxu$8i>3xp>z=GeAwy?E!4wddRn z2;oUL#$()kK*oB$eawR@zHa=Jg1BzKsNXG$Q8C{&;#s*+|0#s-{|~Y9&mdTTp31+- zwc=-u(E0IH{v~bQZP>ce^W8axF}p9={Zxf9=2;?+Yth#JeiUK!jlscAO4xmc@RzRH z`pRJyJ>?0$qNP%cUR*1}nSfmMSK;Kksz2i5k*J?g&+?v{|3_fL@NIlB#WPuVfnj)wV3YXxiIL!=(m;taiTo= z_roP{@0n+Y{D(PL+v~*RWCZ^a@<^3U2S$MW1?1wNQu)uhTs*hjU+?Z-**rv&{^jIBv8x0zq zgUdkkO*g7#_()YdBk3B~6O7X;>T0h#7^G8!aH4Y7y+3kKs9l*IR_eG@C#?`X^DUi+ zm=e$luK9LQK0e}D+dxzaxGFk#o&)CrH)p!De#1|`NF_{=M8_o_uJ1xRXQ!#_LUaOe-zfm!xu3|k{*-Xx`mi6+2=W^!Cob`P) zZeA_>Efds_g<)$3@R6#GOh(suMkQ@VV{0}s$Yu>POk1>r3n%jWgDjTw%A6U4>)=l1 z=F7o>kDMPR%6e52@SS1Fj>}YJ5mt~?LscN+D0?#W96)6i7g~o=zd)tN^OR!bH7g&8#b_XY}i1z5k@0HX;aaaxo6Ks zdK#*Zn(|OwfUt*(8K|<0A|SdL&FTvDdI^hNqdRU6kTM+e$jlbzQa-ng<|5AhKhbf^nElaN2h6Zr;Y<#5Zc|2MlC^QlDms~x)1ht)8FnR=|pHYEV z&&O0))ed~5>Kyzfm-iW;w!Q~hl&#L?`OnMduYl|Wh_zXxd$(y5JtN5GV3|Vffq4RI z9V<5)C%fruD?29>IjxuS0CG+)!?#pj&cD0(LCR^q?~8oh3u7SQaS zSUjL<#>_jZ&u|3|7@P;;mFNJ`!}v(mRg9)MdKW{_(%$nZ}gO|yJvuL|eg zL%Ig`KYJg7N*H|(_Q3a;pu>oYca>YUxp>BaGtYH|&G%aMg5D?!%Y+U^Gjtb6M@n|g z7^U@ohx`DR{6;|6~R7*qtI5S>wc zq{?Qtbd6_RBVlQXPKiOv8f5qv^N3EpybASEfl*$FFLgNO1XW2Fe@*!pd zAF1+~SzY59FG;Ypm{BY|&L9&SWcYB1Sy#3(z1y^%DNrJ}*?RKfB%u}NCkakzn=T!!WwEk@h;tuGNk9x)9rgEl$V){^F`@{6 ztyVPf<1ZC+V?vE9qUmZW^o#kZNKK-7L`_lW${Fwd7~fv*ou=L+W(_SezzZ1QdID%G zdof?%N9u*#n&L0P6~i9=4A+Bd;&|{RyP_ICLs9uS`sd^Q`TB~w?7OAQlR8BbY^HFH~dwBCh)V z>NNz>2ELVDe((2M{N9Jhoo)QZk ztsnv-3`7xr%?C5o+o`$5aZAaVx|1+jD_mSTZTTlD4gasGT;y7`u3B0QRc)q7yI9+*Dy2Tx$>)u{R)I`>&? zL29*b4H{xpLc8~ zinxbI$^1Grvd&EW43BMtp}~W{inmvNmcU5k>{4;c zb7G-Bhn8KfR_9D1Ri7uo7HiLWSkf2h`_k-DwYnd_M7OmZ*BUVjwn!iGsxVexMEgFg zZ49{>lssXtzJ!J$tCQub!No6gYkO413zwoy@C-XrUqRb`tDVHBZacn;HtU4Uc1%r8 z_0^`4#0l7TIPPRWfTMo(HG;wk5XAQ0qVUFqkN>fHfSMPRr=uREuWM#4YvcZ~HgVxZhf>)gG=qn>2$U$>4_1yy zeH)D<)={h}UVuA5DiwhK(#|>RJLuhKEyFEgz3IDXqVs`SS-0sA6YivB9Xb0H<7e&a zdo*YPKb=;O;Creb#ovNBPW)0FjUw6O@k;U-JP%&2u?Ve@D z*#Jo(c1S0LBqV_ll91ke59y8c-h1zbRKEXvv%6O$6ZrjrkM9dVv$r$z=FRIf`({R0 zEv!_7N;7i(uuu=Gc9`R0&8|l|wb#0<)xucqJ!JOPt(qOpHtbL>idD~0DYsz3f}mlO z6}dq@REtMT`=G{ZxCYt2ZH2bsxM38~tXi_Gv>%$nW}^{MWLE(&#eIy1HD#;bU8O_N zI%P+ZTc0w*$P$>Trdzd*$$*T=u??VAeY;A%4Cn`8XaQ}mHDcA?Zu?A(A>Gu{U8No# z(TPUDmEGzcwn@&6xGbA5lk9Q^40QP z0txhW5LcN_AXki*`dVnWAEQ=|mikdK*(6X*hVRy=L$zwOX|4>L6Jg{=5OGj1SMAAA z?Gvkg&4GqRpsIjj9g%~e)23MPXf zst1u>7I~?MkL+bGVGX&}P(3162Mjrj&0L#$4s@27`#_5ML^=6XIcuC=bH7YAXcpDv zMKuRGedZyFV)BV%@*?3p6q5&=c~;E9PR{Jj)M7YG^+=ggbQg7qv&`(#Qa;q_HwUyr z@~J`&a|X=inL1F->Nwn4ZZ2hIS!wnq=%9!}XO-EXs3EV_ zFw``AD~{_|AzHO=OdZ*6@}XT}?c~%^u=w?{%ERKxnn{#UFc+AMp)Ke|6`h6V;-voQ z(q?G9Xo82Z=vg(_E;cCefGaFEoJMi`G5N4MU4%as6E?%5rnBhQtM-lpg^@bOToPGR ztUR^BT#^u?jx}>u6p=g8j9?na#p;nmW^c9GV7O2nKV~ke_*NL={e&^IcLH`vY*;6b znR~MhoHV93I)j$kqv1~;Q>Qp9t){sM^03vWk%d5(c)8l_EOa1~M-6og1vcy3Idv*T za$2lTA94y}@)u}Nf~3!Iiq3lHXy+JbgV`g{YKsF}&m3}&A9GF^b50y{P8xGg9&0)n z(n$t7t9zVn_723F;V$HlzUFGRHCAV1SceBXtvO*eCmdGLNUY95wYfl#-;Iy#1~uz$ z1;H$*&Mh4Z9cALL1hraFFIIdP$_xvkRcrXR=wTG&ofoU~C!7t=vCPE<9bBjjokK0K zqAqflSsnar8**4gW~?q|5pm?XNUZk~kT(MHoCk~|mr@7DZn<3Q47YWD49K}GR+lsT zjL2-UKNhPiQub5)LUFkRzZ!QA7m6)sKq&4>H87zmuEeU!6muZFuqzPS7;>zg_Q?+1 z3GEriDbPM28V6jsvGSM;Mp+Rqx)lc3vZlytI5{;9N&2y>F%OGlShQ z+|Y;uOdMfoxOHO|G191zU4<@P%MT5!Ughv?G#iFxXdM@fvjWPs*lV)0uIH}CjO+VG z-NuY0h`5E_MX;Q9{#Ik!p53VGi6G7`tpPdRc0^%58HQas0Jn`;DVUJimocS?ttp&O zetV@Fi6Fm+1iMYJusv2YtT1NXpI2eN8)4@6%M*T3na-&lP}*#)9?MF*5mJIAjq@;H zp5X`0>gAd2PtN5I*U~aJLGCj~)-sg6ZQN*HRNlJf%+YOIw_RF3b9Br3=WQL`w%)La ztl6QQVGO2f8Z?DehsRFjt|k1y>FrV!B0z8y7xqpTOym1b{)Y_0c^YjkfNvz2*J|l@W-DsW(MxGU`XJZ(LQC_Db zCP$R(WAz*!{R)h}b&}nEkl4?~N;N5LM{QvN(In8Zf#VGY+W~G&!e#?r-Q_AlebSw3 z>Qx!y5g~5CCBlvBhBjuz@t->%GySP}ZUi$A0#(n0Y@Q#h7qD!Y!t1qc*l}-))eBiR zH$XO>?%8tTOw>bzI}sJJuCLoB`S7cI?U)?1X9B7wZrgH9IBjItt$MUIZA*(Sw@s}> z{PaabPQ2U6oYJ|{na}@Lq!NBHB>a%&P2CK|+!CvoKm+1^H+6X5t+9G(+QQz4g_d5M z+}2@K>SfMh;(af7`o;T}q|CoUd*AJ`dL`?aKZKIyTQZZWSHT)?O;F1s<<%Xi6X(ki zz6R2q2g_^0#_MABdJN()#@k!I_l8)#k;gs)V~++iJb%0VCB;*AWXtDWCrKOCo7!j+ zAA0kAH1(xSz6CVh2kdVJt#6Cf+nH7#=?yJkct@<>$+VuszK}==b5-IN`OSLqev$r$ zgfCRA$nt}!QFuT5!r)H*{iNUVy|N9TF7cquJgJPugx__qOgM{p?*@bSH%aO}5W{<8 z^*-<`miG=3L-*2bqUrl~RQB;LpHuHgxcES?SZOcdJ93^GHCrrwe9o5&lUK%?q?S{k0F9rF z)u&jEcL3A)c!!q8$7KzPRi!NT@Qsgnjy0oX1q=(z$_UwphK)~iUSYO1<`QKWx+aa# zII`9Zqa$VE8R5Iy*lzhvTh^Ve6^_C43)Y<$zUC*brXT6m=Y%bj3vwM*!grC5EL#jJupK+@OL&Ry2FEAYdBWu zhB@_J$n<-$`aV;z7))xXU_t(1kAnOm1hNRQ>_6&9VByEH`bjFk?`iqDPOd(r*$eaX3)F1*N}> z)$da&{ZgdlXyf9@SpdKvV)aKJL&|z=tmipN%`E^yyd? zNVeBTSnP^zxg@snH+2Pz>JSOwN@QS-$T?IFp_75{2Q!@Cour^9RpVLGu_GUxJEfT9 zAUBVf+e4ot@$N5krL-xFcYl?5cUP?b#_Zkv&}R>-`BQr^`};iHjr4HH`NxnL&p%`J zFXnAo7jJAp|DM~q=-kDf?}h=fxuvS61u%?juBN^46VpOGfm7B1|7^FWI=ZBx>cO!I z+FKL0NC@lEgq0qAI-r-k=EgcCSZ$<$D|U_+gS2&5(-NVxH=*-zR?5AwYAB2PxR1Gr z*C6+>Ibtrxm`!pi2XIkg>KwEbHFMTTTHmyc+lKD}FZBZg1~7o1n3m(27=Xtd>A2gM zu}dqYvODx+qXv5eIa-NU$YK?KV%i5!Hc;L~X>(;Wmtm{YzJS3L&BSgn7i%=M8kH=V z{TM;Yx0i59598K@to9rW?ay_+_}p1HCKCgS9Q^3vc!}u|crqbUtM%Cmfpmbt9GJk| z!ipQ;nXcDck#IcS2Qn`_$2g@3fIQcP2u=JMgRr;WsM^S8R5`Q-bz{bHD^3pOM&Kg7 zoFpjqM?$*L3kwEP5JAn2uot$T*PD`uOqYeg%Mr8DobcUBu5L&8Xr4|3Vd(^r3e23S z^s!Q`%D~nnD$+IV9+%*>2JGVdk>e;GB&^R#(x;)64rW-a9?8HR}l5&b3 zYqu?|(xF2A9A-!QU^;Y|G-76^!|@i=S}7M~f!ARaEVd)&#Z>fIN{noffAvO2W==GO zO4#K({KRx5o~g6(fb9X7V{{g9kJ7!?ORv1{B_SY%0HZu?bHYoaQ9(e93FrZA)O)sH z=H_SyO@dJSt|zSBlZeB6ye+GzV*o!l=48xo;O60u{Zn9~V^IzEkHb$)kHj-&pXn9$ zJsn`YB$6C2kSAzJ(L;ytd1?I618?xs&~YO7$U;X#ES-c}XKiQb*vNIuTcIOh!9TbT zO(%l}h~*Ug#Iy-dj#mE*gk)xrBBe8=19r=33*KTnQ_2N7On@9?a+rY5;;u!HM~5GC(u*@wG%V0< z!zIb74E>|I!*DmYt*C{h&&E$oBZ8ygB||V7u)(>LJ@Ochpjofx=p2B6;B%$>sP4|9 zXd@4s%LzE02LPt^d;t_%=>oi)%cGzYk!|N^u+arp=|a?sS!O$=2c%-U2u+o+;kfl3 z1;luLDkJHiqitvfK_-4;x){&Yp_oK1FKj96(dZJc13f$uVh6e2tW97MQ>9B$9$8@a zyY-6StlExS#U>g(2DQ7LVrexTB1`G$q?g{Yrpo|f=47uo4C?+YUCv$1McDtJpfTy* zxffzC!R8#fc1TyCb&Se*4vp%?iO#xYd*XT^8VAI+WZ^kl+J7EG(gPDTA%GPDloKXh z_^?Cn)0MoL}|CVEa86A^+Deh>c+^ybI4;FJjU;FZ`FL|JSHabi{)Lt=xS~Q1y2xo?XOS7yT_z;xMxPplNj_rP@|KP zbPYg3(zW=B>B-zrKD032E-7?#T=bZFUP+loPQyC632cJ3+ zdxs(La0$PQxC=dvD=_HO1-NZcdIoCX&H%zbHoS$Ni3+jCXW_+@5*xa~G(DTU&xuQN zk|jsiqY+d+2R||0z>xZz(qlRkYI+2gp35~L=SG3*3>Th`63^o%oU_5Y+PCTXD784G z7oaGlnTu+cTc?{)j_8GWit{k7@~bbQi9qyPX|PedZ>v^zO(YV~>!jUF;=$`t zis=n_X7S*SQs@?8D;~Uw+r7l~-z@##qWg8 zc2tPdzXLCxl-TLlvijc1-Mz%S--SX<@8+t+yP1?^z|^hZBhB-=^?SL=%iLLy{ytR7 zpq_T*_oFP1%;RT{tYJTZHYn$V_=)L5c&3i51)v>yEd;@Ii6eW7x85OLKCHWFZ~cfA z+9Z>Bs|XRSe-!QFtsj%VAJ=_Le-UrJfKS1(w<6)m$B&{}e79SX;RJp)Y`KiFwQ5_w zKZ~6oLulkKII@JT&uWfOtg>$#{GFzgs`d^!k%qj0-VK_;J}G-RFEDZ31P;q09>x>t7O3%;fp4xUlFNJQAx8lVYxIK**UzGk|()~+IV82O7hb|d+SH$?T0Jh<# zub{?UCd-7RCw&!F5;MMr7f(tYGmc`Ff1MGeG2xdjblukd= zowW3SEQK}+C6Pmf2?6{B?GibDDt&*Z`<8wrk>gx_;9Fvc%-0+naG2YjOq_GQfn)C$ z9JXd#b@iKv~{;t9IitRQv(O!9;wA2<(@c9heRJa3C9!|T&PWY{~fMIIVW zL@J+ij@YuRVw;BJ!~`M(_FyV*gU`gu*f?QcL!78^gaL{_Yy)N1Mrk^waMUN%d9w&Ywa_AP@rja}w%`X{v_`gR+zXHE7$zS6q zrr(HU3SJ^OHk&<0g)C7cU!1>{{=d`xVV&8DZ2iUgdueDBIsE}O&XBn7ANdvc3E;QK zrC*$Xk`AY0efwv;#q<{`>wM^0?ROjKuiU%nF#(B^B};RcbZ8qf`RvR__#1c7JLsJV z|BePo7Rn8;3cClF(MHMhfwHW0`hMS!c4bQ!6!=SZfVnpN%C`&mYYV7Px)Q2 zkb?pCMQJa*VLMF9h0;#=>iBSSD+Nac7S3Qkn>vA;ljgAVY;oF>wh z#Qh>6Z;o`ckJBFR#)V*+q|K!5j_n?{Mi}QA*1J3~OA0pO{wSnND9U z3Yem^p(}5_N+9>qkflq&;{#+Jtqiy@*2;kI%VTF5@I)B28nw>Bof+``xULVL&vT^? zDB=STLw!v9GagKkRrUe4@-5i~EA0XI&FXOzNNx>f_KwrEoyPufCTFg(KSJo3W1g9@ zAHdz%*bl@@Olt(Z;3e3Ymv&=6NSfzh8##eZ<6v$~jom;2n_dE+po6g=%H_n^`L5eM zg3;zZa$n8+o&`jQVc>bJnwe(O59i-uxqdefNwgNtu;U}}6Vo7`sU0(y^V#u`K(5n} zrP2Ft$M?+6=9c*i2QBkGl84O9H{qI&LalR1r}?hux}|f?H;?+53XBKl8>QykbtCcq zaZ5z}e8s^X0Xw}+m&NWPlQ-9#j~2S-7+_}3$8a|`=M8v?=~w|TcnLP^<7)V!JN`OJH>Kt4)C%Gvt|)*f2sE*!PY{Zu9)GxtOabQ)@% zHJ#>uI@c}fFn7I4$B~;qs&Gk?ta`{fKCvbjqpjSCtAlNKP6glD(mKMe`*w9L3&1%7a4rM%RoqD5 z6Q)tAJr7S_fSJqE>#lt9hR#PdF1TjAWOrlf0_k=ko=m;mBPO~?zYCb%(RPdps1|Rql9#+bbAap_Tso9Cr3lN48^vr zJTfP|fUD*%<`chRjxI-gKx25ubOpnjt5SKE`Hm*IoDQjs7SOp5#>0gO+z93qBsf^a zn`Gl?8#R~FWCa~?I~YeT$U>cYK(b(pf&dq_T|1gA1TS8~&yo242ny7') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/doc/_build/html/_static/down-pressed.png b/doc/_build/html/_static/down-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7ad782782e4f8e39b0c6e15c7344700cdd2527 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}Z23@f-Ava~9&<9T!#}JFtXD=!G zGdl{fK6ro2OGiOl+hKvH6i=D3%%Y^j`yIkRn!8O>@bG)IQR0{Kf+mxNd=_WScA8u_ z3;8(7x2){m9`nt+U(Nab&1G)!{`SPVpDX$w8McLTzAJ39wprG3p4XLq$06M`%}2Yk zRPPsbES*dnYm1wkGL;iioAUB*Or2kz6(-M_r_#Me-`{mj$Z%( literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/down.png b/doc/_build/html/_static/down.png new file mode 100644 index 0000000000000000000000000000000000000000..3003a88770de3977d47a2ba69893436a2860f9e7 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}xaV3tUZ$qnrLa#kt978NlpS`ru z&)HFc^}^>{UOEce+71h5nn>6&w6A!ieNbu1wh)UGh{8~et^#oZ1# z>T7oM=FZ~xXWnTo{qnXm$ZLOlqGswI_m2{XwVK)IJmBjW{J3-B3x@C=M{ShWt#fYS9M?R;8K$~YwlIqwf>VA7q=YKcwf2DS4Zj5inDKXXB1zl=(YO3ST6~rDq)&z z*o>z)=hxrfG-cDBW0G$!?6{M<$@{_4{m1o%Ub!naEtn|@^frU1tDnm{r-UW|!^@B8 literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/file.png b/doc/_build/html/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..d18082e397e7e54f20721af768c4c2983258f1b4 GIT binary patch literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP$HyOL$D9)yc9|lc|nKf<9@eUiWd>3GuTC!a5vdfWYEazjncPj5ZQX%+1 zt8B*4=d)!cdDz4wr^#OMYfqGz$1LDFF>|#>*O?AGil(WEs?wLLy{Gj2J_@opDm%`dlax3yA*@*N$G&*ukFv>P8+2CBWO(qz zD0k1@kN>hhb1_6`&wrCswzINE(evt-5C1B^STi2@PmdKI;Vst0PQB6!2kdN literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/fonts/fontawesome-webfont.eot b/doc/_build/html/_static/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..7c79c6a6bc9a128a2a8eaffbe49a4338625fdbc2 GIT binary patch literal 38205 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 E0QL`y%>V!Z literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/fonts/fontawesome-webfont.svg b/doc/_build/html/_static/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..45fdf3383 --- /dev/null +++ b/doc/_build/html/_static/fonts/fontawesome-webfont.svg @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/_static/fonts/fontawesome-webfont.ttf b/doc/_build/html/_static/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e89738de5eaf8fca33a2f2cdc5cb4929caa62b71 GIT binary patch literal 80652 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 qx{Kk=rVu5YQ9XR<GPS>b$X_& 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 z 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
      V$fDV|Iw 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 lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
      a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
      t
      ",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
      ",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
      ",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

      ",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
      ","
      "]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
      ").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/doc/_build/html/_static/js/theme.js b/doc/_build/html/_static/js/theme.js new file mode 100644 index 000000000..60520cc3a --- /dev/null +++ b/doc/_build/html/_static/js/theme.js @@ -0,0 +1,47 @@ +$( document ).ready(function() { + // Shift nav in mobile when clicking the menu. + $(document).on('click', "[data-toggle='wy-nav-top']", function() { + $("[data-toggle='wy-nav-shift']").toggleClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + // Close menu when you click a link. + $(document).on('click', ".wy-menu-vertical .current ul li a", function() { + $("[data-toggle='wy-nav-shift']").removeClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + $(document).on('click', "[data-toggle='rst-current-version']", function() { + $("[data-toggle='rst-versions']").toggleClass("shift-up"); + }); + // Make tables responsive + $("table.docutils:not(.field-list)").wrap("
      "); +}); + +window.SphinxRtdTheme = (function (jquery) { + var stickyNav = (function () { + var navBar, + win, + stickyNavCssClass = 'stickynav', + applyStickNav = function () { + if (navBar.height() <= win.height()) { + navBar.addClass(stickyNavCssClass); + } else { + navBar.removeClass(stickyNavCssClass); + } + }, + enable = function () { + applyStickNav(); + win.on('resize', applyStickNav); + }, + init = function () { + navBar = jquery('nav.wy-nav-side:first'); + win = jquery(window); + }; + jquery(init); + return { + enable : enable + }; + }()); + return { + StickyNav : stickyNav + }; +}($)); diff --git a/doc/_build/html/_static/minus.png b/doc/_build/html/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..da1c5620d10c047525a467a425abe9ff5269cfc2 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1SHkYJtzcHoCO|{#XvD(5N2eUHAey{$X?>< z>&kweokM_|(Po{+Q=kw>iEBiObAE1aYF-J$w=>iB1I2R$WLpMkF=>bh=@O1TaS?83{1OVknK< z>&kweokM`jkU7Va11Q8%;u=xnoS&PUnpeW`?aZ|OK(QcC7sn8Z%gHvy&v=;Q4jejg zV8NnAO`-4Z@2~&zopr02WF_WB>pF literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/pygments.css b/doc/_build/html/_static/pygments.css new file mode 100644 index 000000000..57eadc030 --- /dev/null +++ b/doc/_build/html/_static/pygments.css @@ -0,0 +1,63 @@ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #eeffcc; } +.highlight .c { color: #408090; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #007020; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #007020 } /* Comment.Preproc */ +.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #333333 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #007020 } /* Keyword.Pseudo */ +.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #902000 } /* Keyword.Type */ +.highlight .m { color: #208050 } /* Literal.Number */ +.highlight .s { color: #4070a0 } /* Literal.String */ +.highlight .na { color: #4070a0 } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ +.highlight .no { color: #60add5 } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #007020 } /* Name.Exception */ +.highlight .nf { color: #06287e } /* Name.Function */ +.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #bb60d5 } /* Name.Variable */ +.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #208050 } /* Literal.Number.Bin */ +.highlight .mf { color: #208050 } /* Literal.Number.Float */ +.highlight .mh { color: #208050 } /* Literal.Number.Hex */ +.highlight .mi { color: #208050 } /* Literal.Number.Integer */ +.highlight .mo { color: #208050 } /* Literal.Number.Oct */ +.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ +.highlight .sc { color: #4070a0 } /* Literal.String.Char */ +.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ +.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ +.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { color: #c65d09 } /* Literal.String.Other */ +.highlight .sr { color: #235388 } /* Literal.String.Regex */ +.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ +.highlight .ss { color: #517918 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ +.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ +.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ +.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/doc/_build/html/_static/searchtools.js b/doc/_build/html/_static/searchtools.js new file mode 100644 index 000000000..6e1f06bd1 --- /dev/null +++ b/doc/_build/html/_static/searchtools.js @@ -0,0 +1,622 @@ +/* + * searchtools.js_t + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilties for the full-text search. + * + * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + + + +/** + * Simple result scoring code. + */ +var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [filename, title, anchor, descr, score] + // and returns the new score. + /* + score: function(result) { + return result[4]; + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: {0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5}, // used to be unimportantResults + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + // query found in terms + term: 5 +}; + + +/** + * Search Module + */ +var Search = { + + _index : null, + _queued_query : null, + _pulse_status : -1, + + init : function() { + var params = $.getQueryParameters(); + if (params.q) { + var query = params.q[0]; + $('input[name="q"]')[0].value = query; + this.performSearch(query); + } + }, + + loadIndex : function(url) { + $.ajax({type: "GET", url: url, data: null, + dataType: "script", cache: true, + complete: function(jqxhr, textstatus) { + if (textstatus != "success") { + document.getElementById("searchindexloader").src = url; + } + }}); + }, + + setIndex : function(index) { + var q; + this._index = index; + if ((q = this._queued_query) !== null) { + this._queued_query = null; + Search.query(q); + } + }, + + hasIndex : function() { + return this._index !== null; + }, + + deferQuery : function(query) { + this._queued_query = query; + }, + + stopPulse : function() { + this._pulse_status = 0; + }, + + startPulse : function() { + if (this._pulse_status >= 0) + return; + function pulse() { + var i; + Search._pulse_status = (Search._pulse_status + 1) % 4; + var dotString = ''; + for (i = 0; i < Search._pulse_status; i++) + dotString += '.'; + Search.dots.text(dotString); + if (Search._pulse_status > -1) + window.setTimeout(pulse, 500); + } + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch : function(query) { + // create the required interface elements + this.out = $('#search-results'); + this.title = $('

      ' + _('Searching') + '

      ').appendTo(this.out); + this.dots = $('').appendTo(this.title); + this.status = $('

      ').appendTo(this.out); + this.output = $('
      '); + } + // Prettify the comment rating. + comment.pretty_rating = comment.rating + ' point' + + (comment.rating == 1 ? '' : 's'); + // Make a class (for displaying not yet moderated comments differently) + comment.css_class = comment.displayed ? '' : ' moderate'; + // Create a div for this comment. + var context = $.extend({}, opts, comment); + var div = $(renderTemplate(commentTemplate, context)); + + // If the user has voted on this comment, highlight the correct arrow. + if (comment.vote) { + var direction = (comment.vote == 1) ? 'u' : 'd'; + div.find('#' + direction + 'v' + comment.id).hide(); + div.find('#' + direction + 'u' + comment.id).show(); + } + + if (opts.moderator || comment.text != '[deleted]') { + div.find('a.reply').show(); + if (comment.proposal_diff) + div.find('#sp' + comment.id).show(); + if (opts.moderator && !comment.displayed) + div.find('#cm' + comment.id).show(); + if (opts.moderator || (opts.username == comment.username)) + div.find('#dc' + comment.id).show(); + } + return div; + } + + /** + * A simple template renderer. Placeholders such as <%id%> are replaced + * by context['id'] with items being escaped. Placeholders such as <#id#> + * are not escaped. + */ + function renderTemplate(template, context) { + var esc = $(document.createElement('div')); + + function handle(ph, escape) { + var cur = context; + $.each(ph.split('.'), function() { + cur = cur[this]; + }); + return escape ? esc.text(cur || "").html() : cur; + } + + return template.replace(/<([%#])([\w\.]*)\1>/g, function() { + return handle(arguments[2], arguments[1] == '%' ? true : false); + }); + } + + /** Flash an error message briefly. */ + function showError(message) { + $(document.createElement('div')).attr({'class': 'popup-error'}) + .append($(document.createElement('div')) + .attr({'class': 'error-message'}).text(message)) + .appendTo('body') + .fadeIn("slow") + .delay(2000) + .fadeOut("slow"); + } + + /** Add a link the user uses to open the comments popup. */ + $.fn.comment = function() { + return this.each(function() { + var id = $(this).attr('id').substring(1); + var count = COMMENT_METADATA[id]; + var title = count + ' comment' + (count == 1 ? '' : 's'); + var image = count > 0 ? opts.commentBrightImage : opts.commentImage; + var addcls = count == 0 ? ' nocomment' : ''; + $(this) + .append( + $(document.createElement('a')).attr({ + href: '#', + 'class': 'sphinx-comment-open' + addcls, + id: 'ao' + id + }) + .append($(document.createElement('img')).attr({ + src: image, + alt: 'comment', + title: title + })) + .click(function(event) { + event.preventDefault(); + show($(this).attr('id').substring(2)); + }) + ) + .append( + $(document.createElement('a')).attr({ + href: '#', + 'class': 'sphinx-comment-close hidden', + id: 'ah' + id + }) + .append($(document.createElement('img')).attr({ + src: opts.closeCommentImage, + alt: 'close', + title: 'close' + })) + .click(function(event) { + event.preventDefault(); + hide($(this).attr('id').substring(2)); + }) + ); + }); + }; + + var opts = { + processVoteURL: '/_process_vote', + addCommentURL: '/_add_comment', + getCommentsURL: '/_get_comments', + acceptCommentURL: '/_accept_comment', + deleteCommentURL: '/_delete_comment', + commentImage: '/static/_static/comment.png', + closeCommentImage: '/static/_static/comment-close.png', + loadingImage: '/static/_static/ajax-loader.gif', + commentBrightImage: '/static/_static/comment-bright.png', + upArrow: '/static/_static/up.png', + downArrow: '/static/_static/down.png', + upArrowPressed: '/static/_static/up-pressed.png', + downArrowPressed: '/static/_static/down-pressed.png', + voting: false, + moderator: false + }; + + if (typeof COMMENT_OPTIONS != "undefined") { + opts = jQuery.extend(opts, COMMENT_OPTIONS); + } + + var popupTemplate = '\ +
      \ +

      \ + Sort by:\ + best rated\ + newest\ + oldest\ +

      \ +
      Comments
      \ +
      \ + loading comments...
      \ +
        \ +
        \ +

        Add a comment\ + (markup):

        \ +
        \ + reStructured text markup: *emph*, **strong**, \ + ``code``, \ + code blocks: :: and an indented block after blank line
        \ +
        \ + \ +

        \ + \ + Propose a change ▹\ + \ + \ + Propose a change ▿\ + \ +

        \ + \ + \ + \ + \ + \ +
        \ +
        '; + + var commentTemplate = '\ +
        \ +
        \ +
        \ + \ + \ + \ + \ + \ + \ +
        \ +
        \ + \ + \ + \ + \ + \ + \ +
        \ +
        \ +
        \ +

        \ + <%username%>\ + <%pretty_rating%>\ + <%time.delta%>\ +

        \ +
        <#text#>
        \ +

        \ + \ + reply ▿\ + proposal ▹\ + proposal ▿\ + \ + \ +

        \ +
        \
        +<#proposal_diff#>\
        +        
        \ +
          \ +
          \ +
          \ +
          \ + '; + + var replyTemplate = '\ +
        • \ +
          \ +
          \ + \ + \ + \ + \ + \ + \ +
          \ +
        • '; + + $(document).ready(function() { + init(); + }); +})(jQuery); + +$(document).ready(function() { + // add comment anchors for all paragraphs that are commentable + $('.sphinx-has-comment').comment(); + + // highlight search words in search results + $("div.context").each(function() { + var params = $.getQueryParameters(); + var terms = (params.q) ? params.q[0].split(/\s+/) : []; + var result = $(this); + $.each(terms, function() { + result.highlightText(this.toLowerCase(), 'highlighted'); + }); + }); + + // directly open comment window if requested + var anchor = document.location.hash; + if (anchor.substring(0, 9) == '#comment-') { + $('#ao' + anchor.substring(9)).click(); + document.location.hash = '#s' + anchor.substring(9); + } +}); diff --git a/doc/_build/html/auth.html b/doc/_build/html/auth.html new file mode 100644 index 000000000..41fec27c3 --- /dev/null +++ b/doc/_build/html/auth.html @@ -0,0 +1,334 @@ + + + + + + + + + + Authentication — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Authentication

          +

          The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary.

          +

          You can force authentication, by calling:

          +
          $client->authenticate();
          +
          +
          +

          If the credentials are incorrect, a 401 error will be returned. If +credentials are correct, a 200 status is returned with your Service +Catalog.

          +
          +

          Service Catalog

          +

          The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the Catalog +object, which allows you greater control and interactivity.

          +
          /** @var OpenCloud\Common\Service\Catalog */
          +$catalog = $client->getCatalog();
          +
          +// Return a list of OpenCloud\Common\Service\CatalogItem objects
          +foreach ($catalog->getItems() as $catalogItem) {
          +
          +    $name = $catalogItem->getName();
          +    $type = $catalogItem->getType();
          +
          +    if ($name == 'cloudServersOpenStack' && $type == 'compute') {
          +        break;
          +    }
          +
          +    // Array of OpenCloud\Common\Service\Endpoint objects
          +    $endpoints = $catalogItem->getEndpoints();
          +    foreach ($endpoints as $endpoint) {
          +        if ($endpoint->getRegion() == 'DFW') {
          +            echo $endpoint->getPublicUrl();
          +        }
          +    }
          +}
          +
          +
          +

          As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/caching-creds.html b/doc/_build/html/caching-creds.html new file mode 100644 index 000000000..20807585c --- /dev/null +++ b/doc/_build/html/caching-creds.html @@ -0,0 +1,330 @@ + + + + + + + + + + Caching credentials — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Caching credentials

          +

          You can speed up your API operations by caching your credentials in a +(semi-)permanent location, such as your DB or local filesystem. This +enable subsequent requests to access a shared resource, instead of +repetitively having to re-authenticate on every thread of execution.

          +

          Tokens are valid for 24 hours, so you can effectively re-use the same +cached value for that period. If you try to use a cached version that +has expired, an authentication request will be made.

          +
          +

          Filesystem example

          +

          In this example, credentials will be saved to a file in the local +filesystem. Be sure to exclude it from your VCS.

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +    'username' => 'foo',
          +    'apiKey'   => 'bar'
          +));
          +
          +$cacheFile = __DIR__ . '/.opencloud_token';
          +
          +// If the cache file exists, try importing it into the client
          +if (file_exists($cacheFile)) {
          +    $data = unserialize(file_get_contents($cacheFile));
          +    $client->importCredentials($data);
          +}
          +
          +$token = $client->getTokenObject();
          +
          +// If no token exists, or the current token is expired, re-authenticate and save the new token to disk
          +if (!$token || ($token && $token->hasExpired())) {
          +    $client->authenticate();
          +    file_put_contents($cacheFile, serialize($client->exportCredentials()));
          +}
          +
          +
          +

          In tests, the above code shaved about 1-2s off the execution time.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/customizing-clients.html b/doc/_build/html/customizing-clients.html new file mode 100644 index 000000000..23fd45fe9 --- /dev/null +++ b/doc/_build/html/customizing-clients.html @@ -0,0 +1,398 @@ + + + + + + + + + + Customizing Clients — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Customizing Clients

          +
          +

          Logger injection

          +

          As the Rackspace client extends the OpenStack client, they both support +passing $options as an array via the constructor’s third parameter. The +options are passed as a config to the Guzzle client, but also allow to inject +your own logger.

          +

          Your logger should implement the Psr\Log\LoggerInterface as defined in +PSR-3. +One example of a compatible logger is Monolog. +When the client does create a service, it will inject the logger if one is +available.

          +

          To inject a LoggerInterface compatible logger into a new client:

          +
          use Monolog\Logger;
          +use OpenCloud\OpenStack;
          +
          +// create a log channel
          +$logger = new Logger('name');
          +
          +$client = new OpenStack('http://identity.my-openstack.com/v2.0', array(
          +  'username' => 'foo',
          +  'password' => 'bar'
          +), array(
          +  'logger' => $logger,
          +));
          +
          +
          +
          +
          +

          Authentication

          +

          The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary.

          +

          You can force authentication, by calling:

          +
          $client->authenticate();
          +
          +
          +

          If the credentials are incorrect, a 401 error will be returned. If +credentials are correct, a 200 status is returned with your Service +Catalog.

          +
          +
          +

          Service Catalog

          +

          The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the Catalog +object, which allows you greater control and interactivity.

          +
          /** @var OpenCloud\Common\Service\Catalog */
          +$catalog = $client->getCatalog();
          +
          +// Return a list of OpenCloud\Common\Service\CatalogItem objects
          +foreach ($catalog->getItems() as $catalogItem) {
          +
          +    $name = $catalogItem->getName();
          +    $type = $catalogItem->getType();
          +
          +    if ($name == 'cloudServersOpenStack' && $type == 'compute') {
          +        break;
          +    }
          +
          +    // Array of OpenCloud\Common\Service\Endpoint objects
          +    $endpoints = $catalogItem->getEndpoints();
          +    foreach ($endpoints as $endpoint) {
          +        if ($endpoint->getRegion() == 'DFW') {
          +            echo $endpoint->getPublicUrl();
          +        }
          +    }
          +}
          +
          +
          +

          As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs.

          +
          +
          +

          Default HTTP headers

          +

          To set default HTTP headers:

          +
          $client->setDefaultOption('headers/X-Custom-Header', 'FooBar');
          +
          +
          +
          +
          +

          User agents

          +

          php-opencloud will send a default User-Agent header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format:

          +
          +
          User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz
          +

          where xxx is the current version of the SDK, yyy is the current +version of cURL, and zzz is the current PHP version. To override +this default, you must run:

          +
          $client->setUserAgent('MyCustomUserAgent');
          +
          +
          +

          which will result in:

          +
          +
          User-Agent: MyCustomUserAgent
          +

          If you want to set a prefix for the user agent, but retain the default +User-Agent as a suffix, you must run:

          +
          $client->setUserAgent('MyPrefix', true);
          +
          +
          +

          which will result in:

          +
          +
          User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz
          +

          where $client is an instance of OpenCloud\OpenStack or +OpenCloud\Rackspace.

          +
          +
          +

          Other functionality

          +

          For a full list of functionality provided by Guzzle, please consult the +official documentation.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/debugging.html b/doc/_build/html/debugging.html new file mode 100644 index 000000000..1024ec86a --- /dev/null +++ b/doc/_build/html/debugging.html @@ -0,0 +1,375 @@ + + + + + + + + + + Debugging — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Debugging

          +

          There are two important debugging strategies to use when encountering +problems with HTTP transactions.

          +
          +

          Strategy 1: Meaningful exception handling

          +

          If the API returns a 4xx or 5xx status code, it indicates that +there was an error with the sent request, meaning that the transaction +cannot be adequately completed.

          +

          The Guzzle HTTP component, which forms the basis of our SDK’s transport +layer, utilizes numerous exception +classes +to handle this error logic.

          +

          The two most common exception classes are:

          +
            +
          • Guzzle\Http\Exception\ClientErrorResponseException, which is +thrown when a 4xx response occurs
          • +
          • Guzzle\Http\Exception\ServerErrorResponseException, which is +thrown when a 5xx response occurs
          • +
          +

          Both of these classes extend the base BadResponseException class.

          +

          This provides you with the granularity you need to debug and handle +exceptions.

          +
          +

          An example with Swift

          +

          If you’re trying to retrieve a Swift resource, such as a Data Object, +and you’re not completely certain that it exists, it makes sense to wrap +your call in a try/catch block:

          +
          use Guzzle\Http\Exception\ClientErrorResponseException;
          +
          +try {
          +    return $service->getObject('foo.jpg');
          +} catch (ClientErrorResponseException $e) {
          +    if ($e->getResponse()->getStatusCode() == 404) {
          +      // Okay, the resource does not exist
          +      return false;
          +    }
          +} catch (\Exception $e) {
          +    // Some other exception was thrown...
          +}
          +
          +
          +

          Both ClientErrorResponseException and +ServerErrorResponseException have two methods that allow you to +access the HTTP transaction:

          +
          // Find out the faulty request
          +$request = $e->getRequest();
          +
          +// Display everything by casting as string
          +echo (string) $request;
          +
          +// Find out the HTTP response
          +$response = $e->getResponse();
          +
          +// Output that too
          +echo (string) $response;
          +
          +
          +
          +
          +
          +

          Strategy 2: Wire logging

          +

          Guzzle provides a Log +plugin +that allows you to log everything over the wire, which is useful if you +don’t know what’s going on.

          +

          Here’s how you enable it:

          +
          +

          Install the plugin

          +
          composer require guzzle/guzzle
          +
          +
          +
          +
          +

          Add to your client

          +
          use Guzzle\Plugin\Log\LogPlugin;
          +
          +$client->addSubscriber(LogPlugin::getDebugPlugin());
          +
          +
          +

          The above will add a generic logging subscriber to your client, which +will output every HTTP transaction to STDOUT.

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/genindex.html b/doc/_build/html/genindex.html new file mode 100644 index 000000000..af30558df --- /dev/null +++ b/doc/_build/html/genindex.html @@ -0,0 +1,615 @@ + + + + + + + + + + + Index — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          +
            +
          • Docs »
          • + +
          • +
          • + +
          • +
          +
          +
          +
          + + +

          Index

          + +
          + A + | C + | D + | E + | F + | G + | I + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + +
          +

          A

          +
          + + +
          + +
          account +
          + + +
          addNodes() (global function) +
          + + +
          agent +
          + +
          + +
          agent token +
          + + +
          alarm +
          + +
          + +

          C

          + + + +
          + +
          cdn +
          + + +
          check +
          + + +
          claim +
          + +
          + +
          cloneDomain() (global function) +
          + + +
          configuration group +
          + + +
          container +
          + +
          + +

          D

          + + + +
          + +
          database +
          + +
          + +
          datastore +
          + +
          + +

          E

          + + +
          + +
          entity +
          + +
          + +

          F

          + + +
          + +
          flavor, [1] +
          + +
          + +

          G

          + + + +
          + +
          group +
          + +
          + +
          group configuration +
          + +
          + +

          I

          + + + +
          + +
          image +
          + +
          + +
          instance +
          + +
          + +

          L

          + + +
          + +
          launch configuration +
          + +
          + +

          M

          + + + +
          + +
          message +
          + + +
          metadata +
          + +
          + +
          monitoring zone +
          + +
          + +

          N

          + + + +
          + +
          network +
          + + +
          notification +
          + +
          + +
          notification plan +
          + +
          + +

          O

          + + +
          + +
          object +
          + +
          + +

          P

          + + + +
          + +
          policy +
          + +
          + +
          port +
          + +
          + +

          Q

          + + +
          + +
          queue +
          + +
          + +

          R

          + + +
          + +
          resource +
          + +
          + +

          S

          + + + +
          + +
          security group +
          + + +
          security group rule +
          + + +
          server +
          + +
          + +
          snapshot +
          + + +
          stack +
          + + +
          subnet +
          + +
          + +

          T

          + + + +
          + +
          template +
          + + +
          tenant +
          + +
          + +
          token +
          + +
          + +

          U

          + + +
          + +
          user, [1] +
          + +
          + +

          V

          + + + +
          + +
          volume, [1] +
          + +
          + +
          volume type +
          + +
          + +

          W

          + + +
          + +
          webhook +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/getting-started-with-openstack.html b/doc/_build/html/getting-started-with-openstack.html new file mode 100644 index 000000000..f1d051180 --- /dev/null +++ b/doc/_build/html/getting-started-with-openstack.html @@ -0,0 +1,469 @@ + + + + + + + + + + Getting Started with OpenStack — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Getting Started with OpenStack

          +
          +

          Installing the SDK

          +

          You must install through Composer, because this library has a few +dependencies:

          +
          composer require rackspace/php-opencloud
          +
          +
          +

          Once you have installed the library, you will need to load Composer’s +autoloader (which registers all the required namespaces):

          +
          require 'vendor/autoload.php';
          +
          +
          +

          And you’re good to go!

          +
          +
          +

          Quick deep-dive: building some Nova instances

          +

          In this example, you will write code that will create a Nova instance +running Ubuntu.

          +
          +

          1. Setup the client and pass in your credentials

          +

          To authenticate against Keystone:

          +
          use OpenCloud\OpenStack;
          +
          +$client = new OpenStack('http://my-openstack.com:35357/v2.0/', array(
          +    'username'   => 'foo',
          +    'password'   => 'bar',
          +    'tenantName' => 'baz'
          +));
          +
          +
          +

          You will need to substitute in the public URL endpoint for your Keystone +service, as well as your username, password and tenantName. +You can also specify your tenantId instead of tenantName if you +prefer.

          +
          +
          +

          2. Pick what service you want to use

          +

          In this case, we want to use the Nova service:

          +
          $compute = $client->computeService('nova', 'regionOne');
          +
          +
          +

          The first argument is the name of the service as it appears in the +OpenStack service catalog. For OpenStack users, this must be retrieved +and entered in your code. If you are unsure how to retrieve the service +name, follow these steps:

          +
            +
          1. Setup the $client object, as above
          2. +
          3. Copy and run this code:
          4. +
          +
          $client->authenticate();
          +print_r($client->getCatalog()->getItems());
          +
          +
          +
            +
          1. This will output all the items in your service catalog. Go through +the outputted list and find your service, making note of the “name” +field. This is the name you will need to enter as the first argument. +You will also be able to see the available regions.
          2. +
          +

          The second argument is the region. The third and last argument is the +type of URL; you may use either publicURL or internalURL.

          +
          +
          +

          3. Select your server image

          +

          Instances are based on “images”, which are effectively just the type of +operating system you want. Let’s go through the list and find an Ubuntu +one:

          +
          $images = $compute->imageList();
          +
          +foreach ($images as $image) {
          +    if (strpos($image->name, 'Ubuntu') !== false) {
          +        $ubuntu = $image;
          +        break;
          +    }
          +}
          +
          +
          +

          Alternatively, if you already know the image ID, you can do this much +easier:

          +
          $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5');
          +
          +
          +
          +
          +
          +

          4. Select your flavor

          +

          There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The ‘flavor’ of an instance is its +hardware configuration. So if you want a 2GB instance but don’t know the +ID, you have to traverse the list:

          +
          $flavors = $compute->flavorList();
          +
          +foreach ($flavors as $flavor) {
          +    if (strpos($flavor->name, '2GB') !== false) {
          +        $twoGbFlavor = $flavor;
          +        break;
          +    }
          +}
          +
          +
          +

          Again, it’s much easier if you know the ID:

          +
          $twoGbFlavor = $compute->flavor('4');
          +
          +
          +
          +
          +

          5. Thunderbirds are go!

          +

          Okay, you’re ready to spin up a server:

          +
          use Guzzle\Http\Exception\BadResponseException;
          +
          +$server = $compute->server();
          +
          +try {
          +    $response = $server->create(array(
          +        'name'   => 'My lovely server',
          +        'image'  => $ubuntu,
          +        'flavor' => $twoGbFlavor
          +    ));
          +} catch (BadResponseException $e) {
          +    // No! Something failed. Let's find out:
          +    printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse());
          +}
          +
          +
          +

          As you can see, you’re creating a server called “My lovely server” - +this will take a few minutes for the build to complete. You can always +check the progress by logging into your Controller node and running:

          +
          nova list
          +
          +
          +

          You can also execute a polling function immediately after the create +method that checks the build process:

          +
          use OpenCloud\Compute\Constants\ServerState;
          +
          +$callback = function($server) {
          +    if (!empty($server->error)) {
          +        var_dump($server->error);
          +        exit;
          +    } else {
          +        echo sprintf(
          +            "Waiting on %s/%-12s %4s%%",
          +            $server->name(),
          +            $server->status(),
          +            isset($server->progress) ? $server->progress : 0
          +        );
          +    }
          +};
          +
          +$server->waitFor(ServerState::ACTIVE, 600, $callback);
          +
          +
          +

          So, the server will be polled until it is in an ACTIVE state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output.

          +
          +
          +

          More fun with Nova

          +

          Once you’ve booted up your instance, you can use other API operations to +monitor your Compute nodes. To list every node on record, you can +execute:

          +
          $servers = $compute->serverList();
          +
          +foreach ($servers as $server) {
          +    // do something with each server...
          +    echo $server->name, PHP_EOL;
          +}
          +
          +
          +

          or, if you know a particular instance ID you can retrieve its details:

          +
          $server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx');
          +
          +
          +

          allowing you to update its properties:

          +
          $server->update(array(
          +   'name' => 'New server name'
          +));
          +
          +
          +

          or delete it entirely:

          +
          $server->delete();
          +
          +
          +
          +
          +

          Next steps

          +

          Read our docs for the Compute v2 service.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/getting-started-with-rackspace.html b/doc/_build/html/getting-started-with-rackspace.html new file mode 100644 index 000000000..9a5609fca --- /dev/null +++ b/doc/_build/html/getting-started-with-rackspace.html @@ -0,0 +1,435 @@ + + + + + + + + + + Getting Started with Rackspace — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Getting Started with Rackspace

          +
          +

          Installing the SDK

          +

          You must install through Composer, because this library has a few +dependencies:

          +
          composer require rackspace/php-opencloud
          +
          +
          +

          Once you have installed the library, you will need to load Composer’s +autoloader (which registers all the required namespaces):

          +
          require 'vendor/autoload.php';
          +
          +
          +

          And you’re good to go!

          +
          +
          +

          Quick deep-dive: building some Nova instances

          +

          In this example, you will write code that will create a Cloud Servers instance +running Ubuntu.

          +
          +

          1. Setup the client and pass in your credentials

          +

          To authenticate against the Rackspace API and use its services:

          +
          <?php
          +
          +require 'vendor/autoload.php';
          +
          +use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +    'username' => 'foo',
          +    'apiKey'   => 'bar'
          +));
          +
          +
          +

          You can see in the first example that the constant +Rackspace::US_IDENTITY_ENDPOINT is just a string representation of +Rackspace’s identity endpoint +(https://identity.api.rackspacecloud.com/v2.0/). Another difference +is that Rackspace uses API key for authentication, whereas OpenStack +uses a generic password.

          +
          +
          +

          2. Pick what service you want to use

          +

          In this case, we want to use the Compute (Nova) service:

          +
          $compute = $client->computeService(null, 'ORD');
          +
          +
          +

          The first argument is the name of the service as it appears in the +OpenStack service catalog. If in doubt, you can leave blank and it will +revert to the default name for the service. The second argument is the +region; you may use:

          +
            +
          • DFW (Dallas)
          • +
          • ORD (Chicago)
          • +
          • IAD (Virginia)
          • +
          • LON (London)
          • +
          • HKG (Hong Kong)
          • +
          • SYD (Sydney)
          • +
          +

          The third and last argument is the type of URL; you may use either +publicURL or internalURL. If you select internalUrl all API +traffic will use ServiceNet (internal IPs) and will receive a +performance boost.

          +
          +
          +

          3. Select your server image

          +

          Servers are based on “images”, which are effectively just the type of +operating system you want. Let’s go through the list and find an Ubuntu +one:

          +
          $images = $compute->imageList();
          +
          +foreach ($images as $image) {
          +    if (strpos($image->name, 'Ubuntu') !== false) {
          +        $ubuntu = $image;
          +        break;
          +    }
          +}
          +
          +
          +

          Alternatively, if you already know the image ID, you can do this much +easier:

          +
          $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5');
          +
          +
          +
          +
          +
          +

          4. Select your flavor

          +

          There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The ‘flavor’ of a server is its hardware +configuration. So if you want a 2GB instance but don’t know the ID, you +have to traverse the list:

          +
          $flavors = $compute->flavorList();
          +
          +foreach ($flavors as $flavor) {
          +    if (strpos($flavor->name, '2GB') !== false) {
          +        $twoGbFlavor = $flavor;
          +        break;
          +    }
          +}
          +
          +
          +

          Again, it’s much easier if you know the ID:

          +
          $twoGbFlavor = $compute->flavor('4');
          +
          +
          +
          +
          +

          5. Thunderbirds are go!

          +

          Okay, you’re ready to spin up a server:

          +
          use Guzzle\Http\Exception\BadResponseException;
          +
          +$server = $compute->server();
          +
          +try {
          +    $response = $server->create(array(
          +        'name'   => 'My lovely server',
          +        'image'  => $ubuntu,
          +        'flavor' => $twoGbFlavor
          +    ));
          +} catch (BadResponseException $e) {
          +    // No! Something failed. Let's find out:
          +    printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse());
          +}
          +
          +
          +

          You can also call a polling function that checks on the build process:

          +
          use OpenCloud\Compute\Constants\ServerState;
          +
          +$callback = function($server) {
          +    if (!empty($server->error)) {
          +        var_dump($server->error);
          +        exit;
          +    } else {
          +        echo sprintf(
          +            "Waiting on %s/%-12s %4s%%",
          +            $server->name(),
          +            $server->status(),
          +            isset($server->progress) ? $server->progress : 0
          +        );
          +    }
          +};
          +
          +$server->waitFor(ServerState::ACTIVE, 600, $callback);
          +
          +
          +

          So, the server will be polled until it is in an ACTIVE state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output.

          +
          +
          +

          Next steps

          +

          Read our docs for the Compute v2 service.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/http-clients.html b/doc/_build/html/http-clients.html new file mode 100644 index 000000000..6c9e22c14 --- /dev/null +++ b/doc/_build/html/http-clients.html @@ -0,0 +1,332 @@ + + + + + + + + + + HTTP Clients — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          HTTP Clients

          +
          +

          Default HTTP headers

          +

          To set default HTTP headers:

          +
          $client->setDefaultOption('headers/X-Custom-Header', 'FooBar');
          +
          +
          +
          +
          +

          User agents

          +

          php-opencloud will send a default User-Agent header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format:

          +
          +
          User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz
          +

          where xxx is the current version of the SDK, yyy is the current +version of cURL, and zzz is the current PHP version. To override +this default, you must run:

          +
          $client->setUserAgent('MyCustomUserAgent');
          +
          +
          +

          which will result in:

          +
          +
          User-Agent: MyCustomUserAgent
          +

          If you want to set a prefix for the user agent, but retain the default +User-Agent as a suffix, you must run:

          +
          $client->setUserAgent('MyPrefix', true);
          +
          +
          +

          which will result in:

          +
          +
          User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz
          +

          where $client is an instance of OpenCloud\OpenStack or +OpenCloud\Rackspace.

          +
          +
          +

          Other functionality

          +

          For a full list of functionality provided by Guzzle, please consult the +official documentation.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/index.html b/doc/_build/html/index.html new file mode 100644 index 000000000..aaca875a8 --- /dev/null +++ b/doc/_build/html/index.html @@ -0,0 +1,365 @@ + + + + + + + + + + Welcome to php-opencloud! — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Welcome to php-opencloud!

          +
          +

          Installation

          +

          You must install this library through Composer:

          +
          composer require rackspace/php-opencloud
          +
          +
          +

          If you do not have Composer installed, please consult the official docs.

          +

          Once you have installed the library, you will need to load Composer’s autoloader +(which registers all the required namespaces). To do this, place the following +line of PHP code at the top of your application’s PHP files:

          +
          require 'vendor/autoload.php';
          +
          +
          +

          This assumes your application’s PHP files are located in the same folder as +vendor/. If your files are located elsewhere, please supply the path to +vendor/autoload.php in the require statement above.

          +

          Read the Getting Started with OpenStack or +Getting Started with Rackspace to help you get started with basic +Compute operations.

          +
          +

          Note

          +

          If you are running PHP 5.3, please see our Using the SDK with PHP v5.3 guide.

          +
          +
          + + +
          +

          Help and support

          +

          If you have specific problems or bugs with this SDK, please file an issue on +our official Github. We also +have a mailing list, +so feel free to join to keep up to date with all the latest changes and +announcements to the library.

          +

          For general feedback and support requests, send an email to +sdk-support@rackspace.com.

          +

          You can also find assistance via IRC on #rackspace at freenode.net.

          +
          +
          +

          Contributing

          +

          If you’d like to contribute to the project, or require help running the +unit/acceptance tests, please view the contributing guidelines.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/iterators.html b/doc/_build/html/iterators.html new file mode 100644 index 000000000..edf391c30 --- /dev/null +++ b/doc/_build/html/iterators.html @@ -0,0 +1,515 @@ + + + + + + + + + + Iterators — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Iterators

          +

          Iterators allow you to traverse over collections of your resources in an +efficient and easy way. Currently there are two Iterators provided by +the SDK:

          +
            +
          • ResourceIterator. The standard iterator class that implements +SPL’s standard +Iterator, +ArrayAccess +and Countable +interfaces. In short, this allows you to traverse this object (using +foreach), count its internal elements like an array (using +count or sizeof), and access its internal elements like an +array (using $iterator[1]).
          • +
          • PaginatedIterator. This is a child of ResourceIterator, and as +such inherits all of its functionality. The difference however is +that when it reaches the end of the current collection, it attempts +to construct a URL to access the API based on predictive paginated +collection templates.
          • +
          +
          +

          Common behaviour

          +
          $iterator = $computeService->flavorList();
          +
          +
          +

          There are two ways to traverse an iterator. The first is the longer, +more traditional way:

          +
          while ($iterator->valid()) {
          +    $flavor = $iterator->current();
          +
          +    // do stuff..
          +    echo $flavor->id;
          +
          +    $iterator->next();
          +}
          +
          +
          +

          There is also a shorter and more intuitive version:

          +
          foreach ($iterator as $flavor) {
          +    // do stuff...
          +    echo $flavor->id;
          +}
          +
          +
          +

          Because the iterator implements PHP’s native Iterator interface, it +can inherit all the native functionality of traversible data structures +with foreach.

          +
          +
          +

          Very important note

          +

          Until now, users have been expected to do this:

          +
          while ($flavor = $iterator->next()) {
          +   // ...
          +}
          +
          +
          +

          which is incorrect. The single responsibility of next is to move +the internal pointer forward. It is the job of current to retrieve +the current element.

          +

          For your convenience, these two Iterator classes are fully backward +compatible: they exhibit all the functionality you’d expect from a +correctly implemented iterator, but they also allow previous behaviour.

          +
          +
          +

          Using paginated collections

          +

          For large collections, such as retrieving DataObjects from +CloudFiles/Swift, you need to use pagination. Each resource will have a +different limit per page; so once that page is traversed, there needs to +be another API call to retrieve to next page’s resources.

          +

          There are two key concepts:

          +
            +
          • limit is the amount of resources returned per page
          • +
          • marker is the way you define a starting point. It is some form of +identifier that allows the collection to begin from a specific +resource
          • +
          +
          +

          Resource classes

          +

          When the iterator returns a current element in the internal list, it +populates the relevant resource class with all the data returned to the +API. In most cases, a stdClass object will become an instance of +OpenCloud\Common\PersistentObject.

          +

          In order for this instantiation to happen, the resourceClass option +must correspond to some method in the parent class that creates the +resource. For example, if we specify ‘ScalingPolicy’ as the +resourceClass, the parent object (in this case +OpenCloud\Autoscale\Group, needs to have some method will allows the +iterator to instantiate the child resource class. These are all valid:

          +
            +
          1. Group::scalingGroup($data);
          2. +
          3. Group::getScalingGroup($data);
          4. +
          5. Group::resource('ScalingGroup', $data);
          6. +
          +

          where $data is the standard object. This list runs in order of +precedence.

          +
          +
          +
          +

          Setting up a PaginatedIterator

          +
          use OpenCloud\Common\Collection\PaginatedIterator;
          +
          +$service = $client->computeService();
          +
          +$flavors = PaginatedIterator::factory($service, array(
          +    'resourceClass'  => 'Flavor',
          +    'baseUrl'        => $service->getUrl('flavors')
          +    'limit.total'    => 350,
          +    'limit.page'     => 100,
          +    'key.collection' => 'flavors'
          +));
          +
          +foreach ($flavors as $flavor) {
          +    echo $flavor->getId();
          +}
          +
          +
          +

          As you can see, there are a lot of configuration parameters to pass in - +and getting it right can be quite fiddly, involving a lot of API +research. For this reason, using the convenience methods like +flavorList is recommended because it hides the complexity.

          +
          +

          PaginatedIterator options

          +

          There are certain configuration options that the paginated iterator +needs to work. These are:

          + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionTypeRequiredDefault
          resourceClassThe resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator.stringYes
            +
          • +
          +
          baseUrlThe base URL that is used for making new calls to the API for new pagesGuzzle\Http\UrlYes
            +
          • +
          +
          limit.totalThe total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the listintNo10000
          limit.pageThe amount of resources each page containsintNo100
          key.linksOften, API responses will contain “links” that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is images_links.stringNolinks
          key.collectionThe top-level key for the array of resources. For example, servers are returned with this data structure: {"servers": [...]}. The key.collection value in this case would be servers.stringNonull
          key.collectionElementRarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: {"keypairs": [ {"keypair": {...}} ] }. So in this case the collectionElement key would be keypair.stringNonull
          key.markerThe value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is id or name.stringNoname
          request.methodThe HTTP method used when making API calls for new pagesstringNoGET
          request.headersThe HTTP headers to send when making API calls for new pagesarrayNoarray()
          request.bodyThe HTTP entity body to send when making API calls for new pagesGuzzle\Http\EntityBodyNonull
          request.curlOptionsAdditional cURL options to use when making API calls for new pagesarrayNoarray()
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/logging.html b/doc/_build/html/logging.html new file mode 100644 index 000000000..8c8b9cddd --- /dev/null +++ b/doc/_build/html/logging.html @@ -0,0 +1,320 @@ + + + + + + + + + + Logging — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Logging

          +
          +

          Logger injection

          +

          As the Rackspace client extends the OpenStack client, they both support +passing $options as an array via the constructor’s third parameter. The +options are passed as a config to the Guzzle client, but also allow to inject +your own logger.

          +

          Your logger should implement the Psr\Log\LoggerInterface as defined in +PSR-3. +One example of a compatible logger is Monolog. +When the client does create a service, it will inject the logger if one is +available.

          +

          To inject a LoggerInterface compatible logger into a new client:

          +
          use Monolog\Logger;
          +use OpenCloud\OpenStack;
          +
          +// create a log channel
          +$logger = new Logger('name');
          +
          +$client = new OpenStack('http://identity.my-openstack.com/v2.0', array(
          +  'username' => 'foo',
          +  'password' => 'bar'
          +), array(
          +  'logger' => $logger,
          +));
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/objects.inv b/doc/_build/html/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..ce930f07253f1f2f9de4196ed1f57f251925e50a GIT binary patch literal 686 zcmV;f0#W@VAX9K?X>NERX>N99Zgg*Qc_4OWa&u{KZXhxWBOp+6Z)#;@bUGk#XmBlW zaAj^|Y;SdB3L_v^WpZ8b#rNMXCQiPX<{x4c-p0w&2HN;49D+z3Ipp^3wG^!mmM|? z*b~qa6Kjz~S0CxpryngPZH!tjtR4ac5`VNv{z=J_(~-P($L@t3b|I?Ah~9SMTNluu zwZyQuuSR?A6#n}2R)%+fPt-5 zn@C4dAGCz1$#tUvx7o|R2a!F9T|~?OVnqPt<$xjjj4Yq3gxi!0ZQ)q?up~RCxSai) zAR6QnBFIy7sP_eEsi0J@QK^#LK_62&+*-?3qn~+r?Yd{|2ck+wAVmriOeK|UR%_X{ z>T$_kY)YTUK*`HysYW_sr2ty-p?DlEauy>@d2Os+33%n>D`96=6n5%W2esU-Mo`K< z7tzL&XBTr7vEz|^+ZZf|ZIZ!AM$1c!WVPe|cd})0l*?fAMZuD+&6^LRG?1JeK3B8i z<6Y4#hEN^6D_6cWpzU^wTmUOf9c1ODZpC)Ig7BV#kZPZ4~?NC#fmCAJfu~N7w>%TfGv+ zG$8O4pBt97tJ$Ao@SEk!a^&~xcd{(5ZXCwP1B*Q?{5~4TzibkUhdhv#`b7#ul`;fM U^!!reS>g9?vVEif1Byo=;WNWSQ~&?~ literal 0 HcmV?d00001 diff --git a/doc/_build/html/regions.html b/doc/_build/html/regions.html new file mode 100644 index 000000000..e4d0c94f3 --- /dev/null +++ b/doc/_build/html/regions.html @@ -0,0 +1,325 @@ + + + + + + + + + + Rackspace regions — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Rackspace regions

          +

          Below are the supported regions on the Rackspace network:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          codelocation
          IADVirginia
          ORDChicago
          DFWDallas
          LONLondon
          SYDSydney
          HKGHong Kong
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/search.html b/doc/_build/html/search.html new file mode 100644 index 000000000..332cba474 --- /dev/null +++ b/doc/_build/html/search.html @@ -0,0 +1,297 @@ + + + + + + + + + + Search — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          +
            +
          • Docs »
          • + +
          • +
          • + +
          • +
          +
          +
          +
          + + + + +
          + +
          + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/searchindex.js b/doc/_build/html/searchindex.js new file mode 100644 index 000000000..2dee3f4ab --- /dev/null +++ b/doc/_build/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({envversion:42,terms:{getetag:63,four:[15,57],"catch":[71,17,39,34,91,92,63],prefix:[63,5,0],oldest:[85,41],whose:[],authorit:64,collation_serv:81,cloudloadbalanc:[],under:[],spec:[17,92],digit:[65,32],everi:[71,50,0,17,94,78,67,25,63,74],addsubdomain:60,"50mb":[],monitoring_zones_pol:52,tenni:85,getcontainercount:38,affect:60,"377341426ac5":5,macaddress:13,volumelist:19,planid:78,previou:[56,29,68,86,34,63],correct:[88,63],getchecktyp:52,nwww:[],bulkextract:63,ef08aa7a:[],settempurlsecret:6,repres:[56,34,58,7,60,18,63,67,5,32,70,15,27,37],terminolog:63,direct:[97,5,74],second:[85,75,17,93,68,96,86,52,34,22,41,14,92,63],even:[60,91],image_schedul:34,getendpoint:[3,77,88],check_id:30,errorpagecont:25,calcul:14,dataobject:[56,63],accountobjectcount:[],roleid:99,"new":[],net:[28,60],groupid:[8,45,23,33],metadata:[],abov:[28,29,50,71,17,93,96,9,80,25,90,24,22,63],domainlist:60,error:[],here:[26,71,37,63,90],snnrespons:[],"4xx":71,path:[28,15,63,6],interpret:99,anymor:[],datetim:[85,41,34],loop:29,permit:57,aka:29,jami:[11,75,63],unix:[2,14],securetrafficonli:26,printf:[29,57,17,60,92,98,76,33,77,4],total:[],"byte":[],unit:[2,28,30],sys_type_nam:3,describ:[],would:[29,56,36,37,69,26,41,15],snapshotlist:21,emailaddress:[60,11],recommend:56,type:[],until:[29,17,85,56,52,69,92,74],relat:[3,61,63,64],describedbi:15,notic:63,warn:[30,78],phone:63,exce:[65,85],regionless:[],localarchivefilenam:[],kskei:[],hold:[11,69],partsiz:63,"4b40":[],must:[0,94,67,78,2,24,25,26,27,4,28,85,75,57,58,6,7,60,61,55,63,80,30,5,8,83,33,74,40,84,97,69,35,9,65,36,13,98,90,14,92,16,76,56,43,17,93,45,18,96,19,20,52,46,21,59,41,77],join:28,restor:68,portrangemin:5,work:[],ntbbbb:78,conceptu:60,snapshotid:21,root:[],dns1:60,overrid:[57,63,0],"7a9ad27c0866":81,smtp:52,older_fil:63,secgroupid:12,getcriticalst:78,validatetempl:39,want:[],end:[80,29,56,57,0,60,63,67,25,26,16,74],opencloudrackspac:[],turn:[57,63],faulti:71,how:[71,52,24,26,85,57,3,77,61,39,35,65,38,15,63,10,17,18,99,41,48,60],theuserspassword:[],hop:48,rewind:[],verifi:65,logos_copi:[],config:[],connect_timeout:[81,58],updat:[],"41a6":[],setusernam:11,after:[29,75,17,96,85,34,24,63,40],befor:[],"49c5":[],alloweddomain:9,parallel:24,averag:[35,58],volumedeleteontermin:34,attempt:[56,78,11,69,4],third:[42,17,96,24,92,63,64],unclaim:41,opaqu:[85,32],"43cc":5,exclud:[67,85,50],hooladomain:60,receiv:[22,29,96,57,92],maintain:[],environ:[59,97],enter:[17,22],exclus:52,order:[24,25,55,27,4,85,75,76,58,60,78,80,30,16,8,33,98,9,56,90,14,15,63,94,43,93,45,96,20,34,41,77],oper:[],feedback:28,diagnos:78,offici:[28,0,68,20,86,52,34,3,15,63,4],filesystem:[],becaus:[56,58,17,77,92,86,90,91,41,14,15,63],jpeg:[],connectionlog:94,london:[79,92],zgggb4rmymif:[],flexibl:85,vari:[],publicurl:[],getev:66,objectmetadata:[],uuid:65,fit:[60,34],fix:57,cred:11,easier:[17,77,92],them:[57,7,60,35,78,52,34,3,63,74],thei:[85,42,57,6,60,78,56,63,32,90,91,14,15,27,74],isdis:30,fragment:63,"8gb":29,"break":[17,29,60,88,92],listkeypair:82,manifestfil:63,isen:[11,62],choic:81,exportcredenti:50,setdescript:99,ntaaaa:78,timeout:[58,52,17,26,24,92,4],each:[],debug:[],metahead:[],mean:[71,78,34],memoryinfo:3,capac:97,logo:[],collector:48,oldcontain:24,network:[],goe:78,foo_bar:[],newli:34,setemail:11,dsl:30,sprintf:[17,78,92],removerol:99,forth:18,jamie_dev:51,nodetyp:96,navig:[],uploaddirectori:57,situat:91,free:[24,28,31],standard:[57,68,78,56,52,63],silent:98,filter:[],renew:41,curlopt_progressfunct:[],onto:34,cpuinfo:3,rang:[14,9,67,5,34,48,63],independ:[29,57,78],"81c1":23,networkintinfo:3,restrict:74,hook:78,unlik:63,alreadi:[11,17,96,78,82,41,92],logplugin:71,getmemb:49,nptechnicalcontactsemail:78,primari:[2,60,11,96,95],attemptsbeforedeactiv:4,listev:66,top:[56,28,15],sometim:[2,52],eb60583c:[67,13],master:[39,40],too:[59,71],hadptrrecordsinterfac:90,similarli:63,scalingpolici:[56,23],john:[],listen:[26,29,20],iosstreamingurl:[],namespac:[17,28,92],tool:[75,78],incur:31,getimag:[91,49,51],technic:78,target:[],provid:[71,49,0,59,78,2,52,53,25,74,29,75,56,58,7,60,61,82,83,84,34,35,88,10,85,91,63,86,11,68,18,96,97,69,41,77],"26dc":[],my_amazing_contain:57,project:[65,28,32],"5399cd36":[],minut:[17,40,41,14,6],"51db6f78c508f17ddc924357":85,createaccesslist:9,provis:[97,34],createmessag:85,imageref:23,ram:[],mind:[11,57,63,6],getrequest:[17,71,92],raw:[70,39,40],aforement:[],manner:34,rax:34,recreat:40,latter:57,accountcontainercount:[],client:[],queuesservic:69,expens:83,remotefilenam:[],nsrecord:60,general1:40,usernam:[],object:[],what:[],regular:[83,27],specifi:[],letter:[65,60],getprivatekei:82,phase:37,hide:[56,49],min5:14,deviceown:13,tradit:56,blacklist:9,don:[17,71,92],doc:[],alarm:[],clock:[85,41],instancelist:81,doe:[71,42,57,60,88,63,26,13,34,24,91,27,46],wildcard:9,getprocessvers:3,routabl:[29,74],api:[],opposit:[48,64],random:6,advisori:52,messageid:85,getcheckid:30,enablecdnlog:75,involv:[86,56],servererrorresponseexcept:71,explain:[],apach:[94,63],busi:63,tar_gz:63,getpartialobject:63,"49bd":[],invalidtemplateerror:39,stop:[56,52,41],getcdn:[],bar:[11,42,76,50,17,45,92,65,91,60,15],notificationplan:78,baz:[17,76],method:[],new_queu:65,respond:[52,41,74],previewstack:40,result:[],"123abc":[],respons:[],fail:[17,98,96,52,34,24,41,92],themselv:[49,64],createent:37,flavorid:[86,29,34],best:[41,34],awar:[57,63,98],npaaaa:[],singleitem:78,ac6bb468c5bf:[],simplest:[68,63],awai:[55,63],irc:28,loadbalanc:[],createsymlinkfrom:[],cloudmonitoringservic:[],accord:[82,60,14,63],extend:[71,91,41,42],var_dump:[17,92],monitoringservic:[48,35],pnrr0lre:[],extens:[],protect:[15,97],accident:65,expos:57,howev:[56,29,35,78,86,52,21,34,24,22,48,27],against:[49,17,95,51,88,52,24,92,74],cloudqueu:[],logic:[7,30,71],countri:48,seri:[85,78,73],com:[28,11,42,17,60,78,52,75,98,26,90,48,92,39,40],computeservic:[17,59,19,56,90,92],can:[95,49,50,1,78,2,3,93,62,24,26,27,74,28,29,75,56,57,58,6,7,60,61,55,77,63,81,69,30,39,59,83,64,84,68,35,9,98,10,87,66,12,13,85,91,65,14,15,5,86,11,52,17,44,18,96,19,40,46,97,34,99,36,92,41,88,67,37],foobar:[3,27,0],a2cc:[],serverthre:96,guid:[],assum:[99,28,85,96],duplic:60,summer_vac:63,entitybodi:[56,63],getscalingpolici:[33,45],three:[53,96,91,63],been:[29,11,49,56,75,25,24,63],servertwonod:26,trigger:[52,35,78],debug_info:[52,78],basic:[],min240:14,a0a1:78,addrecord:60,new_email:11,deeper:64,xxx:0,worker:[41,69],search:[60,98],ani:[29,75,49,58,68,60,69,96,35,65,52,13,85,32,26,41,46],accesslist:9,lift:63,child:56,"86df":[],getaccount:[38,6],spin:[17,92],enablerootus:27,ident:[],adress:67,servic:[],properti:[],container_2:63,container_1:63,apikei:[47,74,97,50,7,18,35,61,2,72,69,89,32,83,59,92,70,64],dashboard:3,publicli:75,"7e4a":13,privatekei:20,spawn:[],"7704fb98ab1":[],cpumetr:3,securitygroupid:5,transferbuild:63,undelet:41,setid:[99,11],sever:[14,64],hong:[79,92],cntl4qsiba:[],perform:[],make:[71,85,49,17,77,95,78,56,32,24,41,14,74],demoauthor:[],weighted_least_connect:96,complex:56,backoffplugin:24,fefcaa640216:[],complet:[],testnewcheckparam:52,raid:97,disablecdn:75,fairli:15,rais:24,portal:60,refin:[86,68,63],deletetag:51,tune:[],kept:34,abandonstackdata:40,scenario:57,flavorref:23,inherit:[56,99],contact:[78,6],ubuntuimag:[],thi:[72,71,49,50,0,67,3,93,25,26,27,4,29,75,56,57,58,6,77,78,55,92,80,81,30,5,8,82,33,98,9,10,12,13,38,91,14,15,16,76,86,11,43,17,44,94,96,20,68,52,46,34,99,23,88,37],gzip:[],everyth:[71,63],left:[60,34,57,6],identifi:[56,85,35,65,53,13,70],just:[57,17,86,24,92,63],enableconnectionlog:94,bandwidth:[24,86,34],human:[67,12,13,46],yet:63,languag:30,previous:34,easi:[56,57,16,74],requestor:26,maximumtotalsizeofobjectsallowedincontain:[],els:[17,92,63],save:[68,95,82,50,34],explanatori:[],lbservic:29,applic:[28,34,10,97,69,15,63,74],preserv:40,openstack:[],shadow:20,claimmessag:41,daemon:35,orchestrationservic:[18,36,1,39,40],specif:[],arbitrari:[85,65,52,30,37,34],manual:[60,63,34],remind:49,badresponseexcept:[17,71,92],unnecessari:34,grouplist:23,www:60,right:[56,99,60,32],old:[24,63],deal:41,getbytesquota:57,percentag:68,intern:[24,56,92,31,34],stacktempl:40,ccccc:[],txt:63,file_get_cont:[82,50,39,40],getheaderlin:[],eleph:[],middlewar:57,getcontain:[24,57,63,6],condit:[],foo:[71,11,42,76,50,17,45,92,65,90,91,60,15],pvhvm:40,core:3,plu:41,"89b3":5,newtoken:3,post:[],plug:7,postgresql:52,hasconnectionlog:94,slightli:77,simul:[],coars:14,produc:[66,18,49,39,69],soa:60,getiosstreaminguri:75,nrespons:[17,92],encount:[52,71],encod:[70,57,63],end_mark:57,down:[60,13,46],wrap:[71,34],getagenthost:3,storag:[86,85,75,97,68,19,2,21,83],hallo:75,wai:[56,85,96,2,34,22,91,15,63],strtotim:60,support:[],nova:[],crecord:60,avail:[49,78,72,54,26,74,75,3,6,59,60,61,64,35,88,14,52,11,42,67,17,44,68,97,99,41],jane:[],call:[71,52,24,74,85,75,57,60,15,40,98,88,65,10,92,56,11,17,95,96,68,34,99,77],updatestatu:49,head:[],form:[56,71,90,13,15],sub1:[],forc:[88,74],getsecuritygroup:12,getwarningst:78,cidr:[7,5,67],icmp:5,"true":[65,85,57,63,0,68,78,60,16,67,20,86,52,13,34,26,48,94,27,46],freenod:28,filesystemmetr:3,getpassword:11,createus:11,maximum:[63,11,58,5,61],tell:[24,57,6],new_object_nam:63,"4bb8":[],fundament:[],entityid:[30,14,37,78],unrel:48,enablelog:57,"08z":34,featur:[24,25,54,3,74,75,76,58,6,78,80,30,16,4,9,86,14,63,43,93,94,96,20],volume_type_id:19,"abstract":48,mirror:57,backoff:24,glanc:15,metadatalist:76,check:[],us_identity_endpoint:[47,74,97,50,7,18,35,61,2,72,69,89,32,83,59,92,70,64],assembl:[18,39],arecord:60,listcontain:[75,57],"42fe":[17,92],connectionid:3,when:[71,78,52,25,24,26,74,29,75,57,7,60,61,63,81,30,16,82,40,35,88,56,36,85,14,92,39,11,42,17,18,96,20,34,99,41,77],guzzlephp:[],roll:[41,14],node:[],notif:[],subdivid:78,consid:[60,14],marconi:[],file_put_cont:[50,40],faster:24,furthermor:85,anywher:31,deleteallobject:57,ignor:[85,41],time:[58,73,57,50,6,68,60,69,78,52,75,63,97,32,24,83,74,14,16,40],push:[3,35],backward:56,concept:[],skip:41,consum:[11,49,32],focus:3,getcountrycod:48,getsubnet:67,hierarch:[],depend:[84,97,17,7,60,18,35,61,2,52,69,85,32,24,83,92,59,14,77,74],zone:[],getrecordedcheck:78,getconnect:3,intermedi:20,autoscaleservic:61,text:57,listresourc:87,compart:2,sourc:[80,48,15,34],string:[71,67,25,85,56,57,6,60,78,92,82,5,32,40,98,65,12,37,15,63,10,11,52,96,46,34,36,70,48,13],could:[90,25,78],logexcept:[],"8d5c":[67,13],fixedip:13,brows:15,bbbbb:[],cloudimag:[],hour:[],administr:[11,13,46],level:[56,15,27,64],iter:[],item:[],team:[35,78],cooki:80,x0r:[],round:96,shave:50,demo_db:[],slower:86,deviceid:13,cost:[],dnsservic:[90,64],port:[],getobjectcount:[57,38],getipaddress:37,c55c4705a286:[],repli:[],rollup:14,warning_st:78,containersizeinbyt:[],boost:92,urltyp:[84,7,18,35,61,2,69,97,59,83,63,74],deriv:85,process_vers:3,stmp:52,gatewayip:67,satisfi:52,address:[],clonedomain:60,along:[60,88],getprivateurl:77,"860d":[],wait:[],listnetwork:46,groupconfigur:23,ini_set:24,shift:22,images_link:56,objectcont:[],gettokenobject:50,persistentobject:56,queue:[],behav:32,commonli:[52,35],regardless:[56,57,40,49,6],extra:34,modul:[],prefer:17,getbodi:85,toarrai:65,rackspace_u:[],visibl:[15,20],marker:[86,68,85,56,57],instal:[],regex:37,httpd:63,file_exist:50,memori:[24,3,97,63],visit:61,hasqueu:65,"57ea":[67,13],"0d589460":23,criteria:[60,30],scope:[57,34],testexist:78,peopl:57,finit:32,supported_platform:52,unseri:50,easiest:34,subschema:15,b5e4:[],prepar:91,uniqu:[85,11,65,67,12,13,97,34,91,74,15,63,46],getlastmodifi:63,subet:7,whatev:34,"20902aec0e74":[],purpos:[95,64],heart:61,encapsul:88,predict:56,container_55:57,critic:[30,78],spl:[56,91],setuserag:0,resourceclass:56,occur:[71,91,15,78],gettyp:[3,52,77,78,88],alwai:[17,7,35,52,31,34,91],multipl:[],uptim:52,container_quota:57,write:[24,17,92],targettyp:60,my_secret:6,foreach:[49,52,53,62,73,26,29,75,56,57,3,60,78,55,81,30,63,82,33,40,98,85,9,86,87,66,36,13,90,91,14,92,5,76,65,11,58,67,17,44,95,96,19,68,46,21,93,99,12,23,88,77],fourth:64,"4bc5":81,parameter:[18,39],map:[80,48,32],product:[55,27],network_interfac:3,abstractschemaresourc:91,mac:[7,13],mai:[29,49,6,17,48,60,34,20,52,32,99,83,91,14,92,57],underscor:65,data:[],subsequ:[49,50],waitfor:[17,29,60,92],rather:[63,6],"switch":7,preced:[58,9,56,67,13,46],combin:[2,75,97,95],getlastconnect:3,filedata:[],setdefaultopt:0,getnotif:78,anticip:63,midnight:[],virtualip:[93,29],increas:[29,97],ttl:[],still:[22,52],pointer:[56,15,64],dynam:[67,91,97,15,63],entiti:[],sata:83,polici:[],platform:52,window:2,mail:[28,60,75,64],main:[7,78,6],bootablevolum:34,averagenumconnect:58,initi:[96,34],bound:37,therebi:34,half:26,nov:63,now:[56,34,74,7,18,35,61,2,69,97,32,83,23,59,70,64],discuss:59,name:[],revert:92,separ:[60,13,63],getitem:[17,77,88],januari:14,resourcetyp:[36,1],failov:[],getmetr:14,replac:[],continu:[29,60],opencloudopenstack:[],happen:[17,56,92,88],dispos:98,firstkei:45,subnet:[],shown:[39,29,46,44,96,1,67,81,66,12,25,87,36,40,5,13],passwordcredenti:[],space:[29,57,59,60,34,3,63],old_fil:63,monolog:42,urlencod:[57,63],catalogtyp:[],"25c16d7fa83d":[],factori:[56,77,63],curlopt:56,e41380a:[],userconst:11,tempurl:[],contain:[],remoteipprefix:5,setpassword:11,org:[],ord:[24,79,92],"5ac56230f841":[],adoptstack:40,frequenc:[52,14,35],synchron:57,catalog:[],thing:[78,60,11,77,34],place:[28,91,61,6],principl:3,think:[83,35],first:[72,49,47,61,2,55,93,25,26,59,27,4,85,75,57,58,7,60,51,92,80,30,63,32,8,83,33,74,64,69,35,9,98,86,67,89,14,15,16,76,56,70,43,17,78,45,18,96,20,68,97,34,22,41,48,94],origin:[2,52,85,41,63],ipvers:[67,29],directli:[34,48,32],subrang:67,carri:[],onc:[28,29,75,49,6,17,60,35,62,78,56,3,30,85,24,92,41,52,77,63],arrai:[72,50,26,70,47,88,78,2,24,25,3,59,27,4,89,29,56,57,6,7,60,61,55,92,63,80,81,69,30,39,85,32,8,82,33,74,64,97,68,35,9,98,10,67,12,13,83,90,91,65,14,15,5,76,86,11,42,52,17,45,18,96,19,20,40,46,21,34,23,41,48,37],yourself:[91,95],cachefil:50,"long":[85,41,49],createcontain:57,oppos:[],secureport:[26,20],happi:78,open:[49,27],mzdfw:14,country_cod:48,size:[],given:[],billableloadbalancerlist:58,convent:[24,60,91],getclaim:41,iad:[24,79,92],datastorelist:44,httpheader:63,userinfo:3,apikeycredenti:[],white:60,conveni:[24,56,63],webhookid:45,server_hostnam:40,somecontain:57,season:57,programat:[],copi:[],artifact:18,broadcast:7,github:28,than:[67,63,86,52,13,98,90,91,41,48,77,5,46],png:[],serv:[],wide:85,hostrout:67,isrooten:27,balanc:[],posit:[],mov:63,browser:63,alarmstatu:30,sai:96,san:97,argument:[6,17,93,60,68,86,52,24,92,63,40],nbodi:[],createscalingpolici:33,"20b":63,deliv:[2,94,78],maxconn:58,implod:[],engin:97,offer:[24,17,91,78,92],networkitem:9,router_interfac:13,ideal:69,checktypeid:52,take:[40,17,61,9,67,12,13,97,24,91,41,15,5,46],advis:24,fetchdatapoint:14,noth:[],channel:42,begin:[24,52,56,57,58],sure:50,normal:[24,41,96],track:80,compress:69,pair:[52,30,37,97,34,74],unalloc:48,c6dd8e837028:5,iptyp:93,later:[],drive:83,getresourcetyp:36,b5da:[],runtim:52,recipi:69,show:[60,11,14],concurr:63,label:[78,52,30,37,3,48],fifth:64,rotat:[96,74],onli:[72,49,52,54,26,74,29,75,6,60,61,80,81,31,83,40,69,35,86,37,85,91,63,65,11,67,68,64,96,20,46,34,41,13],explicitli:[11,35],ios_stream:[],transact:[71,58],maximumnumberofobjectsallowedincontain:[],activ:[29,58,17,96,61,68,92],createqueu:65,getobject:[71,63],analyz:[3,35,57],over:[],logfile_:63,variou:[26,52,15],importcredenti:50,getid:[85,11,99,77,78,62,56,52,30,3],secondari:96,cannot:[71,85,60,2,52,81,91,64],ssh:[82,34],"_must_":[],ssd:83,requir:[],ndescript:40,getemail:11,hasptrrecordsinterfac:90,error_log:63,privileg:99,where:[57,6,60,0,96,78,56,3,24,91,63,74],imageservic:70,deadlin:85,reserv:7,operationtyp:15,getmetadata:[87,30,37,63],character_set:55,new_dev:60,behind:15,between:[85,57,96,2,26,24,41,14,74],message_id:[],"4c4a":81,assumpt:[85,14],parent:[],nodeev:96,come:[24,26,57],tue:63,region:[],tutori:[],flavor_id:40,mani:[10,57,3,52,35,61,2,26,38,24,65,48,63,74],overview:[3,34],getpolici:[],period:[],colon:13,user2:[],launch_serv:23,user3:[],west:48,rebuild:[68,59],mark:85,asyncrespons:60,getdefaultregion:11,addfil:34,attachvolum:19,former:57,those:[86,59,68,57,9],"case":[17,19,56,68,27,34,92,63],virtualiplist:93,tostr:[],mount:19,plugin:[],new_autoscale_group:23,stackev:66,advantag:24,stdout:71,reportprogress:[],destin:[67,78,63,80],blah:45,ascii:65,addr:[26,96],develop:[70,74,7,18,35,61,2,69,97,32,83,59,64],author:[57,63,32],alphabet:[65,40],iscsi:97,same:[28,29,11,74,50,69,85,67,75,37,34,46,13],binari:[70,57],hkg:[79,92],html:[],array_merg:63,eventu:[],ptrrecordlist:90,exhaust:15,finish:[60,41],webserv:12,f177:23,nest:[56,57,6,2,15,64],assist:28,driver:85,someon:[],capabl:97,allocationpool:67,getrol:99,improv:[16,74],statuscod:[],serverid:[68,19,34],checkhistori:78,stockhead:63,appropri:[22,14],bind9data:60,listresourcetyp:36,without:[65,75,63,34],model:[],nodelist:96,webhead:23,rest:35,migratecontain:24,touch:26,flavor:[],speed:50,resetapikei:11,versu:20,display_nam:[21,19],servertwo:[26,96],getcdnstreaminguri:75,except:[],param:[49,77,51,19,78,86,52,53,21,55,90,91,63,57],objectstor:[75,57,63],blog:57,"3afe97b2":[],fb11:[],getgroup:[],real:[],newproperti:91,around:63,read:[28,57,17,69,24,92],virginia:[79,57,92],traffic:[7,94,96,9,20,26,31,93,24,92,5,74],mon:63,psr:42,saniti:57,interv:[14,58],whitespac:85,getcont:[],integ:[52,96,67,15,5,40],server:[],either:[11,57,80,17,93,96,78,92,63,68,90,25,34,83,14,15,5,74],d3f15879:[],ok_stat:78,output:[71,17,18,30,92,63],manag:[60,18,61,20,97,34],mail2:60,portrangemax:5,adequ:71,authent:[],constitut:85,poll:[17,52,29,92],confirm:75,getnotificationplan:78,definit:59,achiev:[29,34,33,64],protocol:[],evolv:78,keyston:[17,77],servicenet:[24,29,92,74],bundle_vers:3,clienterrorresponseexcept:[71,11,63,93],refer:[],processesinfo:3,filesysteminfo:3,domain_limit:10,starttim:58,fulli:[56,90],cloudmonitor:[3,52],isset:[17,92],comparison:[57,98],adoptstackdata:40,act:[52,35],industri:78,naction:60,getguid:3,effici:[85,95,56,3,34,24],getexponentialbackoff:24,zoneid:48,scalinggroup:56,minram:86,streamingurl:[],yyi:0,your:[],demo_us:[],her:49,area:[97,63],imagest:68,lon:[79,92],some_empty_contain:[],overwrit:[65,57],start:[],catalogitem:[77,88],interfac:[56,7,91,97,34],ipv4:[],lot:56,ipv6:[],"2yiukpwfoclimyrxesyxpd":[],hard:83,detachvolum:19,amongst:[26,74],categor:65,longer:[56,26,22,41,48,40],target_hostnam:52,cloudnetwork:[],"default":[],"173c":[],connect:[],creat:[],certain:[71,57,59,56,34,15,63],loadbalancerservic:[29,74],errorpag:25,ntarget:60,incorrect:[56,88],again:[17,60,92],extract:[],alaaaa:[],changedetail:60,field:[17,52,60,68,90],valid:[],collis:57,you:[],getdescript:[99,78,62],architectur:18,condens:14,sequenc:[55,57],latenc:34,guzzl:[71,11,42,49,0,17,77,51,92,56,24,15,63],pool:[7,13],reduc:[74,16,34],escal:78,directori:[2,97,57,63,6],descript:[],hello:75,week:60,potenti:49,cpu:[3,59,97],setdomainid:11,represent:[34,36,92,63,32],rax_priv:[],forbidden:91,mp4:63,mp3:[],abil:[7,63],follow:[1,78,2,52,26,74,28,75,58,6,7,60,61,5,83,40,84,69,35,65,12,13,14,39,66,67,17,93,18,96,46,97,59,87,36],disk:[29,50,68,59,86,34,3,83,70],groupconfig:[8,23],content:[],opencloud_token:50,wish:49,diskconfig:34,enabledhcp:67,bind_9:60,introduc:[],checkid:[30,14,78],fals:[94,65,57,71,17,60,68,16,86,67,13,34,91,92,27,46],util:[3,71],mechan:80,loadbalancerlist:26,failur:[63,58],veri:[],pathstobedelet:63,pagelimit:24,main_kei:[82,34],ordservic:24,webmast:60,httpmethodallow:[],adjust:[41,34],removenod:96,getnotificationhistoryitem:78,ten:52,sync:[],rate:24,design:[2,3,52],pass:[],further:[],unreceiv:78,deleg:[57,64],sub:60,"646ac7b0":78,section:[26,40,57,63,34],abl:[17,80,29,91],brief:3,rackspac:[],version:[],getagenttarget:3,"public":[],essenc:26,millisecond:14,full:[0,68,20,86,26,52,34,24,14,15,63,4],hash:[52,37,78,6],berkelei:60,"5gb":[],unmodifi:63,behaviour:[],listsubnet:67,inher:[91,74],parenthesi:60,valu:[50,0,26,59,2,52,25,55,74,85,75,56,57,58,6,7,60,61,80,81,30,63,82,40,84,69,35,10,12,37,83,91,15,5,76,65,67,68,18,96,19,46,97,34,41,13],a23f:[],volumeid:34,securitygrouprul:5,prior:[],amount:[56,29,75,59,86,24],pick:[],action:[60,41,57,78],narrow:3,via:[28,42,49,59,35,34,26,39,40],thirdkei:45,primit:35,transit:29,"0b3f":[],filenam:[82,63,34],vip:[],href:[85,41,15],establish:11,select:[],rackspacecloud:92,distinct:11,regist:[17,28,34,92,64],two:[],createnetwork:46,getnotificationtyp:78,taken:7,basi:71,minor:22,more:[],reachabl:61,flat:[60,34],desir:18,particular:[],known:[48,39],compani:35,"0b34":78,min1440:14,dictat:91,none:63,endpoint:[84,95,17,7,77,18,35,88,61,2,3,69,97,59,24,83,48,92,74],servicecatalog:77,valuabl:78,hous:63,outlin:[77,15,61],dev:[60,19],addvirtualip:[26,93,29],remain:[26,85,41,34],caveat:[],userguid:[],dec:63,connecterror:58,tahr:40,accept:[28,96,49],cation:[],minimum:[86,22,61,5,52],getcheck:[30,14,78,52],"20gb":[],getcredenti:77,timeoutmin:40,xxxx:17,secur:[],programmat:26,anoth:[56,15,92],snippet:63,display_descript:19,reject:[49,9],iso:34,simpl:[59,40,57,63,34],css:6,resourc:[],referenc:37,webhook:[],hoola:[],bdf7:[],okai:[17,71,92],aaaaa:[],datastor:[],mariadb:97,target_resolv:[52,48],newdomainnam:60,ethertyp:5,"short":56,launchconfig:[8,23],maximumtotalsizeofobjectsincontain:[],created_at:15,getbytesus:[57,38],hogarth:57,assign:[2,7,34,11,32],callback:[17,92],addsubscrib:[24,71],media:75,listsecuritygroup:12,help:[],soon:63,held:24,through:[],hierarchi:60,"9e9d":81,imagelist:[17,68,92],addtag:51,woolf:57,style:[94,6],overhead:86,getstatuscod:71,ip_address:[52,37],coarser:14,relev:[56,33],pend:49,might:[85,74,63,98,35,86,67,12,13,34,91,40,15,39,46],good:[17,15,92],"return":[],timestamp:[52,14],outgoingtransf:58,serveron:[26,96],detach:[],subdomain:[90,60,64],enact:24,userid:[99,11],instruct:[52,90],refresh:63,easili:[57,9],token:[],found:41,appendtometadata:[57,63],truncat:85,responsebodi:[],weight:96,blank:[60,92],procedur:[],heavi:63,gettempl:[36,40],createstack:40,cloudorchestr:[],publish:[57,69],research:56,etag:63,health:[],trusti:40,utf8:55,print:29,occurr:60,container_2001:57,upon:52,cdncontain:75,pub:82,alarmid:[30,78],reason:[56,91],base:[68,71,56,57,52,17,94,34,35,78,2,3,69,30,32,26,91,14,92,63,40],put:[24,60,6],setcountquota:57,testparam:78,resourceev:66,thrown:[71,25,93],"0d3ac30bnh6sw9jd9uzhycpsxsibechw":[],thread:50,omit:[84,11,7,18,35,61,2,69,97,59,83,63,74],agentid:3,iadservic:24,perman:[],lifetim:41,dfw:[79,92,88],sessionpersist:80,notifi:[41,49,78],httpsurl:[],prevent:69,feel:28,exchang:64,number:[],containerobjectcount:[],done:[41,57,63,78],construct:[56,77,63],loadbalancerid:[26,23],stabl:22,ystem:68,internalurl:[],differ:[10,75,56,57,17,77,95,35,88,78,2,52,98,34,99,91,14,92,63],script:[1,67,26,5,29,75,57,6,81,82,63,40,85,9,86,66,12,13,38,39,65,11,68,44,19,20,46,21,34,87,36,41],ipaddress:13,interact:[],minent:23,least:[93,41],http_cooki:80,statement:28,"11e4":13,scheme:34,"11e1":78,store:[],adher:60,option:[],relationship:85,getscalinggroup:56,memcmp:57,getten:62,absolutelimit:10,round_robin:26,part:[52,41,35,63],consult:[28,0,68,78,20,52,30,34,90,23,14,15,63,4],grace:[41,34],mode_email:11,kind:[24,37],prebuilt:59,whenev:[30,78],instanceid:[55,29,27],html_site:6,beforehand:[],packag:24,expir:[],nodeeventlist:96,dedic:69,"null":[59,2,67,74,29,7,61,80,83,40,84,35,56,12,13,91,92,5,18,96,46,97,69,70,37],b8b3:[17,92],getpublickei:82,entireti:65,built:[57,34],zero:87,self:[15,57],violat:27,also:[],build:[],getbundlevers:3,objectlist:[24,63],autoload:[17,28,92],portid:13,distribut:[2,26,96,74],unsur:17,choos:20,reach:[56,85],alloweddomainlist:9,most:[56,57,71,65,27,63],plai:85,getalarm:[30,78],plan:[],charg:[24,31],appear:[84,49,17,7,18,35,61,2,69,36,97,59,83,92,74],recordlist:98,my_api_kei:[],destruct:55,getter:[99,91,11],keystoneurl:[7,18,47,2,69,97,59,83,70,32],healthmonitor:4,newvalu:60,usual:[95,19,31,34,63,64],databaselist:55,cdn:[],alphanumer:[74,57,63,40],bulkdelet:63,"0ff89fa2fa26":78,fine:[14,9],find:[],coerc:63,payroll_2001:63,pretti:[],setnam:99,nexampl:[],templat:[],getexpir:77,yml:40,datastorevers:44,hit:78,express:58,nativ:[56,91],tcp:[52,58,5],banner:52,restart:[],getmonitoringzon:48,rfc:[15,63],connecttimeout:58,common:[],getdetail:[78,38],sslconfig:20,obscura:63,certif:[52,20],set:[],php_eol:[17,3,85,65,58],tokenid:[3,77],claimid:41,see:[68,28,11,56,52,17,77,38,88,92,86,26,34,3,15,63,40],sec:24,arg:[8,52,23],close:[26,63,58],analog:15,simultan:58,notification_plan_id:30,gethref:85,someth:[17,65,15,92],particip:61,enclosur:15,experi:[],altern:[17,52,78,63,92],signatur:[60,96],dalla:[79,21,19,92],numer:71,isol:[7,97,32],"4gb":[86,29],image_id:[],getdebugplugin:71,popul:[56,63,34],"6a4l6rr":[],both:[71,42,49,58,78,20,52,97,90,91],last:[],some_contain:[],defaultregion:11,imageid:[68,91,49,51,34],ssltermin:20,load:[],simpli:41,instanti:[],schedul:[14,35,34],header:[],shutdown:41,suppli:[28,58,60,19,55,90,70,63],getcriteria:30,backend:[46,74],devic:[75,35,67,90,13,34,3,83,74],"11be":13,empti:[29,57,67,17,60,55,82,13,26,92,63],destructor:63,lastmodifi:63,accessipv4:34,secret:6,accessipv6:34,setstaticerrorpag:6,monthli:57,nonexist:85,strategi:[],memory_limit:24,resizevolum:29,bzip:[],flight:68,flavorinterfac:86,fire:[],imag:[],stdclass:[56,87],append:[65,57,63],geten:11,understand:[],demand:[3,97],nameserv:67,repetit:50,limittyp:10,imap:[52,98],checktyp:52,look:[63,40],"5e3898d7":13,solid:83,straight:63,batch:[],oldvalu:60,durat:[40,30,32],cluster:37,"while":[56,41,97],old_contain:24,behavior:[61,97],checksum:[],piqspo8ndphw0010jemh9gygnnflyy3:[],anonym:15,robin:96,getttl:85,gettempurlsecret:6,readi:[17,60,96,92,34],systeminfo:3,revoketoken:77,dfw1:48,setttl:[],itself:[96,35,55,16,98,8,33,41,27,74],mxrecord:60,extra_spec:53,getbuildinfo:1,fedora:15,grant:[99,6],belong:64,shorter:56,decod:[],sessionpersistencetyp:80,higher:[17,92],syd:[79,92],optim:97,wherea:92,alert:[52,30,35,78,37],getsupportedplatform:52,chang:[],recent:[63,16,74],halfclos:26,travers:[17,56,60,92,98],task:69,older:22,entri:60,httpsredirect:26,npaaaaa:30,hasexpir:[77,50],getusernam:11,"2gb":[17,92],getentityid:73,algorithmlist:26,test_fil:34,explan:[20,39,4],datatimedout:58,volume_typ:19,cooldown:[23,33],mysql:[29,55,52,97,26,63],love:[17,92,34],createsecuritygrouprul:5,protocollist:26,expirationtimeinsecond:[],getview:95,sydnei:[79,92],getnetwork:46,"2076db17":5,pubkei:82,web01:40,min60:14,vendor:[17,28,92],password123:11,format:[],intuit:56,alertdev:[],"868a0966":[17,92],volumetypelist:53,semi:50,resolv:[52,64],elaps:14,manifest:63,collect:[],"boolean":[52,67,13,34,15,46],a2fc8d80c001:[],createport:13,tempnam:[],often:[56,52,78],objecttransf:[],some:[],back:[29,57,68,80,26,41,16,74],catalognam:[84,7,18,35,19,61,2,69,97,59,83,74],sampl:[],remotegroupid:5,containerlist:[],sizeof:56,containernam:[75,57,63],objectstoreservic:[2,24],weighted_round_robin:96,scale:[],per:[56,11,10,69,24,74],cast:71,substitut:[17,55,98,8,33,27],retri:24,slash:57,e836fc4:[],machin:[59,70,48,97],dnsnameserv:67,run:[28,56,0,17,48,18,35,10,81,97,24,41,14,92,40],zzz:0,"59e6":66,snake:[],nodeid:96,example2:60,impos:[10,57],paginatediter:[],prove:[7,18,47,2,69,97,59,83,70,32],b2f5eccd2b2:13,subnetid:[67,13],"1gb":[17,92],block:[71,29,68,7,35,52,21,34,83],forbiddenoperationexcept:91,primarili:[],"512m":24,within:[66,57,58,52,31,32,99,74,48,15,64],drain:96,ensur:[57,68,65,52,24,41,63,74],hemingwai:63,announc:28,identityservic:32,fledg:[],updatepassword:11,megabyt:86,live:[41,69],submit:[26,85],custom:[],doubt:92,includ:[74,99,58,60,95,65,67,3,90,41,77,64],resourcetypetempl:36,forward:[56,57,13,46],setstaticindexpag:6,getrespons:[17,71,92],blueprint:61,properli:[26,74],geturl:56,link:[],line:[22,60,28],getresourc:87,sdk:[],concaten:63,serverst:[17,92,34],consist:[60,74],nodecondit:96,sample_adopt_stack_data:40,oldimmutablefil:63,configid:81,similar:[2,24,35,19,93],constant:[85,11,57,17,68,34,41,92,63,49],f5b8c8a7c62b0150b68a50d6:[],user1:[],getfield:52,contentcach:16,addrol:99,containermetadata:[],"char":[52,37,78],queuenam:[85,41],guarante:34,curl:[56,63,0],metadatahead:[],jamie_keypair_1_rsa:[],invalid:39,librari:[17,28,92],gigabyt:[86,19],clouddatabas:[],retrievemetadata:[65,63],formatt:[],meaning:[],percona:97,getport:13,latin1_swedish_ci:81,rootus:27,stabletransit:60,source_ip:[80,48],evenli:26,depth:[59,15],listport:13,chaaaa:[30,78],deletemessag:85,endtim:58,secondkei:45,code:[],partial:63,memberstatu:49,fclose:63,image_fil:[],container_nam:[],privat:[29,93,96,88,46,34,26,15],nheader:[],listmessag:85,elsewher:28,friendli:[52,30,78],send:[],granular:[],itemuuid:78,diskinfo:3,exit:[17,92],"4b0f":23,"6lhbqdw5ayd44bd8jttdc":[],sent:[71,85,77,35,78,24,39],deactiv:4,xxxxxxxx:17,mindisk:86,enablecontentcach:16,containerobject:[],wipe:[55,57,63],newcontain:24,volumetyp:[53,19],id_rsa:82,geograph:[2,24,35],"try":[71,50,17,93,39,34,91,92,63],race:41,c1342a0a:66,pleas:[28,85,57,0,68,98,78,20,30,34,26,90,23,14,63,4],object_a:[],impli:7,getpublicurl:[77,88],readabl:[67,12,13,46],setcont:63,tempurlsecret:[],tar_bz:[],setclientid:65,hannaford:75,video:[75,63],download:63,object_z:[],claim_id:[],"85cc3048":5,compat:[56,42],index:[67,46,13,6],compar:[85,57],chicago:[79,92],"0800200c9a66":13,access:[],xxxxxxxxx:17,createsecuritygroup:12,some_other_contain:[],getcdnservic:75,san_diego_vac:[],backupstart:85,bodi:[85,77,15,56,34,60,63],firewal:34,let:[17,92],ubuntu:[17,92,40],becom:[56,29,34],sinc:[68,60,34,22,14,63],convert:75,setupobjecttransf:63,larger:[57,34],id_2:85,customhead:[],cert:20,id_1:85,implement:[85,42,57,77,56,90,91],chanc:41,mode_id:11,claim:[],appli:[26,41,97,5,20],foundat:[52,35],gatewai:67,expect:[56,70,77,86,41,63],egress:5,serverlist:[17,34],getus:[99,11],cloud:[],getothercredenti:11,collectionel:56,from:[],usb:83,commun:[26,35],accountsizeinbyt:[],next:[],websit:[],few:[11,17,7,77,18,47,2,69,97,32,26,83,59,92,70],camera:63,panel:11,remaind:41,sort:[65,57],localfilenam:[],new_contain:24,rare:56,account:[],scalabl:97,alic:[45,27],mycustomuserag:0,us2:[],control:[],sqlite:57,malform:[85,41],quickstart:32,tar:63,process:[58,17,96,61,65,3,30,69,24,41,92,63,64],high:[83,97],tag:[],tab:60,setexpir:[],serial:[83,50],launchconfigur:23,getwebooklist:45,a30b:[],listqueu:65,lamp:[39,40],port_range_min:5,versionlist:44,instead:[17,95,37,50],createcheck:52,delai:4,likewis:58,prikei:82,policyid:[33,45],abc3:5,getclientid:65,agentconnect:3,getstat:[65,23],physic:97,alloc:[7,29,13,67],essenti:60,backup:68,correspond:56,element:[68,61,56,67,13,46],issu:[99,28,78],allow:[],ingress:5,regionon:17,move:[26,56],securitygroup:[12,13],ord1:48,comma:75,getsourceip:48,usagerecord:58,ext4:3,ext3:34,curl_setopt:[],chosen:34,getokst:78,infrastructur:[3,52],heat_template_vers:40,myprefix:0,greater:[88,52,98,24,90,63],handl:[],auto:[],resourceiter:56,overal:[],dai:[85,60,78,73,41,14],auth:[],keepalivetimedout:58,getnotificationhistoryforcheck:78,jsonbodi:77,getclient:[],anyth:19,configurationid:29,mode:80,fiddli:56,ocj02rhipyyxypv9fhi:[],stock:[],subset:[57,63],bump:22,chunk:63,meta:[],"static":6,our:[71,28,17,77,26,34,3,92],special:[],out:[71,29,57,6,17,44,78,92,10,52,81,69,15,63,40],variabl:26,rel:[56,85,41,15],hardwar:[17,92,97],dhcp:[67,13],listsecuritygrouprul:5,defens:34,getstack:40,insid:[],liststack:40,setdefaultregion:11,nsrecord2:[],nsrecord1:[],organ:[2,32],dns2:60,getagentid:[3,37],fqdn:52,keep:[86,28,80,78,34],length:[65,90,85],cname:60,remotepath:[],retain:[65,63,0],getagentip:3,softwar:[87,52],listmemb:49,suffix:0,sshprivatekeyfilenam:[],scene:15,getnotificationplanid:30,echo:[71,52,3,85,57,58,6,60,78,30,88,65,10,14,92,63,56,11,17,95,19,34,48],exact:52,date:[],owner:[67,13,46],prioriti:[60,97],publickei:82,newus:11,system:[34,17,59,18,35,20,2,52,69,37,97,32,3,92,70,14,75,74],messag:[],tenantnam:17,attach:[],termin:[],"final":[26,29,63,27,78],udp:5,"51db7067821e727dc24df754":[],rsa:[],getapikei:11,accompani:77,arrayaccess:[56,91],exactli:[70,57],include_claim:[],"91de":5,githubusercont:[39,40],structur:[85,57,6,77,56,34,70,15,63],charact:[74,57,60,55,90,70,48,63,40],cloudserversopenstack:88,sens:71,bind:[60,30],exhibit:56,"function":[],"75906d20":13,beefi:15,generatetoken:77,have:[],snapshot_id:[],need:[71,49,47,61,2,24,26,59,27,74,28,29,75,57,7,60,51,92,82,32,83,64,69,35,98,56,89,91,14,15,63,70,72,17,93,18,19,68,97,34,22,23,40],billabl:58,buildinfo:1,inform:[],rout:67,rax_publ:[],flavorlist:[17,86,56,92],which:[],datacent:[11,35],singl:[],unless:[7,0],deploy:[18,61,39],who:[3,7,11,57,32],baseurl:56,callabl:[],persistencetyp:80,"9af5":66,segment:63,why:39,listimag:91,url:[],gather:[3,52,35],request:[],uri:85,getlaunchconfig:8,deni:9,snapshot:[],determin:[74,99,80,35,52,30,26,23,41,14,64],target_alia:52,constrain:5,fact:57,resourcemetadata:87,getregion:[77,88],verbos:[],getcountquota:57,anywai:63,metadataitem:76,setter:[99,91,11],redirect:26,disablelog:57,footbal:85,locat:[],should:[85,42,57,67,68,60,18,96,52,34,26,91,41,48,39,40],san_diego_vacation_video:[],local:[11,57,50,67,82,39,97,34,63,40],meant:[],recordid:98,insight:35,contribut:[],gettemporaryurl:6,mzaaaaa:[],bear:[11,57,63,6],autom:34,joint:[],jamie_keypair_1:82,jpg:71,gettimestamp:95,kong:[79,92],a80:[],httperror:24,enabl:[],loggerinterfac:42,possibl:[],glasgow:63,my_usernam:[],stuff:[56,37],pagerduti:78,partit:34,ascertain:24,agenttoken:3,view:[],getgroupconfig:8,seten:11,packet:[13,46],displai:71,volumeservic:[83,34],statu:[],correctli:[56,74],pattern:57,boundari:11,dlo:63,"1379cc8b":81,remotegroupprefix:5,progress:[17,68,92],email:[],agent_ip:3,kei:[],job:[56,69],entir:[17,35,9,68,81,37,34],agent_id:[3,37],"8ab8903ac7d8":23,swift:[],addit:[],getcontenttyp:63,admin:[67,57,11,13,46],equal:[90,5],etc:[15,63],instanc:[],grain:[14,9],disablecdnlog:75,comment:[90,60],connectfailur:58,guidelin:28,hyphen:[65,60],print_r:[17,65],chmod:[],respect:60,quit:56,yaml:[18,39,40],addition:[],httpurl:[],compos:[28,71,17,88,24,22,92],incomingtransf:58,compon:[71,18],json:[],getlabel:[3,48,30,37,78],xvhdd:19,twogbflavor:[17,92],immedi:[17,85,41],xlsx:63,adob:[],updated_at:15,togeth:[24,57],present:52,datastoreid:44,multi:[],databaseservic:97,countabl:56,vanilla:[],configurationlist:81,rate_limit:10,defin:[],"5cadc0e0b3c5":[17,92],retent:11,networkid:[67,13,46],layer:[3,7,71,20],domainid:[60,11,98],file:[],demo:[],non:[85,11,57,52,80,34,91,63],archiv:[],savemetadata:[65,57,63],incom:[26,74,5,32],maxent:23,batchlimit:24,refetch:75,cross:24,member:[],python:[],guzzlehttpexceptionbadresponseexcept:[],difficult:[],getagenttoken:3,http:[],hostnam:[52,29,98],cdnservic:75,effect:[17,97,92,50],"5xx":71,volume_id:[21,19],collat:[55,57],"__dir__":[50,39,40],off:[52,50,16,74],getchangelog:73,firstli:34,well:[17,60,61,16,74],phar:[],versionid:44,command:22,english:[57,63],audio:[],latest:[28,63],newest:41,less:[52,90,5,83],obtain:[],privateurl:[24,77],"_dynam":[],getcontentlength:63,web:[78,35,20,2,52,37,16,74],f0ac4394:13,typeid:78,domain_record_limit:10,getag:[3,85],fopen:63,utf8_general_ci:[55,81],keepalive_timeout:58,add:[],smart:[],deleteobject:57,match:[63,37,5],gmt:63,networkingservic:[7,67,12,13,5,46],piec:[52,35],mzlon:52,critical_st:78,know:[71,49,17,92,53,34,70,15],password:[],associ:[40,29,11,74,58,46,7,96,35,67,37,12,13,21,93,99,41,27,5,64],insert:[80,85,15],resid:[3,74,35,34],like:[24,25,26,4,28,29,75,57,58,6,59,60,78,62,80,40,9,56,52,15,16,76,43,68,93,94,96,20],success:[78,88],getcdnssluri:75,adminstateup:[13,46],necessari:[75,88,63],lose:41,resiz:[],page:[],createsubnet:67,exceed:58,captur:68,tenantid:[70,49,17,7,18,47,2,67,69,13,97,32,83,59,46],home:[57,63],transport:71,tmp:63,avoid:41,octet:13,createwebhook:45,leav:[27,5,92],delimet:75,setmetadata:63,getnam:[77,78,62,65,88,99,14,63],dcf:34,"enum":[93,96,15,49],usag:[],symlink:[],aaaab3nzac1yc2eaaaadaqabaaaagqdx8nkqv:[],host:[],getstream:[],although:[24,91,63],expiri:77,jsonstr:[],about:[],actual:[3,85,63,19],column:60,to_do_list:63,dbinstanc:[],constructor:42,getdomainid:11,disabl:[],own:[42,49,67,13,91,41,15,46],allhead:63,enablecdn:[75,6],ptrrecord:90,objectnam:[],nexthop:67,creatememb:49,automat:[11,69,19,65,82,13,97,34,88,63],due:57,jamie_macbook:[],getqueu:[65,85,41],getmessag:[85,39],merg:[65,63],"8fab2d22224c":[67,13],createsymlinkto:[],transfer:[24,94,63,58],addnod:[29,96],min20:14,much:[17,92],"var":[3,1,52,93,25,26,4,85,75,58,77,80,81,5,8,40,9,65,12,13,63,66,67,44,96,20,46,34,87,36,23,88],deliveri:[],brand:[8,37],subscrib:[24,71],getent:[30,95,14,37,78],serveronenod:26,getcatalog:[17,88],bug:28,count:[],made:[87,49,50,32],whether:[85,11,6,78,88,65,67,46,41,40],"483e":13,strpo:[17,92],getwebhook:45,troubl:60,"056d":[],record:[],below:[29,68,44,96,79,52,81,98,26],limit:[],uploadobject:[63,6],otherwis:34,problem:[28,71],quickli:41,formpost:57,"int":[56,63],dure:[29,41,37],additionalproperti:15,"41b9":[67,13],customhttphead:[],mountpoint:19,unpartit:34,ba8be283dbc3:13,probabl:[],"848a":[],mutual:52,a522:5,boot:[17,49],detail:[],virtual:[],"4c5e28f0":[],other:[],bool:[60,30,34],rememb:[60,96],varieti:[85,78],ntcccc:78,templateurl:[39,40],"07c9cae7d729":66,hascontentcach:16,stat:[],"class":[],accomplish:52,setbytesquota:57,"605e13f6":[],"24t17":34,rule:[],eol:22,portion:2,status:[78,73],eot:60},objtypes:{"0":"php:function"},objnames:{"0":["php","function","PHP function"]},filenames:["http-clients","services/orchestration/build-info","services/object-store/index","services/monitoring/agents","services/load-balancer/monitors","services/networking/security-group-rules","services/object-store/access","services/networking/index","services/autoscale/group-config","services/load-balancer/access","services/dns/limits","services/identity/users","services/networking/security-groups","services/networking/ports","services/monitoring/metrics","services/image/schemas","services/load-balancer/caching","getting-started-with-openstack","services/orchestration/index","services/volume/volumes","services/load-balancer/ssl","services/volume/snapshots","using-php-5.3","services/autoscale/groups","services/object-store/migrating-containers","services/load-balancer/errors","services/load-balancer/load-balancer","services/database/users","index","services/database/instances","services/monitoring/alarms","url-types","services/identity/index","services/autoscale/policies","services/compute/servers","services/monitoring/index","services/orchestration/resource-types","services/monitoring/entities","services/object-store/account","services/orchestration/templates","services/orchestration/stacks","services/queues/claims","logging","services/load-balancer/lb-setup.sample","services/database/datastores","services/autoscale/webhooks","services/networking/networks","services/common/clients.sample","services/monitoring/zones","services/image/sharing","caching-creds","services/image/tags","services/monitoring/checks","services/volume/volume-types","services/object-store/rs-only","services/database/databases","iterators","services/object-store/containers","services/load-balancer/stats","services/compute/index","services/dns/domains","services/autoscale/index","services/identity/tenants","services/object-store/objects","services/dns/index","services/queues/queues","services/orchestration/events","services/networking/subnets","services/compute/images","services/queues/index","services/image/index","debugging","services/common/rs-only.sample","services/monitoring/changelogs","services/load-balancer/index","services/object-store/cdn","services/load-balancer/metadata","services/identity/tokens","services/monitoring/notifications","regions","services/load-balancer/sessions","services/database/configurations","services/compute/keypairs","services/volume/index","services/common/service-args","services/queues/messages","services/compute/flavors","services/orchestration/resources","auth","services/common/rs-client","services/dns/reverse-dns","services/image/images","getting-started-with-rackspace","services/load-balancer/virtual-ips","services/load-balancer/logging","services/monitoring/views","services/load-balancer/nodes","services/database/index","services/dns/records","services/identity/roles"],titles:["HTTP Clients","Build info","Object Store v1","Agents","Health Monitors","Security Group Rules","Temporary URLs","Networking v2","Group configurations","Allowed Domains","Limits","Users","Security Groups","Ports"," Metrics","JSON schemas","Content Caching","Getting Started with OpenStack","Orchestration v1","Volumes","SSL Termination","Snapshots","Using the SDK with PHP v5.3","Groups","Migrating containers across regions","Error Pages","Load Balancer","Users","Welcome to php-opencloud!","Instances","Alarms","URL types","Identity v2","Scaling Policies","Servers","Monitoring v1","Resource types"," Entities","Account Details","Templates","Stacks","Claims","Logging","Setup","Datastores","Webhooks","Networks","Setup","Zones","Sharing images","Caching credentials","Image tags","Checks","Volume Types","<no title>","Databases","Iterators","Containers","Statistics and Usage Reports","Compute v2","Domains","Auto Scale v2","Tenants","Objects","DNS v1","Queues","Stack resource events","Subnets","Images","Queues v1","Images v1","Debugging","Setup","Changelogs","Load Balancer v1","CDN Containers","Metadata","Tokens","Notifications","Rackspace regions","Session Persistence","Configurations","Keypairs","Volumes v1","<no title>","Messages","Flavors","Stack resources","Authentication","<no title>","Reverse DNS","Images","Getting Started with Rackspace","Virtual IPs","Connection Logging","Views","Nodes","Databases v1","Records","Roles"],objects:{"":{cloneDomain:[60,0,1,""],addNodes:[96,0,1,""]}},titleterms:{all:[57,48,45,95,78,10,52,38,23,14,33],concept:[],queri:[10,41,98],global:99,code:[],ptr:90,hierarch:[],zone:48,send:[52,78],granular:14,under:63,webhook:45,volum:[83,29,53,19,34],veri:56,list:[49,52,53,93,62,26,75,57,3,60,78,55,65,80,81,30,63,82,40,98,9,86,87,66,36,13,90,91,14,5,76,10,11,67,68,44,95,96,19,46,21,34,99,12,23,48],upload:[82,63],iter:56,item:[78,9],small:[],sync:57,pass:[17,92],further:[70,74,7,18,35,61,2,69,97,32,83,59,64],port:13,what:[17,92],current:23,delet:[49,78,52,25,55,27,4,29,57,60,51,81,30,82,33,40,98,85,65,12,37,90,91,63,11,67,68,45,19,20,46,21,34,99,23,13],version:44,"new":[29,45,85,82,55,23,33,63],method:[99,11,62],metadata:[76,6,65,87,63,57],"5gb":63,gener:82,behaviour:56,glossari:[70,74,7,18,35,61,2,69,97,32,83,59,64],address:[11,34],modifi:[76,60,96,98,90,63],wait:29,checksum:63,step:[17,92],queue:[65,69],pick:[17,92],chang:60,revok:77,vip:93,api:[11,15],instal:[17,28,92,71],total:[57,38],select:[17,92],from:[19,9,98,15,39,40],describ:53,two:[],next:[17,92],websit:[],preview:40,type:[78,10,52,53,36,31,15,63],more:17,notif:78,about:[3,52,68,48],agent:[3,0],particular:78,cach:[50,16],account:38,retriev:[29,11,57,58,68,44,38,65,52,81,75,30,34,3,23,14,4],hour:14,setup:[72,49,59,61,2,24,93,25,3,27,4,85,75,76,58,7,51,55,80,30,63,32,8,83,33,74,64,69,35,9,98,38,14,92,16,70,43,17,78,45,18,96,20,97,47,41,94],work:[],histori:78,abandon:40,root:27,fetch:14,control:[],claim:41,stream:75,give:[],share:49,templat:[36,39,40],tag:51,want:[17,92],multipl:[67,13,63,46],secur:[12,5],anoth:[],purg:75,config:[],updat:[49,52,26,4,29,57,3,78,81,30,33,40,65,37,91,63,11,67,45,20,46,34,41,13],resourc:[56,87,66,36],dive:[17,92],befor:[],catalog:[77,88],date:63,datastor:44,data:14,orchestr:18,credenti:[17,11,92,50],inform:3,allow:9,help:28,over:63,through:[],ttl:[],paramet:[85,60,98,78,52,34,41],group:[8,23,12,5],monitor:[35,4],polici:33,requir:24,persist:80,"return":[],handl:71,auto:61,detach:19,introduct:[],name:[57,63,98],changelog:73,edit:8,revers:90,authent:[77,88],token:[3,77],each:63,debug:71,cloudfil:[],reset:11,domain:[60,9],replac:81,individu:[],keypair:[82,34],connect:[3,29,94],patch:[81,15],extract:63,event:[66,96],subnet:67,network:[7,46,9],content:[63,25,16],health:4,internet:[],clone:60,statist:58,insid:57,workflow:49,migrat:24,quick:[17,92],releas:41,"byte":[57,38],tenant:[77,62],launch:8,filter:[65,57,68,60,86,34],pagin:56,perman:[],oper:[70,74,7,18,35,61,2,69,97,32,83,59,75,64],number:14,bootabl:34,internalurl:31,size:63,given:[],interact:77,messag:[85,41],attach:19,termin:20,store:2,schema:[91,15],option:[24,52,56],"public":[],copi:63,specifi:14,loadbalanc:[],serv:[],target:3,remot:52,remov:[26,93,96,76,9],balanc:[26,29,74],deliveri:57,comput:59,ram:29,expir:14,have:78,tabl:[],destroi:77,mid:[],note:[65,56,91,49],also:[],client:[17,65,92,71,0],build:[17,29,1,92],indic:[],singl:[63,85,21,19],usernam:11,object:[11,57,62,2,75,38,99,63],quota:57,discov:78,plan:78,"class":[56,91],url:[75,6,31,34,39,40],doc:[],alarm:[30,78],adopt:40,thunderbird:[17,92],snapshot:21,cdn:[75,6],session:[80,77],find:[65,52,60,98],absolut:10,locat:63,explain:24,configur:[94,20,80,81,8,16],folder:[],info:1,contribut:28,get:[1,3,26,85,57,60,78,8,33,40,98,86,87,66,12,13,90,91,92,63,65,11,67,17,93,45,19,46,21,34,99,36,23,48],ssl:[75,20],report:58,restart:29,enabl:[80,94,75,27,16],across:24,common:56,contain:[24,75,57,63,38],view:[73,95,96,9,20,25,38],set:[56,85,25,6],result:[86,68],respons:15,statu:49,wire:71,kei:[11,6],databas:[55,97],state:23,"import":[56,60],publicurl:31,email:11,attribut:[52,37,34],parent:63,swift:71,addit:49,last:63,plugin:71,region:[24,79],tracerout:48,instanc:[17,29,81,92],load:[26,29,74],point:14,instanti:[],period:14,header:0,typic:49,guid:[],rackspac:[79,7,18,47,92,2,69,97,59,83,70,32],json:15,basic:[],strategi:71,imag:[49,17,51,68,34,91,70,92],resolut:14,bulk:[],ident:32,servic:[28,70,74,17,7,77,18,96,35,88,61,2,69,97,32,83,59,92,64],properti:[99,11,62],batch:[85,63],defin:[],error:25,fun:17,metric:[3,14],site:6,archiv:63,welcom:28,perform:48,member:49,html:6,document:[],complet:[],http:0,retriv:37,temporari:6,user:[99,77,11,27,0],php:[22,28],stack:[87,66,40],person:34,exampl:[71,15,50],thi:[],filesystem:50,entiti:37,model:[],protocol:26,execut:33,obtain:[],openstack:[70,17,7,18,47,2,69,97,32,83,59],flavor:[17,86,92],except:71,add:[71,76,93,96,51,9,98,99,90],valid:39,logger:42,which:78,format:[],password:11,specif:[10,85],server:[17,92,19,34],collect:[56,57],resiz:29,page:25,opencloud:28,creation:[],some:[17,3,92],sampl:[],scale:[23,33,61],larg:[57,63],inject:42,condit:63,refer:[],usag:[28,58],symlink:[],host:[3,6],prerequisit:[],post:85,paginatediter:56,disabl:[80,94,75,16],your:[17,71,92],log:[71,94,75,42,57],support:28,nova:[17,92],custom:25,start:[17,92],ipv4:93,ipv6:93,"function":0,cloud:[],link:[70,74,7,18,35,61,2,69,97,32,83,59,64],sdk:[17,22,92],count:[57,38],possibl:[52,78],"default":0,access:[57,9,34],record:[90,98],limit:10,"export":60,creat:[49,67,26,55,27,29,57,3,6,60,78,81,30,63,33,40,65,12,13,5,11,52,45,19,46,21,34,23,77,37],request:63,deep:[17,92],autoscal:[],intro:[],exist:[93,78,65,52,82,33,63],file:[40,63,39,34],check:[94,78,65,52,16,27],tip:28,detail:[65,52,68,19,86,26,21,38,3,91,48,4],virtual:93,other:0,role:99,test:[52,78],you:[17,92],node:96,stat:[65,58],meaning:71,algorithm:26,descript:[],pseudo:[],rule:5,fresh:[]}}) \ No newline at end of file diff --git a/doc/_build/html/services/autoscale/group-config.html b/doc/_build/html/services/autoscale/group-config.html new file mode 100644 index 000000000..a4b5ebbaf --- /dev/null +++ b/doc/_build/html/services/autoscale/group-config.html @@ -0,0 +1,343 @@ + + + + + + + + + + Group configurations — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Group configurations

          +
          +

          Setup

          +

          In order to interact with the functionality of a group’s configuration, +you must first retrieve the details of the group itself. To do this, you must +substitute {groupId} for your group’s ID:

          +
          $group = $service->group('{groupId}');
          +
          +
          +
          +
          +

          Get group configuration

          +
          /** @var  */
          +$groupConfig = $group->getGroupConfig();
          +
          +
          +
          +
          +

          Edit group configuration

          +
          $groupConfig->update(array(
          +    'name' => 'New name!'
          +));
          +
          +
          +
          +
          +

          Get launch configuration

          +
          /** @var */
          +$launchConfig = $group->getLaunchConfig();
          +
          +
          +
          +
          +

          Edit group/launch configuration

          +
          $launchConfig = $group->getLaunchConfig();
          +
          +$server = $launchConfig->args->server;
          +$server->name = "BRAND NEW SERVER NAME";
          +
          +$launchConfig->update(array
          +    'args' => array(
          +        'server' => $server,
          +        'loadBalancers' => $launchConfig->args->loadBalancers
          +    )
          +));
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/autoscale/groups.html b/doc/_build/html/services/autoscale/groups.html new file mode 100644 index 000000000..e1f771595 --- /dev/null +++ b/doc/_build/html/services/autoscale/groups.html @@ -0,0 +1,367 @@ + + + + + + + + + + Groups — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Groups

          +
          +

          List all groups

          +
          $groups = $service->groupList();
          +foreach ($group as $group) {
          +  /** @var $group OpenCloud\Autoscale\Resources\Group */
          +}
          +
          +
          +

          Please consult the iterator guide for more information about +iterators.

          +
          +
          +

          Retrieve group by ID

          +
          $group = $service->group('{groupId}');
          +
          +
          +
          +
          +

          Create a new group

          +
          // Set the config object for this autoscale group; contains all of properties
          +// which determine its behaviour
          +$groupConfig = array(
          +  'name'        => 'new_autoscale_group',
          +  'minEntities' => 5,
          +  'maxEntities' => 25,
          +  'cooldown'    => 60,
          +);
          +
          +// We need specify what is going to be launched. For now, we'll launch a new server
          +$launchConfig = array(
          +  'type' => 'launch_server',
          +  'args' => array(
          +    'server' => array(
          +      'flavorRef' => 3,
          +      'name'      => 'webhead',
          +      'imageRef'  => '0d589460-f177-4b0f-81c1-8ab8903ac7d8'
          +    ),
          +    'loadBalancers' => array(
          +      array('loadBalancerId' => 2200, 'port' => 8081),
          +    )
          +  )
          +);
          +
          +// Do we want particular scaling policies?
          +$policy = array(
          +  'name'     => 'scale up by 10',
          +  'change'   => 10,
          +  'cooldown' => 5,
          +  'type'     => 'webhook',
          +);
          +
          +$group->create(array(
          +  'groupConfiguration'  => $groupConfig,
          +  'launchConfiguration' => $launchConfig,
          +  'scalingPolicies'     => array($policy),
          +));
          +
          +
          +
          +
          +

          Delete a group

          +
          $group->delete();
          +
          +
          +
          +
          +

          Get the current state of the scaling group

          +
          $group->getState();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/autoscale/index.html b/doc/_build/html/services/autoscale/index.html new file mode 100644 index 000000000..86e99d2d9 --- /dev/null +++ b/doc/_build/html/services/autoscale/index.html @@ -0,0 +1,409 @@ + + + + + + + + + + Auto Scale v2 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Auto Scale v2

          +
          +

          Note

          +

          This service is only available for Rackspace users.

          +
          +
          +

          Setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +

          Auto Scale service

          +

          Now to instantiate the Auto Scale service:

          +
          $service = $client->autoscaleService();
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          group
          +
          The scaling group is at the heart of an Auto Scale deployment. The scaling +group specifies the basic elements of the Auto Scale configuration. It +manages how many servers can participate in the scaling group. It also +specifies information related to load balancers if your configuration uses +a load balancer.
          +
          group configuration
          +
          Outlines the basic elements of the Auto Scale configuration. The group +configuration manages how many servers can participate in the scaling group. +It sets a minimum and maximum limit for the number of entities that can be +used in the scaling process. It also specifies information related to load +balancers.
          +
          launch configuration
          +
          Creates a blueprint for how new servers will be created. The launch +configuration specifies what type of server image will be started on +launch, what flavor the new server is, and which load balancer the new +server connects to.
          +
          policy
          +
          Auto Scale uses policies to define the scaling activity that will take +place, as well as when and how that scaling activity will take place. +Scaling policies specify how to modify the scaling group and its behavior. +You can specify multiple policies to manage a scaling group.
          +
          webhook
          +
          A webhook is a reachable endpoint that when visited will execute a scaling +policy for a particular scaling group.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/autoscale/policies.html b/doc/_build/html/services/autoscale/policies.html new file mode 100644 index 000000000..ac077aeb3 --- /dev/null +++ b/doc/_build/html/services/autoscale/policies.html @@ -0,0 +1,361 @@ + + + + + + + + + + Scaling Policies — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Scaling Policies

          +
          +

          Setup

          +

          In order to interact with the functionality of a group’s scaling +policies, you must first retrieve the details of the group itself. To do this, +you must substitute {groupId} for your group’s ID:

          +
          $group = $service->group('{groupId}');
          +
          +
          +
          +
          +

          Get all policies

          +
          $policies = $group->getScalingPolicies();
          +
          +foreach ($policies as $policy) {
          +    printf("Name: %s Type: %s\n", $policy->name, $policy->type);
          +}
          +
          +
          +
          +
          +

          Create new scaling policies

          +

          Creating policies is achieved through passing an array to the create +method.

          +
          $policies = array(
          +  array(
          +    'name'     => 'NEW NAME',
          +    'change'   => 1,
          +    'cooldown' => 150,
          +    'type'     => 'webhook',
          +  )
          +);
          +
          +$group->createScalingPolicies($policies);
          +
          +
          +
          +
          +

          Get an existing scaling policy

          +
          $policy = $group->getScalingPolicy('{policyId}');
          +
          +
          +
          +
          +

          Update a scaling policy

          +
          $policy = $group->getScalingPolicy('{policyId}');
          +$policy->update(array(
          +    'name' => 'More relevant name'
          +));
          +
          +
          +
          +
          +

          Delete a scaling policy

          +
          $policy = $group->getScalingPolicy('{policyId}');
          +$policy->delete();
          +
          +
          +
          +
          +

          Execute a scaling policy

          +
          $policy = $group->getScalingPolicy('{policyId}');
          +$policy->execute();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/autoscale/service.sample.html b/doc/_build/html/services/autoscale/service.sample.html new file mode 100644 index 000000000..c8f23803f --- /dev/null +++ b/doc/_build/html/services/autoscale/service.sample.html @@ -0,0 +1,211 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +

          The first thing to do is pass in your credentials and instantiate a Rackspace +client:

          +
          <?php
          +
          +require 'vendor/autoload.php';
          +
          +use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +

          Now, set up the Auto Scale service:

          +
          $service = $client->autoscaleService();
          +
          +
          +

          {catalogName} is the name of the service, as it appears in the service +catalog. For Rackspace users, a default will be provided if you pass null +in for this argument. For OpenStack users, you cannot do this: you must instead +set your own value since it can depend on your environment setup.

          +

          {region} is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.

          +

          {urlType} is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either internalURL or +publicURL. The former will execute HTTP transactions over the internal +network configured for your service, possibly reducing latency and the overall +bandwidth cost - the caveat is that all of your resources must be in same region. +publicURL, however, which is the default, will operate over the public +Internet and is to be used for multi-region installations.

          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/autoscale/webhooks.html b/doc/_build/html/services/autoscale/webhooks.html new file mode 100644 index 000000000..dd7326134 --- /dev/null +++ b/doc/_build/html/services/autoscale/webhooks.html @@ -0,0 +1,345 @@ + + + + + + + + + + Webhooks — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Webhooks

          +
          +

          Setup

          +

          In order to interact with webhooks, you must first retrieve the +details of the group and scaling policy you want to execute:

          +
          $group = $service->group('{groupId}');
          +$policy = $group->getScalingPolicy('{policyId}');
          +
          +
          +
          +
          +

          Get all webhooks

          +
          $webhooks = $policy->getWebookList();
          +
          +
          +
          +
          +

          Create a new webhook

          +
          $policy->createWebhooks(array(
          +    array(
          +        'name' => 'Alice',
          +        'metadata' => array(
          +            'firstKey'  => 'foo',
          +            'secondKey' => 'bar'
          +        )
          +    )
          +));
          +
          +
          +
          +
          +

          Get webhook

          +
          $webhook = $policy->getWebhook('{webhookId}');
          +
          +
          +
          +
          +

          Update webhook

          +
          // Update the metadata
          +$metadata = $webhook->metadata;
          +$metadata->thirdKey = 'blah';
          +$webhook->update(array(
          +    'metadata' => $metadata
          +));
          +
          +
          +
          +
          +

          Delete webhook

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/common/clients.sample.html b/doc/_build/html/services/common/clients.sample.html new file mode 100644 index 000000000..115c25c90 --- /dev/null +++ b/doc/_build/html/services/common/clients.sample.html @@ -0,0 +1,307 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/common/os-client.sample.html b/doc/_build/html/services/common/os-client.sample.html new file mode 100644 index 000000000..cdc1a064c --- /dev/null +++ b/doc/_build/html/services/common/os-client.sample.html @@ -0,0 +1,237 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +

          or if you’re an OpenStack user:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/common/rs-client.html b/doc/_build/html/services/common/rs-client.html new file mode 100644 index 000000000..3d0e9414e --- /dev/null +++ b/doc/_build/html/services/common/rs-client.html @@ -0,0 +1,289 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/common/rs-client.sample.html b/doc/_build/html/services/common/rs-client.sample.html new file mode 100644 index 000000000..7441fc667 --- /dev/null +++ b/doc/_build/html/services/common/rs-client.sample.html @@ -0,0 +1,254 @@ + + + + + + + + + + Rackspace setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/common/rs-only.sample.html b/doc/_build/html/services/common/rs-only.sample.html new file mode 100644 index 000000000..2f30d54aa --- /dev/null +++ b/doc/_build/html/services/common/rs-only.sample.html @@ -0,0 +1,296 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Note

          +

          This service is only available for Rackspace users.

          +
          +
          +

          Setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/common/service-args.html b/doc/_build/html/services/common/service-args.html new file mode 100644 index 000000000..aebe9a3d7 --- /dev/null +++ b/doc/_build/html/services/common/service-args.html @@ -0,0 +1,289 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/Images.md.html b/doc/_build/html/services/compute/Images.md.html new file mode 100644 index 000000000..42f5c5717 --- /dev/null +++ b/doc/_build/html/services/compute/Images.md.html @@ -0,0 +1,253 @@ + + + + + + + + + + Compute Images — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Compute Images

          +
          +

          Intro

          +

          An image is a collection of files for a specific operating system that +you use to create or rebuild a server. Rackspace provides prebuilt +images. You can also create custom images from servers that you have +launched.

          +

          In addition to creating images manually, you can also schedule images of +your server automatically. Please consult the official +docs +for more information about this extension, including enabling and +disabling scheduled images and showing scheduled images.

          +

          With standard servers, the entire disk (OS and data) is captured in the +image. With Performance servers, only the system disk is captured in the +image. The data disks should be backed up using Cloud Backup or Cloud +Block Storage to ensure availability in case you need to rebuild or +restore a server.

          +
          +
          +

          Setup

          +

          You first need to setup a Compute service. For information, please +consult the Compute service documentation.

          +
          +
          +

          List images

          +
          $images = $service->imageList();
          +
          +foreach ($images as $image) {
          +
          +}
          +
          +
          +

          For more information about iterators, +please consult the official documentation.

          +
          +

          Query parameters

          +

          You can also refine the list of images returned by providing specific +URL parameters:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Field nameDescription
          serverFilters the list of images by server. Specify the server reference by ID or by full URL.
          nameFilters the list of images by image name.
          statusFilters the list of images by status. In-flight images have a status of SAVING and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the OpenCloud\Compute\Constants\ImageState class. Images with an ACTIVE status are available for use.
          changes-sinceFilters the list of images to those that have changed since the changes-since time. See the official docs for more information.
          markerThe ID of the last item in the previous list. See the official docs for more information.
          limitSets the page size. See the official docs for more information.
          typeFilters base Rackspace images or any custom server images that you have created. Can either be BASE or SNAPSHOT.
          +
          +
          +

          Example

          +

          You can return more information about each image by setting the +$details argument to true. The second argument can be an array +of query parameters:

          +
          use OpenCloud\Compute\Constants\ImageState;
          +
          +$list = $service->imageList(true, array(
          +    'server' => 'fooBar',
          +    'status' => ImageState::ACTIVE
          +));
          +
          +
          +
          +
          +
          +

          Get an image

          +
          $imageId = '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001';
          +$image = $service->image($imageId);
          +
          +
          +
          +
          +

          Delete an image

          +
          $image->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/Keypair.md.html b/doc/_build/html/services/compute/Keypair.md.html new file mode 100644 index 000000000..4ccc67499 --- /dev/null +++ b/doc/_build/html/services/compute/Keypair.md.html @@ -0,0 +1,242 @@ + + + + + + + + + + Keypairs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Keypairs

          +
          +

          Generate new keypair

          +

          This operation creates a new keypair under a provided name; the public +key value is automatically generated for you.

          +
          $keypair = $service->keypair();
          +
          +$keypair->create(array(
          +   'name' => 'jamie_keypair_1'
          +));
          +
          +echo $keypair->getPublicKey();
          +
          +// Save private key to a file so you can use it to SSH into
          +// your server later.
          +$sshPrivateKeyFilename = 'jamie_keypair_1_rsa';
          +$privateKey = $keypair->getPrivateKey();
          +file_put_contents($sshPrivateKeyFilename, $privateKey);
          +chmod($sshPrivateKeyFilename, 0600);
          +
          +
          +
          +
          +

          Upload existing keypair

          +

          This operation creates a new keypair under a provided name using a +provided public key value. This public key will probably exist on your +local filesystem, and so provide easy access to your server when +uploaded.

          +
          $keypair = $service->keypair();
          +
          +$key = <<<EOT
          +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Example public key
          +EOT;
          +
          +$keypair->create(array(
          +   'name'      => 'jamie_macbook',
          +   'publicKey' => $key
          +));
          +
          +
          +
          +
          +

          List keypairs

          +

          To list all existing keypairs:

          +
          $keys = $service->listKeypairs();
          +
          +foreach ($keys as $key) {
          +   // ...
          +}
          +
          +
          +

          For more information about iterators, please see the +docs.

          +
          +
          +

          Delete keypairs

          +

          To delete a specific keypair:

          +
          $keypair->delete();
          +
          +
          +
          +
          +

          Creating a server with a keypair

          +

          In order to spawn an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter:

          +
          use Guzzle\Http\Exception\BadResponseException;
          +use OpenCloud\Compute\Constants\Network;
          +
          +$server = $compute->server();
          +
          +try {
          +    $response = $server->create(array(
          +        'name'     => 'New server',
          +        'image'    => $ubuntuImage,
          +        'flavor'   => $twoGbFlavor,
          +        'networks' => array(
          +            $compute->network(Network::RAX_PUBLIC),
          +            $compute->network(Network::RAX_PRIVATE)
          +        ),
          +        'keypair' => 'jamie_macbook'
          +    ));
          +} catch (BadResponseException $e) {
          +   // error...
          +}
          +
          +
          +

          So, as you can see, you specify the name of an existing keypair that +you previously created on the API.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/Server.md.html b/doc/_build/html/services/compute/Server.md.html new file mode 100644 index 000000000..f6faa48b9 --- /dev/null +++ b/doc/_build/html/services/compute/Server.md.html @@ -0,0 +1,447 @@ + + + + + + + + + + Servers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Servers

          +
          +

          Intro

          +

          A server is a virtual machine instance in the Cloud Servers environment.

          +
          +
          +

          Setup

          +

          Server objects are instantiated from the Compute service. For more +details, see the Service docs.

          +
          +
          +

          Get server

          +

          The easiest way to retrieve a specific server is by its unique ID:

          +
          $serverId = 'ef08aa7a-b5e4-4bb8-86df-5ac56230f841';
          +$server   = $service->server($serverId);
          +
          +
          +
          +
          +

          List servers

          +

          You can list servers in two different ways:

          +
            +
          • return an overview of each server (ID, name and links)
          • +
          • return detailed information for each server
          • +
          +

          Knowing which option to use might help save unnecessary bandwidth and +reduce latency.

          +
          // overview
          +$servers = $service->serverList();
          +
          +// detailed
          +$servers = $service->serverList(true);
          +
          +
          +
          +

          URL parameters for filtering servers

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionType
          imageThe image IDstring
          flavorThe flavor IDstring
          nameThe server namestring
          statusThe server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult OpenCloud\Compute\Constants\ServerStatestring
          changes-sinceValue for checking for changes since a previous requestA valid ISO 8601 dateTime (2011-01-24T17:08Z)
          RAX-SI:image_scheduleIf scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule.bool
          +
          +
          +
          +

          Create server

          +
          +

          Using an image

          +

          There are a few parameter requirements when creating a server using an +image:

          +
            +
          • name - needs to be a string;
          • +
          • flavor - a OpenCloud\Compute\Resource\Flavor object, that is +populated with the values of a real API flavor;
          • +
          • image - a OpenCloud\Compute\Resource\Image object, that is +populated with the values of a real API image;
          • +
          +

          Firstly we need to find our flavor and image using their UUIDs. For more +information about these concepts, including how to find flavor/image +UUIDs, please consult §§ 3-4 in the Getting Started +guide.

          +
          $ubuntuImage = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5');
          +$twoGbFlavor = $compute->flavor('4');
          +
          +
          +

          Now we’re ready to create our instance:

          +
          use OpenCloud\Compute\Constants\Network;
          +
          +$server = $compute->server();
          +
          +try {
          +    $response = $server->create(array(
          +        'name'     => 'My lovely server',
          +        'image'    => $ubuntuImage,
          +        'flavor'   => $twoGbFlavor
          +    ));
          +} catch (\Guzzle\Http\Exception\BadResponseException $e) {
          +
          +    // No! Something failed. Let's find out:
          +    $responseBody = (string) $e->getResponse()->getBody();
          +    $statusCode   = $e->getResponse()->getStatusCode();
          +    $headers      = $e->getResponse()->getHeaderLines();
          +
          +    echo sprintf('Status: %s\nBody: %s\nHeaders: %s', $statusCode, $responseBody, implode(', ', $headers);
          +}
          +
          +
          +

          It’s always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner.

          +
          +
          +

          Using a bootable volume

          +

          There are a few parameter requirements when creating a server using a +bootable volume:

          +
            +
          • name - needs to be a string;
          • +
          • flavor - a OpenCloud\Compute\Resource\Flavor object, that is +populated with the values of a real API flavor;
          • +
          • volume - a OpenCloud\Volume\Resource\Volume object, that is +populated with the values of a real API volume;
          • +
          +

          Firstly we need to find our flavor and volume using their IDs.

          +
          $volumeService = $client->volumeService();
          +$bootableVolume = $volumeService->volume('<ID OF A BOOTABLE VOLUME>');
          +$flavor = $compute->flavor('<ID OF A FLAVOR>');
          +
          +
          +

          Now we’re ready to create our instance:

          +
          use OpenCloud\Compute\Constants\Network;
          +
          +$server = $compute->server();
          +
          +try {
          +    $response = $server->create(array(
          +        'name'     => 'My lovely server',
          +        'volume'   => $bootableVolume,
          +        'flavor'   => $flavor
          +    ));
          +} catch (\Guzzle\Http\Exception\BadResponseException $e) {
          +    // No! Something failed. Let's find out:
          +    echo $e->getRequest() . PHP_EOL . PHP_EOL;
          +    echo $e->getResponse();
          +}
          +
          +
          +

          It’s always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner.

          +
          +
          +

          Create parameters

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionTypeRequired
          nameThe server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync.stringYes
          flavorA populated OpenCloud\Compute\Resource\Flavor object representing your chosen flavorobjectYes
          imageA populated OpenCloud\Compute\Resource\Image object representing your chosen imageobjectNo, if volume is specified
          volumeA populated OpenCloud\Volume\Resource\Volume object representing your chosen bootable volumeobjectNo, if image is specified
          volumeDeleteOnTerminationtrue if the bootable volume should be deleted when the server is terminated; false, otherwisebooleanNo; default = false
          OS-DCF:diskConfigThe disk configuration value. You can use two options: AUTO or MANUAL. AUTO means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. AUTO is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.MANUAL means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration.stringNo
          networksAn array of populated OpenCloud\Compute\Resource\Network objects that indicate which networks your instance resides in.arrayNo
          metadataAn array of arbitrary data (key-value pairs) that adds additional meaning to your server.arrayNo
          keypairYou can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication.arrayNo
          personalityFiles that you can upload to your newly created instance’s filesystem.arrayNo
          +
          +
          +

          Creating a server with keypairs

          +

          Please see the Keypair docs for more information.

          +
          +
          +

          Creating a server with personality files

          +

          Before you execute the create operation, you can add “personality” files +to your OpenCloud\Compute\Resource\Server object. These files are +structured as a flat array.

          +
          $server->addFile('/var/test_file', 'FILE CONTENT');
          +
          +
          +

          As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the official +documentation.

          +
          +
          +
          +

          Update server

          +

          You can update certain attributes of an existing server instance. These +attributes are detailed in the next section.

          +
          $server->update(array(
          +   'name' => 'NEW SERVER NAME'
          +));
          +
          +
          +
          +

          Updatable attributes

          + ++++ + + + + + + + + + + + + + + + + +
          namedescription
          nameThe name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique.
          accessIPv4The IP version 4 address.
          accessIPv6The IP version 6 address.
          +
          +
          +
          +

          Delete server

          +
          $server->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/Service.md.html b/doc/_build/html/services/compute/Service.md.html new file mode 100644 index 000000000..99171c8e8 --- /dev/null +++ b/doc/_build/html/services/compute/Service.md.html @@ -0,0 +1,186 @@ + + + + + + + + + + Compute service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Compute service

          +
          +

          Setup

          +

          To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the Clients documentation.

          +
          $service = $client->computeService();
          +
          +
          +

          If no arguments are provided to the above method, certain values are set +to their default values:

          + ++++ + + + + + + + + + + + + + + + + +
          ParamDefault value
          $namecloudServersOpenStack
          $regionDFW
          $urltypepublicURL
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/flavors.html b/doc/_build/html/services/compute/flavors.html new file mode 100644 index 000000000..f2d579914 --- /dev/null +++ b/doc/_build/html/services/compute/flavors.html @@ -0,0 +1,358 @@ + + + + + + + + + + Flavors — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Flavors

          +
          +

          Get a flavor

          +
          $flavor = $service->flavor('{flavorId}');
          +
          +
          +
          +
          +

          List flavors

          +
          $flavors = $service->flavorList();
          +
          +foreach ($flavors as $flavor) {
          +    /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +

          Detailed results

          +

          By default, the flavorList method returns full details on all flavors. +However, because of the overhead involved in retrieving all the details, this +function can be slower than might be expected. To disable this feature and +keep bandwidth at a minimum, just pass false as the first argument:

          +
          // Name and ID only
          +$compute->flavorList(false);
          +
          +
          +
          +
          +

          Filtering

          +

          You can also refine the list of images returned by providing specific filters:

          + ++++ + + + + + + + + + + + + + + + + + + + +
          Array keyDescription
          minDiskFilters the list of flavors to those with the specified minimum number of gigabytes of disk storage.
          minRamFilters the list of flavors to those with the specified minimum amount of RAM in megabytes.
          markerThe ID of the last item in the previous list. See the official docs for more information.
          limitSets the page size. See the official docs for more information.
          +

          These are defined in an array and passed in as the second argument. For example, +to return all flavors over 4GB in RAM:

          +
          $flavors = $service->flavorList(true, array('minRam' => 4));
          +
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/images.html b/doc/_build/html/services/compute/images.html new file mode 100644 index 000000000..cd8007d21 --- /dev/null +++ b/doc/_build/html/services/compute/images.html @@ -0,0 +1,380 @@ + + + + + + + + + + Images — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Images

          +
          +

          Note

          +

          Images on Rackspace servers: with standard servers, the entire disk +(OS and data) is captured in the image. With Performance servers, only the s +ystem disk is captured in the image. The data disks should be backed up using +Cloud Backup or Cloud Block Storage to ensure availability in case you need +to rebuild or restore a server.

          +
          +
          +

          List images

          +

          Below is the simplest usage for retrieving a list of images:

          +
          $images = $service->imageList();
          +
          +foreach ($images as $image) {
          +
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +

          Detailed results

          +

          By default, the only fields returned in a list call are id and name, but +you can enable more detailed information to be result by passing in true as +the first argument of the call, like so:

          +
          $images = $service->imageList(true);
          +
          +
          +
          +
          +

          Filtering

          +

          You can also refine the list of images returned by providing specific filters:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Array keyDescription
          serverFilters the list of images by server. Specify the server reference by ID or by full URL.
          nameFilters the list of images by image name.
          statusFilters the list of images by status. In-flight images have a status of SAVING and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the OpenCloud\Compute\Constants\ImageState class. Images with an ACTIVE status are available for use.
          changes-sinceFilters the list of images to those that have changed since the changes-since time. See the official docs for more information.
          markerThe ID of the last item in the previous list. See the official docs for more information.
          limitSets the page size. See the official docs for more information.
          typeFilters base Rackspace images or any custom server images that you have created. Can either be BASE or SNAPSHOT.
          +

          These are defined in an array and passed in as the second argument. For example, +to filter images for a particular server:

          +
          $images = $service->imageList(false, array('server' => '{serverId}'));
          +
          +
          +
          +
          +
          +

          Retrieve details about an image

          +
          $image = $service->image('{imageId}');
          +
          +
          +
          +
          +

          Delete an image

          +
          $image->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/index.html b/doc/_build/html/services/compute/index.html new file mode 100644 index 000000000..e508a4a4d --- /dev/null +++ b/doc/_build/html/services/compute/index.html @@ -0,0 +1,419 @@ + + + + + + + + + + Compute v2 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Compute v2

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Compute service

          +

          Now to instantiate the Compute service:

          +
          $service = $client->computeService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          image
          +
          An image is a collection of files for a specific operating system that you +use to create or rebuild a server. Rackspace provides prebuilt images. You +can also create custom images from servers that you have launched.
          +
          flavor
          +
          A flavor is a named definition of certain server parameters such as +the amount of RAM and disk space available. (There are other parameters +set via the flavor, such as the amount of disk space and the number of +virtual CPUs, but a discussion of those is too in-depth for a simple +Getting Started Guide like this one.)
          +
          server
          +
          A server is a virtual machine instance in the Cloud Servers environment.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/keypair.html b/doc/_build/html/services/compute/keypair.html new file mode 100644 index 000000000..830311ef9 --- /dev/null +++ b/doc/_build/html/services/compute/keypair.html @@ -0,0 +1,246 @@ + + + + + + + + + + Keypairs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Keypairs

          +
          +

          Setup

          +

          The first thing to do is pass in your credentials and instantiate a Rackspace +client:

          +
          <?php
          +
          +require 'vendor/autoload.php';
          +
          +use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +

          Now, set up the Auto Scale service:

          +
          $service = $client->computeService('{catalogType}', '{region}', '{urlType}');
          +
          +
          +

          {catalogType} is the name of the service, as it appears in the service +catalog. For Rackspace users, this will default to cloudServersOpenStack; for +OpenStack users, you must set your own value since it can depend on your +environment setup.

          +

          {region} is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.

          +

          {urlType} is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either internalURL or publicURL. +The former will execute HTTP transactions over the internal Rackspace network, +reducing latency and the overall bandwidth cost - the caveat is that all of your +resources must be in same region. publicURL, however, which is the default, +will operate over the public Internet and is to be used for multi-region +installations.

          +
          +
          +

          Generate a new keypair

          +

          This operation creates a new keypair under a provided name; the public key +value is automatically generated for you.

          +
          // Instantiate empty object
          +$keypair = $service->keypair();
          +
          +// Send to API
          +$keypair->create(array(
          +   'name' => 'jamie_keypair_1'
          +));
          +
          +// Save these!
          +$pubKey = $keypair->getPublicKey();
          +$priKey = $keypair->getPrivateKey();
          +
          +
          +
          +
          +

          Upload existing keypair

          +

          This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem.

          +
          $keypair = $service->keypair();
          +
          +// $key needs to be the string content of the key file, not the filename
          +$content = file_get_contents('~/.ssh/id_rsa.pub');
          +
          +$keypair->create(array(
          +   'name'      => 'main_key',
          +   'publicKey' => $content
          +));
          +
          +
          +
          +
          +

          List keypairs

          +

          To list all existing keypairs:

          +
          $keys = $service->listKeypairs();
          +
          +foreach ($keys as $key) {
          +
          +}
          +
          +
          +
          +
          +

          Delete keypairs

          +

          To delete a specific keypair:

          +
          $keypair->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/keypairs.html b/doc/_build/html/services/compute/keypairs.html new file mode 100644 index 000000000..c38987e1a --- /dev/null +++ b/doc/_build/html/services/compute/keypairs.html @@ -0,0 +1,351 @@ + + + + + + + + + + Keypairs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Keypairs

          +
          +

          Generate a new keypair

          +

          This operation creates a new keypair under a provided name; the public key +value is automatically generated for you.

          +
          // Instantiate empty object
          +$keypair = $service->keypair();
          +
          +// Send to API
          +$keypair->create(array(
          +   'name' => 'jamie_keypair_1'
          +));
          +
          +// Save these!
          +$pubKey = $keypair->getPublicKey();
          +$priKey = $keypair->getPrivateKey();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Upload existing keypair

          +

          This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem.

          +
          $keypair = $service->keypair();
          +
          +// $key needs to be the string content of the key file, not the filename
          +$content = file_get_contents('~/.ssh/id_rsa.pub');
          +
          +$keypair->create(array(
          +   'name'      => 'main_key',
          +   'publicKey' => $content
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List keypairs

          +

          To list all existing keypairs:

          +
          $keys = $service->listKeypairs();
          +
          +foreach ($keys as $key) {
          +
          +}
          +
          +
          +
          +
          +

          Delete keypairs

          +

          To delete a specific keypair:

          +
          $keypair->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/server.html b/doc/_build/html/services/compute/server.html new file mode 100644 index 000000000..b77733469 --- /dev/null +++ b/doc/_build/html/services/compute/server.html @@ -0,0 +1,443 @@ + + + + + + + + + + Servers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Servers

          +
          +

          Setup

          +

          The first thing to do is pass in your credentials and instantiate a Rackspace +client:

          +
          <?php
          +
          +require 'vendor/autoload.php';
          +
          +use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +

          Now, set up the Auto Scale service:

          +
          $service = $client->computeService('{catalogType}', '{region}', '{urlType}');
          +
          +
          +

          {catalogType} is the name of the service, as it appears in the service +catalog. For Rackspace users, this will default to cloudServersOpenStack; for +OpenStack users, you must set your own value since it can depend on your +environment setup.

          +

          {region} is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.

          +

          {urlType} is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either internalURL or publicURL. +The former will execute HTTP transactions over the internal Rackspace network, +reducing latency and the overall bandwidth cost - the caveat is that all of your +resources must be in same region. publicURL, however, which is the default, +will operate over the public Internet and is to be used for multi-region +installations.

          +
          +
          +

          Get server

          +

          The easiest way to retrieve a specific server is by its unique ID:

          +
          $server = $service->server('{serverId}');
          +
          +
          +
          +
          +

          List servers

          +

          You can list servers in two different ways:

          +
            +
          • return an overview of each server (ID, name and links)
          • +
          • return detailed information for each server
          • +
          +

          Knowing which option to use might help save unnecessary bandwidth and +reduce latency.

          +
          // overview
          +$servers = $service->serverList();
          +
          +// detailed
          +$servers = $service->serverList(true);
          +
          +
          +
          +

          URL parameters for filtering servers

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionType
          imageThe image IDstring
          flavorThe flavor IDstring
          nameThe server namestring
          statusThe server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult OpenCloud\Compute\Constants\ServerStatestring
          changes-sinceValue for checking for changes since a previous requestA valid ISO 8601 dateTime (2011-01-24T17:08Z)
          RAX-SI:image_scheduleIf scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule.bool
          +
          +
          +
          +

          Create server

          +
          +

          Using an image

          +

          Now we’re ready to create our instance:

          +
          $server = $compute->server();
          +
          +$server->create(array(
          +    'name'     => 'My lovely server',
          +    'imageId'  => '{imageId}',
          +    'flavorId' => '{flavorId}',
          +));
          +
          +
          +

          It’s always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner.

          +
          +
          +

          Using a bootable volume

          +

          Firstly we need to find our volume using their IDs.

          +
          $bootableVolume = $client->volumeService()->volume('{volumeId}');
          +
          +
          +

          Now we’re ready to create our instance:

          +
          $server = $compute->server();
          +
          +$response = $server->create(array(
          +    'name'     => 'My lovely server',
          +    'volume'   => $bootableVolume,
          +    'flavorId' => '{flavorId}'
          +));
          +
          +
          +

          It’s always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner.

          +
          +
          +

          Create parameters

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionTypeRequired
          nameThe server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync.stringYes
          flavorA populated OpenCloud\Compute\Resource\Flavor object representing your chosen flavorobjectYes
          imageA populated OpenCloud\Compute\Resource\Image object representing your chosen imageobjectNo, if volume is specified
          volumeA populated OpenCloud\Volume\Resource\Volume object representing your chosen bootable volumeobjectNo, if image is specified
          volumeDeleteOnTerminationtrue if the bootable volume should be deleted when the server is terminated; false, otherwisebooleanNo; default = false
          OS-DCF:diskConfigThe disk configuration value. You can use two options: AUTO or MANUAL. AUTO means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. AUTO is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.MANUAL means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration.stringNo
          networksAn array of populated OpenCloud\Compute\Resource\Network objects that indicate which networks your instance resides in.arrayNo
          metadataAn array of arbitrary data (key-value pairs) that adds additional meaning to your server.arrayNo
          keypairYou can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication.arrayNo
          personalityFiles that you can upload to your newly created instance’s filesystem.arrayNo
          +
          +
          +

          Creating a server with keypairs

          +

          In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter:

          +
          $server = $compute->server();
          +
          +$server->create(array(
          +    'name'     => 'New server',
          +    'imageId'  => '{imageId}',
          +    'flavorId' => '{flavorId}',
          +    'keypair'  => 'main_key'
          +));
          +
          +
          +

          So, as you can see, you specify the name of an existing keypair that +you previously created on the API.

          +
          +
          +

          Creating a server with personality files

          +

          Before you execute the create operation, you can add “personality” files +to your OpenCloud\Compute\Resource\Server object. These files are +structured as a flat array.

          +
          $server->addFile('/var/test_file', 'FILE CONTENT');
          +
          +
          +

          As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the official +documentation.

          +
          +
          +
          +

          Update server

          +

          You can update certain attributes of an existing server instance. These +attributes are detailed in the next section.

          +
          $server->update(array(
          +   'name' => 'NEW SERVER NAME'
          +));
          +
          +
          +
          +

          Updatable attributes

          + ++++ + + + + + + + + + + + + + + + + +
          namedescription
          nameThe name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique.
          accessIPv4The IP version 4 address.
          accessIPv6The IP version 6 address.
          +
          +
          +
          +

          Delete server

          +
          $server->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/servers.html b/doc/_build/html/services/compute/servers.html new file mode 100644 index 000000000..2f313f538 --- /dev/null +++ b/doc/_build/html/services/compute/servers.html @@ -0,0 +1,580 @@ + + + + + + + + + + Servers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Servers

          +
          +

          Get server

          +

          The easiest way to retrieve a specific server is by its unique ID:

          +
          $server = $service->server('{serverId}');
          +
          +
          +
          +
          +

          List servers

          +

          You can list servers in two different ways:

          +
            +
          • return an overview of each server (ID, name and links)
          • +
          • return detailed information for each server
          • +
          +

          Knowing which option to use might help save unnecessary bandwidth and +reduce latency.

          +
          // overview
          +$servers = $service->serverList();
          +
          +// detailed
          +$servers = $service->serverList(true);
          +
          +
          +
          +

          URL parameters for filtering servers

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionType
          imageThe image IDstring
          flavorThe flavor IDstring
          nameThe server namestring
          statusThe server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult OpenCloud\Compute\Constants\ServerStatestring
          changes-sinceValue for checking for changes since a previous requestA valid ISO 8601 dateTime (2011-01-24T17:08Z)
          RAX-SI:image_scheduleIf scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule.bool
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Create server

          +
          +

          Using an image

          +

          Now we’re ready to create our instance:

          +
          $server = $compute->server();
          +
          +$server->create(array(
          +    'name'     => 'My lovely server',
          +    'imageId'  => '{imageId}',
          +    'flavorId' => '{flavorId}',
          +));
          +
          +
          +

          It’s always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Using a bootable volume

          +

          Firstly we need to find our volume using their IDs.

          +
          $bootableVolume = $client->volumeService()->volume('{volumeId}');
          +
          +
          +

          Now we’re ready to create our instance:

          +
          $server = $compute->server();
          +
          +$response = $server->create(array(
          +    'name'     => 'My lovely server',
          +    'volume'   => $bootableVolume,
          +    'flavorId' => '{flavorId}'
          +));
          +
          +
          +

          It’s always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Create parameters

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionTypeRequired
          nameThe server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync.stringYes
          flavorA populated OpenCloud\Compute\Resource\Flavor object representing your chosen flavorobjectYes
          imageA populated OpenCloud\Compute\Resource\Image object representing your chosen imageobjectNo, if volume is specified
          volumeA populated OpenCloud\Volume\Resource\Volume object representing your chosen bootable volumeobjectNo, if image is specified
          volumeDeleteOnTerminationtrue if the bootable volume should be deleted when the server is terminated; false, otherwisebooleanNo; default = false
          OS-DCF:diskConfigThe disk configuration value. You can use two options: AUTO or MANUAL. AUTO means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. AUTO is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.MANUAL means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration.stringNo
          networksAn array of populated OpenCloud\Compute\Resource\Network objects that indicate which networks your instance resides in.arrayNo
          metadataAn array of arbitrary data (key-value pairs) that adds additional meaning to your server.arrayNo
          keypairYou can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication.arrayNo
          personalityFiles that you can upload to your newly created instance’s filesystem.arrayNo
          +
          +
          +

          Creating a server with keypairs

          +

          In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter:

          +
          $server = $compute->server();
          +
          +$server->create(array(
          +    'name'     => 'New server',
          +    'imageId'  => '{imageId}',
          +    'flavorId' => '{flavorId}',
          +    'keypair'  => 'main_key'
          +));
          +
          +
          +

          So, as you can see, you specify the name of an existing keypair that +you previously created on the API.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Creating a server with personality files

          +

          Before you execute the create operation, you can add “personality” files +to your OpenCloud\Compute\Resource\Server object. These files are +structured as a flat array.

          +
          $server->addFile('/var/test_file', 'FILE CONTENT');
          +
          +
          +

          As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the official +documentation.

          +
          +
          +
          +

          Update server

          +

          You can update certain attributes of an existing server instance. These +attributes are detailed in the next section.

          +
          $server->update(array(
          +   'name' => 'NEW SERVER NAME'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +

          Updatable attributes

          + ++++ + + + + + + + + + + + + + + + + +
          namedescription
          nameThe name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique.
          accessIPv4The IP version 4 address.
          accessIPv6The IP version 6 address.
          +
          +
          +

          Updating the access IP address(es)

          +

          For example, you may have a private cloud with internal addresses in the +10.1.x range. However, you can access a server via a firewall device at +address 50.57.94.244. In this case, you can change the accessIPv4 +attribute to point to the firewall:

          +
          $server->update(array('accessIPv4' => '50.57.94.244'));
          +
          +
          +

          When a client application retrieves the server’s information, it will +know that it needs to use the accessIPv4 address to connect to the +server, and not the IP address assigned to one of the network +interfaces.

          +
          +
          +
          +

          Retrieving the server’s IP address

          +

          The Server::ip() method is used to retrieve the server’s IP address. +It has one optional parameter: the format (either IPv4 or IPv6) of the +address to return (by default, it returns the IPv4 address):

          +
          // IPv4
          +echo $server->ip();
          +echo $server->ip(4);
          +
          +// IPv6
          +echo $server->ip(6);
          +
          +
          +
          +
          +

          Delete server

          +
          $server->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/compute/service.sample.html b/doc/_build/html/services/compute/service.sample.html new file mode 100644 index 000000000..1eaec4bfc --- /dev/null +++ b/doc/_build/html/services/compute/service.sample.html @@ -0,0 +1,211 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +

          The first thing to do is pass in your credentials and instantiate a Rackspace +client:

          +
          <?php
          +
          +require 'vendor/autoload.php';
          +
          +use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +

          Now, set up the Auto Scale service:

          +
          $service = $client->computeService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +

          {catalogName} is the name of the service, as it appears in the service +catalog. For Rackspace users, a default will be provided if you pass null +in for this argument. For OpenStack users, you cannot do this: you must instead +set your own value since it can depend on your environment setup.

          +

          {region} is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.

          +

          {urlType} is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either internalURL or +publicURL. The former will execute HTTP transactions over the internal +network configured for your service, possibly reducing latency and the overall +bandwidth cost - the caveat is that all of your resources must be in same region. +publicURL, however, which is the default, will operate over the public +Internet and is to be used for multi-region installations.

          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/README.md.html b/doc/_build/html/services/database/README.md.html new file mode 100644 index 000000000..c08ebcfc7 --- /dev/null +++ b/doc/_build/html/services/database/README.md.html @@ -0,0 +1,274 @@ + + + + + + + + + + Databases — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Databases

          +

          A cloud database is a MySQL relational database service that allows +customers to programatically provision database instances of varying +virtual resource sizes without the need to maintain and/or update MySQL.

          +
          +

          Getting started

          +
          +

          1. Instantiate a Rackspace client.

          +
          use OpenCloud\Rackspace;
          +use OpenCloud\Common\Constants\State;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
          +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
          +));
          +
          +
          +
          +
          +

          2. Create a database server instance.

          +
          $databaseService = $client->databaseService('cloudDatabases', 'DFW');
          +
          +$twoGbFlavor = $databaseService->flavor(3);
          +
          +$dbInstance = $databaseService->instance();
          +$dbInstance->name = 'Demo database instance';
          +$dbInstance->volume = new stdClass();
          +$dbInstance->volume->size = 20; // GB
          +$dbInstance->flavor = $twoGbFlavor;
          +$dbInstance->create();
          +
          +$dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) {
          +
          +    printf("Database instance build status: %s\n", $dbInstance->status);
          +
          +});
          +
          +
          +

          The example above creates a database server instance with 20GB of disk +space and 2GB of memory, then waits for it to become ACTIVE.

          +
          +
          +

          3. Create a database on the database server instance.

          +
          $db = $dbInstance->database();
          +$db->name = 'demo_db';
          +
          +$db->create();
          +
          +
          +

          The example above creates a database named demo_db on the database +server instance created in the previous step.

          +
          +
          +

          4. Create database user and give it access to database.

          +
          $user = $dbInstance->user();
          +$user->name = 'demo_user';
          +$user->password = 'h@X0r!';
          +$user->databases = array('demo_db');
          +
          +$user->create();
          +
          +
          +

          The example above creates a database user named demo_user, sets its +password and gives it access to the demo_db database created in the +previous step.

          +
          +
          +

          5. Optional step: Create a load balancer to allow access to the database from the Internet.

          +

          The database created in the previous step can only be accessed from the +Rackspace private network (aka SERVICENET). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance.

          +

          If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer.

          +
          $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW');
          +
          +$loadBalancer = $loadBalancerService->loadBalancer();
          +
          +$loadBalancer->name = 'Load balancer - DB';
          +$loadBalancer->addNode($dbInstance->hostname, 3306);
          +$loadBalancer->port = 3306;
          +$loadBalancer->protocol = 'MYSQL';
          +$loadBalancer->addVirtualIp('PUBLIC');
          +
          +$loadBalancer->create();
          +
          +$loadBalancer->waitFor(State::ACTIVE, null, function ($lb) {
          +    printf("Load balancer build status: %s\n", $lb->status);
          +});
          +
          +foreach ($loadBalancer->virtualIps as $vip) {
          +    if ($vip->type == 'PUBLIC') {
          +        printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address);
          +    }
          +}
          +
          +
          +

          In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the PUBLIC network address +space so that this load balancer may receive connections from the +Internet.

          +

          Once the load balancer is created and becomes ACTIVE, it’s +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3.

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/configurations.html b/doc/_build/html/services/database/configurations.html new file mode 100644 index 000000000..1771760cd --- /dev/null +++ b/doc/_build/html/services/database/configurations.html @@ -0,0 +1,381 @@ + + + + + + + + + + Configurations — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Configurations

          +
          +

          Creating a configuration

          +
          /** @var $configuration OpenCloud\Database\Resource\Configuration **/
          +$configuration = $service->configuration();
          +
          +$configuration->create(array(
          +    'name'        => 'example-configuration-name',
          +    'description' => 'An example configuration',
          +    'values'      => array(
          +        'collation_server' => 'latin1_swedish_ci',
          +        'connect_timeout' => 120
          +    ),
          +    'datastore' => array(
          +        'type'    => '10000000-0000-0000-0000-000000000001',
          +        'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866'
          +    )
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Listing configurations

          +

          You can list out all the configurations you have created as shown below:

          +
          $configurations = $service->configurationList();
          +foreach ($configurations as $configuration) {
          +    /** @var $configuration OpenCloud\Database\Resource\Configuration **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieving a configuration

          +

          You can retrieve a specific configuration, using its ID, as shown below:

          +
          $configuration = $service->configuration('{configId}');
          +/** @var OpenCloud\Database\Resource\Configuration **/
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Updating a configuration

          +

          You have two choices when updating a configuration:

          + +

          some configuration parameters +* you can entirely replace a configuration to +replace all configuration parameters with new ones

          +
          +

          Patching a configuration

          +

          You can patch a configuration as shown below:

          +
          $configuration->patch(array(
          +    'values' => array(
          +        'connect_timeout' => 30
          +    )
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Replacing a configuration

          +

          You can replace a configuration as shown below:

          +
          $configuration->update(array(
          +    'values' => array(
          +        'collation_server' => 'utf8_general_ci',
          +        'connect_timeout' => 60
          +    )
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Deleting a configuration

          +
          $configuration->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +

          Note

          +

          You cannot delete a configuration if it is in use by a running instance.

          +
          +
          +
          +

          Listing instances using a configuration

          +

          You can list all instances using a specific configuration, using its ID, +as shown below:

          +
          $instances = $configuration->instanceList();
          +foreach ($instances as $instance) {
          +    /** @var $instance OpenCloud\Database\Resource\Instance **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/databases.html b/doc/_build/html/services/database/databases.html new file mode 100644 index 000000000..e9d9278bc --- /dev/null +++ b/doc/_build/html/services/database/databases.html @@ -0,0 +1,344 @@ + + + + + + + + + + Databases — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Databases

          +
          +

          Setup

          +

          In order to interact with the functionality of databases, you must first +retrieve the details of the instance itself. To do this, you must substitute +{instanceId} for your instance’s ID:

          +
          $instance = $service->instance('{instanceId}');
          +
          +
          +
          +
          +

          Creating a new database

          +

          To create a new database, you must supply it with a name; you can +optionally specify its character set and collating sequence:

          +
          // Create an empty object
          +$database = $instance->database();
          +
          +// Send to API
          +$database->create(array(
          +    'name'          => 'production',
          +    'character_set' => 'utf8',
          +    'collate'       => 'utf8_general_ci'
          +));
          +
          +
          +

          You can find values for character_set and collate at the MySQL +website.

          +
          +
          +

          Deleting a database

          +
          $database->delete();
          +
          +
          +
          +

          Note

          +

          This is a destructive operation: all your data will be wiped away and will +not be retrievable.

          +
          +
          +
          +

          Listing databases

          +
          $databases = $service->databaseList();
          +
          +foreach ($databases as $database) {
          +    /** @param $database OpenCloud\Database\Resource\Database */
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/datastores.html b/doc/_build/html/services/database/datastores.html new file mode 100644 index 000000000..8a4033dfe --- /dev/null +++ b/doc/_build/html/services/database/datastores.html @@ -0,0 +1,338 @@ + + + + + + + + + + Datastores — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Datastores

          +
          +

          Listing datastores

          +

          You can list out all the datastores available as shown below:

          +
          $datastores = $service->datastoreList();
          +foreach ($datastores as $datastore) {
          +    /** @var $datastore OpenCloud\Database\Resource\Datastore **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieving a datastore

          +

          You can retrieve a specific datastore’s information, using its ID, as +shown below:

          +
          /** @var OpenCloud\Database\Resource\Datastore **/
          +$datastore = $service->datastore('{datastoreId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Listing datastore versions

          +

          You can list out all the versions available for a specific datastore, as +shown below:

          +
          $versions = $datastore->versionList();
          +foreach ($versions as $version) {
          +    /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieving a datastore version

          +

          You a retrieve a specific datastore version, using its ID, as shown +below:

          +
          $datastoreVersion = $datastore->version('{versionId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/index.html b/doc/_build/html/services/database/index.html new file mode 100644 index 000000000..4a34553a0 --- /dev/null +++ b/doc/_build/html/services/database/index.html @@ -0,0 +1,424 @@ + + + + + + + + + + Databases v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Databases v1

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Databases service

          +

          Now to instantiate the Databases service:

          +
          $service = $client->databaseService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          configuration group
          +
          A configuration group is a collection of key/value pairs which configure a +database instance. Some directives are capable of being applied dynamically, +while other directives require a server restart to take effect. The +configuration group can be applied to an instance at creation or applied to +an existing instance to modify the behavior of the running datastore on the +instance.
          +
          flavor
          +
          A flavor is an available hardware configuration for a database instance. +Each flavor has a unique combination of memory capacity and priority for +CPU time.
          +
          instance
          +
          A database instance is an isolated MySQL instance in a single tenant +environment on a shared physical host machine. Also referred to as +instance.
          +
          database
          +
          A database is a local MySQL database running on an instance.
          +
          user
          +
          A user is a local MySQL user that can access a database running on an +instance.
          +
          datastore
          +
          The database engine running on your instance. Currently, there is support +for MySQL 5.6, MySQL 5.1, Percona 5.6 and MariaDB 10.
          +
          volume
          +
          A volume is user-specified storage that contains the database engine data +directory. Volumes are automatically provisioned on shared Internet Small +Computer System Interface (iSCSI) storage area networks (SAN) that provide +for increased performance, scalability, availability and manageability. +Applications with high I/O demands are performance optimized and data is +protected through both local and network RAID-10.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/instances.html b/doc/_build/html/services/database/instances.html new file mode 100644 index 000000000..d4b28f840 --- /dev/null +++ b/doc/_build/html/services/database/instances.html @@ -0,0 +1,422 @@ + + + + + + + + + + Instances — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Instances

          +
          +

          Create a new instance

          +
          // Create an empty object
          +$instance = $service->instance();
          +
          +// Send to the API
          +$instance->create(array(
          +    'name'   => '{name}',
          +    'flavor' => $service->flavor('{flavorId}'),
          +    'volume' => array('size' => 4)               // 4GB of volume disk
          +));
          +
          +
          +

          Get the executable PHP script for this sample

          +
          +

          Waiting for the instance to build

          +

          The SDK provides a blocking operation that will wait until your instance resource +has transitioned into an ACTIVE state. During this period, it will +continuously poll the API and break the loop when the state has been achieved:

          +
          $instance->waitFor('ACTIVE', null, function ($instance) {
          +    // This will be executed continuously
          +    printf("Database instance build status: %s\n", $instance->status);
          +});
          +
          +
          +
          +
          +

          Connecting an instance to a load balancer

          +

          The instance created in the previous step can only be accessed from the +Rackspace private network (aka SERVICENET). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance.

          +

          If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer.

          +
          $lbService = $client->loadBalancerService(null, '{region}');
          +
          +// Create empty object
          +$loadBalancer = $lbService->loadBalancer();
          +
          +// Associate this LB with the instance as a "node"
          +$loadBalancer->addNode($instance->hostname, 3306);
          +$loadBalancer->addVirtualIp('PUBLIC');
          +
          +// Configure other parameters and send to the API
          +$loadBalancer->create(array(
          +  'name'     => 'DB Load Balancer',
          +  'port'     => 3306,
          +  'protocol' => 'MYSQL',
          +));
          +
          +// Wait for the resource to create
          +$loadBalancer->waitFor('ACTIVE', null, function ($loadBalancer) {
          +    printf("Load balancer build status: %s\n", $loadBalancer->status);
          +});
          +
          +foreach ($loadBalancer->virtualIps as $vip) {
          +    if ($vip->type == 'PUBLIC') {
          +        printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address);
          +    }
          +}
          +
          +
          +

          In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the PUBLIC network address +space so that this load balancer may receive connections from the +Internet.

          +

          Once the load balancer is created and becomes ACTIVE, it’s +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3.

          +
          +
          +
          +

          Retrieving an instance

          +
          $instance = $service->instance('{instanceId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Updating an instance

          +

          An instance can be updated to use a specific configuration as shown below.

          +
          $instance->update(array(
          +    'configuration' => '{configurationId}'
          +));
          +
          +
          +
          +

          Note

          +

          If any parameters in the associated configuration require a restart, then you +will need to restart the instance after the update.

          +
          +
          +
          +

          Deleting an instance

          +
          $instance->delete();
          +
          +
          +
          +
          +

          Restarting an instance

          +
          $instance->restart();
          +
          +
          +
          +
          +

          Resizing an instance’s RAM

          +

          To change the amount of RAM allocated to the instance:

          +
          $flavor = $service->flavor('{flavorId}');
          +$instance->resize($flavor);
          +
          +
          +
          +
          +

          Resizing an instance’s volume

          +

          You can also independently change the volume size to increase the disk +space:

          +
          // Increase to 8GB disk
          +$instance->resizeVolume(8);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/service.sample.html b/doc/_build/html/services/database/service.sample.html new file mode 100644 index 000000000..578571553 --- /dev/null +++ b/doc/_build/html/services/database/service.sample.html @@ -0,0 +1,211 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +

          The first thing to do is pass in your credentials and instantiate a Rackspace +client:

          +
          <?php
          +
          +require 'vendor/autoload.php';
          +
          +use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +

          Now, set up the Database service:

          +
          $service = $client->databaseService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +

          {catalogName} is the name of the service, as it appears in the service +catalog. For Rackspace users, a default will be provided if you pass null +in for this argument. For OpenStack users, you cannot do this: you must instead +set your own value since it can depend on your environment setup.

          +

          {region} is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.

          +

          {urlType} is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either internalURL or +publicURL. The former will execute HTTP transactions over the internal +network configured for your service, possibly reducing latency and the overall +bandwidth cost - the caveat is that all of your resources must be in same region. +publicURL, however, which is the default, will operate over the public +Internet and is to be used for multi-region installations.

          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/database/users.html b/doc/_build/html/services/database/users.html new file mode 100644 index 000000000..c3bb667a6 --- /dev/null +++ b/doc/_build/html/services/database/users.html @@ -0,0 +1,347 @@ + + + + + + + + + + Users — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Users

          +
          +

          Setup

          +

          Finally, in order to interact with the functionality of databases, you must +first retrieve the details of the instance itself. To do this, you must +substitute {instanceId} for your instance’s ID:

          +
          $instance = $service->instance('{instanceId}');
          +
          +
          +
          +
          +

          Creating users

          +

          Database users exist at the Instance level, but can be associated +with a specific Database. They are represented by the +OpenCloud\Database\Resource\User class.

          +
          // New instance of OpenCloud\Database\Resource\User
          +$user = $instance->user();
          +
          +// Send to API
          +$user->create(array(
          +    'name'      => 'Alice',
          +    'password'  => 'fooBar'
          +    'databases' => array('production')
          +));
          +
          +
          +
          +
          +

          Deleting a user

          +
          $user->delete();
          +
          +
          +
          +
          +

          The root user

          +

          By default, Cloud Databases does not enable the root user. In most +cases, the root user is not needed, and having one can leave you open to +security violations. However, if you do want to enable access to the root user:

          +
          $rootUser = $instance->enableRootUser();
          +
          +
          +

          This returns a regular User object with the name attribute set +to root and the password attribute set to an auto-generated +password.

          +
          +
          +

          Check if root user is enabled

          +
          // true for yes, false for no
          +$instance->isRootEnabled();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/Domains.md.html b/doc/_build/html/services/dns/Domains.md.html new file mode 100644 index 000000000..25de5f93e --- /dev/null +++ b/doc/_build/html/services/dns/Domains.md.html @@ -0,0 +1,451 @@ + + + + + + + + + + Domains — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Domains

          +

          A domain is an entity/container of all DNS-related information +containing one or more records.

          +
          +

          Setup

          +

          Limit methods will be called on the DNS service, an instance of +OpenCloud\DNS\Service. Please see the DNS service +documentation for setup instructions.

          +
          +
          +

          Get domain

          +

          To retrieve a specific domain, you will need the domain’s id, not +its domain name.

          +
          $domain = $service->domain(12345);
          +
          +
          +

          If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID.

          +
          +
          +

          List domains

          +

          These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list.

          +
          $domains = $service->domainList();
          +
          +# Return detailed information for each domain
          +$domains = $service->domainList(true);
          +
          +
          +

          Please consult the iterator +documentation for more information +about iterators.

          +
          +

          Filter parameters

          +

          You can filter the aforementioned search by using the name parameter +in a key/value array supplied as a method argument. For example, +providing array('name' => 'hoola.com') will return hoola.com and +similar names such as main.hoola.com and sub.hoola.com.

          +
          $hoolaDomains = $service->domainList(array(
          +    'name' => 'hoola.com'
          +));
          +
          +
          +

          Filter criteria may consist of:

          +
            +
          • Any letter (A-Za-z)
          • +
          • Numbers (0-9)
          • +
          • Hyphen (“-”)
          • +
          • 1 to 63 characters
          • +
          +

          Filter criteria should not include any of the following characters:

          +
          +
          ‘ + , | ! ” £ $ % & / ( ) = ? ^ * ç ° § ; : _ > ] [ @ à, é, ò
          +
          +
          +

          Finding a domain ID

          +

          If you know a domain’s name, but not its unique identifier, you can do +this:

          +
          $domains = $service->domainList(array(
          +    'name' => 'foo.com'
          +));
          +
          +foreach ($domains as $domain) {
          +    $id = $domain->id;
          +}
          +
          +
          +
          +
          +
          +

          List domain changes

          +

          This call shows all changes to the specified domain since the specified +date/time. The since parameter is optional and defaults to midnight of +the current day.

          +
          $changes = $domain->changes();
          +
          +# Changes since last week
          +$since = date('c', strtotime('last week'));
          +$changes = $domain->changes($since);
          +
          +foreach ($changes->changes as $change) {
          +    printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType);
          +
          +    foreach ($change->changeDetails as $detail) {
          +        printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue);
          +    }
          +}
          +
          +
          +
          +
          +

          Export domain

          +

          This call provides the BIND (Berkeley Internet Name Domain) 9 formatted +contents of the requested domain. This call is for a single domain only, +and as such, does not traverse up or down the domain hierarchy for +details (that is, no subdomain information is provided).

          +
          $asyncResponse = $domain->export();
          +$body = $asyncResponse->waitFor('COMPLETED');
          +echo $body['contents'];
          +
          +
          +
          +
          +

          Create domain

          +

          A domain is composed of DNS records (e.g. A, CNAME or MX +records) and an optional list of sub-domains. You will need to specify +these before creating the domain itself:

          +
          // get empty object
          +$domain = $service->domain();
          +
          +// add A record
          +$aRecord = $domain->record(array(
          +    'type' => 'A',
          +    'name' => 'example.com',
          +    'data' => '192.0.2.17',
          +    'ttl'  => 3600
          +));
          +$domain->addRecord($aRecord);
          +
          +// add optional C record
          +$cRecord = $domain->record(array(
          +    'type' => 'CNAME',
          +    'name' => 'www.example.com',
          +    'data' => 'example.com',
          +    'ttl'  => 3600
          +));
          +$domain->addRecord($cRecord);
          +
          +// add optional MX record
          +$mxRecord = $domain->record(array(
          +    'type' => 'MX',
          +    'data' => 'mail.example.com',
          +    'name' => 'example.com',
          +    'ttl'  => 3600,
          +    'priority' => 5
          +));
          +$domain->addRecord($mxRecord);
          +
          +// add optional NS records
          +$nsRecord1 = $domain->record(array(
          +    'type' => 'NS',
          +    'data' => 'dns1.stabletransit.com',
          +    'name' => 'example.com',
          +    'ttl'  => 5400
          +));
          +$domain->addRecord($nsRecord1);
          +
          +$nsRecord2 = $domain->record(array(
          +    'type' => 'NS',
          +    'data' => 'dns2.stabletransit.com',
          +    'name' => 'example.com',
          +    'ttl'  => 5400
          +));
          +$domain->addRecord($nsRecord2);
          +
          +// add optional subdomains
          +$sub1 = $domain->subdomain(array(
          +    'emailAddress' => 'foo@example.com',
          +    'name'         => 'dev.example.com',
          +    'comment'      => 'Dev portal'
          +));
          +$domain->addSubdomain($sub1);
          +
          +// send to API
          +$domain->create(array(
          +    'emailAddress' => 'webmaster@example.com',
          +    'ttl'          => 3600,
          +    'name'         => 'example.com',
          +    'comment'      => 'Optional comment'
          +));
          +
          +
          +
          +
          +

          Clone domain

          +

          This call will duplicate a single existing domain configuration with a +new domain name for the specified Cloud account. By default, all records +and, optionally, subdomain(s) are duplicated as well.

          +

          The method signature you will need to use is:

          +
          cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] )
          +
          +
          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameData typeDefaultDescription
          $newDomainNamestring
            +
          • +
          +
          The new name for your cloned domain
          $subdomainsbooltrueSet to TRUE to clone all the subdomains for this domain
          $commentsbooltrueSet to TRUE to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain.
          $emailbooltrueSet to TRUE to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records.
          +

          For example:

          +
          $asyncResponse = $domain->cloneDomain('new-name.com', true);
          +
          +
          +
          +
          +

          Import domain

          +

          This call provisions a new DNS domain under the account specified by the +BIND 9 formatted file configuration contents defined in the request +object.

          +

          You will need to ensure that the BIND 9 formatted file configuration +contents are valid by adhering to the following rules:

          +
            +
          • Each record starts on a new line and on the first column. If a record +will not fit on one line, use the BIND_9 line continuation +convention where you put a left parenthesis and continue the one +record on the next line and put a right parenthesis when the record +ends. For example,

            +
            +

            example2.net. 3600 IN SOA dns1.stabletransit.com. ( +sample@rackspace.com. 1308874739 3600 3600 3600 3600)

            +
            +
          • +
          • The attribute values of a record must be separated by a single blank +or tab. No other white space characters.

            +
          • +
          • If there are any NS records, the data field should not be +dns1.stabletransit.com or dns2.stabletransit.com. They will result in +“duplicate record” errors.

            +
          • +
          +

          For example:

          +
          $bind9Data = <<<EOT
          +\nexample.net. 3600 IN SOA dns1.stabletransit.com. sample@rackspace.com. 1308874739 3600 3600 3600 3600\nexample.net. 86400 IN A 110.11.12.16\nexample.net. 3600 IN MX 5 mail2.example.net.\nwww.example.net. 5400 IN CNAME example.net.\n
          +EOT;
          +
          +$asyncResponse = $service->import($bind9Data);
          +
          +
          +
          +
          +

          Modify domain

          +

          This call modifies DNS domain(s) attributes only. Only the TTL, email +address and comment attributes of a domain can be modified. Records +cannot be added, modified, or removed through this API operation - you +will need to use the add +records, modify +records or remove +records operations +respectively.

          +
          $domain->update(array(
          +    'ttl'          => ($domain->ttl + 100),
          +    'emailAddress' => 'new_dev@foo.com'
          +));
          +
          +
          +
          +
          +

          Remove domain

          +
          $domain->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/Limits.md.html b/doc/_build/html/services/dns/Limits.md.html new file mode 100644 index 000000000..e23fbe087 --- /dev/null +++ b/doc/_build/html/services/dns/Limits.md.html @@ -0,0 +1,221 @@ + + + + + + + + + + Limits — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Limits

          +
          +

          Setup

          +

          Limit methods will be called on the DNS service, an instance of +OpenCloud\DNS\Service. Please see the DNS service +documentation for setup instructions.

          +
          +
          +

          List all limits

          +

          This call provides a list of all applicable limits for the specified +account.

          +
          $limits = $service->limits();
          +
          +
          +
          +

          Absolute limits

          +

          There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain:

          +
          $absoluteLimits = $limits->absolute;
          +
          +# Domain limit
          +echo $absoluteLimits->domains;
          +
          +# Record limit per domain
          +echo $absoluteLimits->{'records per domain'};
          +
          +
          +
          +
          +
          +

          List limit types

          +

          To find out the different limit types you can query, run:

          +
          $limitTypes = $service->limitTypes();
          +
          +
          +

          will return:

          +
          array(3) {
          +  [0] =>
          +  string(10) "RATE_LIMIT"
          +  [1] =>
          +  string(12) "DOMAIN_LIMIT"
          +  [2] =>
          +  string(19) "DOMAIN_RECORD_LIMIT"
          +}
          +
          +
          +
          +
          +

          Query a specific limit

          +
          $limit = $service->limits('DOMAIN_LIMIT');
          +
          +echo $limit->absolute->limits->value;
          +
          +>>> 500
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/Records.md.html b/doc/_build/html/services/dns/Records.md.html new file mode 100644 index 000000000..5f63aa7dd --- /dev/null +++ b/doc/_build/html/services/dns/Records.md.html @@ -0,0 +1,245 @@ + + + + + + + + + + Records — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Records

          +

          A DNS record belongs to a particular domain and is used to specify +information about the domain.

          +

          There are several types of DNS records. Examples include mail exchange +(MX) records, which specify the mail server for a particular domain, and +name server (NS) records, which specify the authoritative name servers +for a domain.

          +

          It is represented by the OpenCloud\DNS\Resource\Record class. +Records belong to a Domain.

          +
          +

          Get record

          +

          In order to retrieve details for a specific DNS record, you will need +its id:

          +
          $record = $domain->record('NS-1234567');
          +
          +
          +

          If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below).

          +
          +
          +

          List records

          +

          This call lists all records configured for the specified domain.

          +
          $records = $domain->recordList();
          +
          +foreach ($records as $record) {
          +    printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl);
          +}
          +
          +
          +

          Please consult the iterator +documentation for more information +about iterators.

          +
          +

          Query parameters

          +

          You can pass in an array of query parameters for greater control over +your search:

          +
          +
          +

          Find a record ID from its name

          +

          For example:

          +
          $records = $domain->recordList(array(
          +    'name' => 'imap.example.com',
          +    'type' => 'MX'
          +));
          +
          +foreach ($records as $record) {
          +    $recordId = $record->id;
          +}
          +
          +
          +
          +
          +
          +

          Add record

          +

          This call adds a new record to the specified domain:

          +
          $record = $domain->record(array(
          +    'type' => 'A',
          +    'name' => 'example.com',
          +    'data' => '192.0.2.17',
          +    'ttl'  => 3600
          +));
          +
          +$record->create();
          +
          +
          +

          Please be aware that records that are added with a different hostname +than the parent domain might fail silently.

          +
          +
          +

          Modify record

          +
          $record = $domain->record(123456);
          +$record->ttl -= 100;
          +$record->update();
          +
          +
          +
          +
          +

          Delete record

          +
          $record->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/Reverse-DNS.md.html b/doc/_build/html/services/dns/Reverse-DNS.md.html new file mode 100644 index 000000000..a2c3692d2 --- /dev/null +++ b/doc/_build/html/services/dns/Reverse-DNS.md.html @@ -0,0 +1,268 @@ + + + + + + + + + + Reverse DNS — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Reverse DNS

          +

          DNS usually determines an IP address associated with a domain name. +Reverse DNS is the opposite process: resolving a domain name from an IP +address. This is usually achieved with a domain name pointer.

          +
          +

          Get PTR record

          +

          PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one’s PTR record:

          +
          /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */
          +
          +$ptr = $service->ptrRecord(array(
          +    'parent' => $parent
          +));
          +
          +
          +

          So, in the above example, a $parent could be an instance of +OpenCloud\Compute\Resource\Server or +OpenCloud\LoadBalancer\Resource\LoadBalancer - because they both +implement OpenCloud\DNS\Resource\HadPtrRecordsInterface. Please +consult the server documentation and load +balancer documentation for more +detailed usage instructions.

          +
          +
          +

          List PTR records

          +
          /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */
          +
          +$ptrRecords = $service->ptrRecordList($parent);
          +
          +foreach ($ptrRecords as $ptrRecord) {
          +
          +}
          +
          +
          +

          Please consult the iterator +documentation for more information +about iterators.

          +
          +
          +

          Add PTR record

          +
          $parent = $computeService->server('foo-server-id');
          +
          +$ptr = $dnsService->ptrRecord(array(
          +    'parent' => $parent,
          +    'ttl'    => 3600,
          +    'name'   => 'example.com',
          +    'type'   => 'PTR',
          +    'data'   => '192.0.2.7'
          +));
          +
          +$ptr->create();
          +
          +
          +

          Here is a table that explains the above attributes:

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired
          typeSpecifies the record type as “PTR”.Yes
          nameSpecifies the name for the domain or subdomain. Must be a valid domain name.Yes
          dataThe data field for PTR records must be a valid IPv4 or IPv6 IP address.Yes
          ttlIf specified, must be greater than 300. Defaults to 3600 if no TTL is specified.No
          commentIf included, its length must be less than or equal to 160 characters.No
          +
          +
          +

          Modify PTR record

          +
          $ptr->update(array(
          +    'ttl' => $ptr->ttl * 2
          +));
          +
          +
          +
          +
          +

          Delete PTR record

          +
          $ptr->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/Service.md.html b/doc/_build/html/services/dns/Service.md.html new file mode 100644 index 000000000..1bf9d94fe --- /dev/null +++ b/doc/_build/html/services/dns/Service.md.html @@ -0,0 +1,171 @@ + + + + + + + + + + DNS Service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          DNS Service

          +

          To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the Clients documentation.

          +

          You will then need to run:

          +
          $service = $client->dnsService();
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/domains.html b/doc/_build/html/services/dns/domains.html new file mode 100644 index 000000000..a79cf3908 --- /dev/null +++ b/doc/_build/html/services/dns/domains.html @@ -0,0 +1,545 @@ + + + + + + + + + + Domains — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Domains

          +
          +

          Get domain

          +

          To retrieve a specific domain, you will need the domain’s id, not +its domain name:

          +
          $domain = $service->domain('{domainId}');
          +
          +
          +

          If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID.

          +
          +
          +

          List domains

          +

          These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list.

          +
          $domains = $service->domainList();
          +
          +# Return detailed information for each domain
          +$domains = $service->domainList(true);
          +
          +
          +
          +

          Filter parameters

          +

          You can filter the search by using the name parameter in a key/value array +supplied as a method argument. For example, to retrieve domains named foo.com, +along with any subdomains like bar.foo.com:

          +
          $hoolaDomains = $service->domainList(array(
          +    'name' => 'foo.com'
          +));
          +
          +
          +

          Filter criteria may consist of:

          +
            +
          • Any letter (A-Za-z)
          • +
          • Numbers (0-9)
          • +
          • Hyphen (“-”)
          • +
          • 1 to 63 characters
          • +
          +

          Filter criteria should not include any of the following characters:

          +
          +
          ‘ + , | ! ” £ $ % & / ( ) = ? ^ * ç ° § ; : _ > ] [ @ à, é, ò
          +
          +
          +

          Finding a domain ID

          +

          Once you have a list of domains, to retrieve a domain’s ID:

          +
          foreach ($domains as $domain) {
          +    $id = $domain->id;
          +}
          +
          +
          +
          +
          +
          +

          List domain changes

          +

          This call shows all changes to the specified domain since the specified +date/time. To list all available changes for a domain for the current day:

          +
          $changes = $domain->changes();
          +
          +
          +

          For more granular control, you can manually define the since parameter like +so:

          +
          $since = date('c', strtotime('last week'));
          +$changes = $domain->changes($since);
          +
          +
          +

          Once you have a set of changes, you can iterate over them like so:

          +
          foreach ($changes->changes as $change) {
          +    printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType);
          +
          +    foreach ($change->changeDetails as $detail) {
          +        printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue);
          +    }
          +}
          +
          +
          +
          +
          +

          Create domain

          +

          The first thing you will need to do is instantiate a new object and set the +primary A record for the DNS domain, like so:

          +
          // get empty object
          +$domain = $service->domain();
          +
          +// add A record
          +$aRecord = $domain->record(array(
          +    'type' => 'A',
          +    'name' => 'example.com',
          +    'data' => '192.0.2.17',
          +    'ttl'  => 3600
          +));
          +
          +$domain->addRecord($aRecord);
          +
          +
          +

          You also have the option of adding more types of DNS records such as CNAME, +MX and NS records. This step is completely optional and depends on +your requirements:

          +
          // add CNAME record
          +$cRecord = $domain->record(array(
          +    'type' => 'CNAME',
          +    'name' => 'www.example.com',
          +    'data' => 'example.com',
          +    'ttl'  => 3600
          +));
          +$domain->addRecord($cRecord);
          +
          +// add MX record
          +$mxRecord = $domain->record(array(
          +    'type' => 'MX',
          +    'data' => 'mail.example.com',
          +    'name' => 'example.com',
          +    'ttl'  => 3600,
          +    'priority' => 5
          +));
          +$domain->addRecord($mxRecord);
          +
          +// add NS record
          +$nsRecord = $domain->record(array(
          +    'type' => 'NS',
          +    'data' => 'dns1.stabletransit.com',
          +    'name' => 'example.com',
          +    'ttl'  => 5400
          +));
          +$domain->addRecord($nsRecord);
          +
          +
          +

          You can also add sub-domains to your new DNS domain. Again, this is completely +optional:

          +
          $subdomain = $domain->subdomain(array(
          +    'emailAddress' => 'foo@example.com',
          +    'name'         => 'dev.example.com',
          +    'comment'      => 'Dev portal'
          +));
          +$domain->addSubdomain($subdomain);
          +
          +
          +

          Once you’ve finished configuring how your DNS domain will work, you’re ready +to specify the essential details and send it to the API for creation:

          +
          $domain->create(array(
          +    'emailAddress' => 'webmaster@example.com',
          +    'ttl'          => 3600,
          +    'name'         => 'example.com',
          +    'comment'      => 'Optional comment'
          +));
          +
          +
          +
          +
          +

          Clone domain

          +

          This call will duplicate an existing domain under a new name. By default, all +records and, optionally, subdomains are duplicated as well.

          +

          The method signature you will need to use is:

          +
          +
          +cloneDomain($newDomainName[, $subdomains[, $comments[, $email[, $records]]]])
          +

          Clone a domain

          + +++ + + + +
          Parameters:
            +
          • $newDomainName (string) – The name of the new domain entry
          • +
          • $subdomains (bool) – Set to true to clone all the subdomains for this domain
          • +
          • $comments (bool) – Set to true to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain.
          • +
          • $email (bool) – Set to true to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records.
          • +
          • $records (bool) – Set to true to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records.
          • +
          +
          +
          + +

          For example:

          +
          $asyncResponse = $domain->cloneDomain('new-name.com', true, false, true, false);
          +
          +
          +
          +
          +

          Export domain

          +

          This call provides access to the BIND +(Berkeley Internet Name Domain) 9 for the requested domain. This call is for a +single domain only, and as such, does not traverse up or down the domain +hierarchy for details:

          +
          $asyncResponse = $domain->export();
          +
          +$body = $asyncResponse->waitFor('COMPLETED');
          +echo $body['contents'];
          +
          +
          +
          +
          +

          Import domain

          +

          This operation will create a new DNS domain according to a BIND +(Berkeley Internet Name Domain) 9 formatted value.

          +

          In order for the BIND value to be considered valid, it needs to adhere to the +following rules:

          +
            +
          • Each record starts on a new line and on the first column. If a record will +not fit on one line, use the BIND_9 line continuation convention where you put +a left parenthesis and continue the one record on the next line and put a right +parenthesis when the record ends. For example:

            +
            +

            example2.net. 3600 IN SOA dns1.stabletransit.com. (sample@rackspace.com. 1308874739 3600 3600 3600 3600)

            +
            +
          • +
          • The attribute values of a record must be separated by a single blank or tab. +No other white space characters.

            +
          • +
          • If there are any NS records, the data field should not be +dns1.stabletransit.com or dns2.stabletransit.com. They will result in +“duplicate record” errors.

            +
          • +
          +

          For example:

          +
          $bind9Data = <<<EOT
          +
          +example.net. 3600 IN SOA dns1.stabletransit.com. sample@rackspace.com. 1308874739 3600 3600 3600 3600
          +example.net. 86400 IN A 110.11.12.16
          +example.net. 3600 IN MX 5 mail2.example.net.
          +www.example.net. 5400 IN CNAME example.net.
          +
          +EOT;
          +
          +$asyncResponse = $service->import($bind9Data);
          +
          +
          +
          +
          +

          Modify domain

          +

          Only the TTL, email address and comment attributes of a domain can be modified. +Records cannot be added, modified, or removed through this API operation - you +will need to use the add records, modify records or remove records +operations respectively.

          +
          $domain->update(array(
          +    'ttl'          => ($domain->ttl + 100),
          +    'emailAddress' => 'new_dev@foo.com'
          +));
          +
          +
          +
          +
          +

          Delete domain

          +
          $domain->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/index.html b/doc/_build/html/services/dns/index.html new file mode 100644 index 000000000..ad5c65bba --- /dev/null +++ b/doc/_build/html/services/dns/index.html @@ -0,0 +1,404 @@ + + + + + + + + + + DNS v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          DNS v1

          +
          +

          Note

          +

          This service is only available for Rackspace users.

          +
          +
          +

          Setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +

          DNS service

          +

          Now to instantiate the DNS service:

          +
          $service = $client->dnsService();
          +
          +
          +
          +
          + +
          +

          Glossary

          +
          +
          +
          domain
          +
          A domain is an entity/container of all DNS-related information containing +one or more records.
          +
          record
          +
          A DNS record belongs to a particular domain and is used to specify +information about the domain. There are several types of DNS records. Each +record type contains particular information used to describe that record’s +purpose. Examples include mail exchange (MX) records, which specify the +mail server for a particular domain, and name server (NS) records, which +specify the authoritative name servers for a domain.
          +
          subdomain
          +
          Subdomains are domains within a parent domain, and subdomains cannot be +registered. Subdomains allow you to delegate domains. Subdomains can +themselves have subdomains, so third-level, fourth-level, fifth-level, and +deeper levels of nesting are possible.
          +
          pointer records
          +
          DNS usually determines an IP address associated with a domain name. +Reverse DNS is the opposite process: resolving a domain name from an IP +address. This is usually achieved with a domain name pointer.
          +
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/limits.html b/doc/_build/html/services/dns/limits.html new file mode 100644 index 000000000..f04387da4 --- /dev/null +++ b/doc/_build/html/services/dns/limits.html @@ -0,0 +1,340 @@ + + + + + + + + + + Limits — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Limits

          +
          +

          List all limits

          +

          This call provides a list of all applicable limits for the specified account.

          +
          $limits = $service->limits();
          +
          +
          +
          +

          Absolute limits

          +

          There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain:

          +
          $absoluteLimits = $limits->absolute;
          +
          +// Domain limit
          +echo $absoluteLimits->domains;
          +
          +// Record limit per domain
          +echo $absoluteLimits->{'records per domain'};
          +
          +
          +
          +
          +
          +

          List limit types

          +

          To find out the different limit types you can query, run:

          +
          $limitTypes = $service->limitTypes();
          +
          +
          +

          will return:

          +
          array(3) {
          +  [0] => string(10) "RATE_LIMIT"
          +  [1] => string(12) "DOMAIN_LIMIT"
          +  [2] => string(19) "DOMAIN_RECORD_LIMIT"
          +}
          +
          +
          +
          +
          +

          Query a specific limit

          +
          $limit = $service->limits('DOMAIN_LIMIT');
          +echo $limit->absolute->limits->value;
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/records.html b/doc/_build/html/services/dns/records.html new file mode 100644 index 000000000..27861ae55 --- /dev/null +++ b/doc/_build/html/services/dns/records.html @@ -0,0 +1,402 @@ + + + + + + + + + + Records — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Records

          +
          +

          Setup

          +

          In order to interact with the functionality of records, you must first +retrieve the details of the domain itself. To do this, you must substitute +{domainId} for your domain’s ID:

          +
          $domain = $service->domain('{domainId}');
          +
          +
          +
          +
          +

          Get record

          +

          In order to retrieve details for a specific DNS record, you will need +its id:

          +
          $record = $domain->record('{recordId}');
          +
          +
          +

          If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below).

          +
          +
          +

          List records

          +

          This call lists all records configured for the specified domain.

          +
          $records = $domain->recordList();
          +
          +foreach ($records as $record) {
          +    printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl);
          +}
          +
          +
          +
          +

          Query parameters

          +

          You can pass in an array of query parameters for greater control over +your search:

          + +++++ + + + + + + + + + + + + + + + + + + + + +
          NameData typeDescription
          typestringThe record type
          namestringThe record name
          datastringData for this record
          +
          +
          +

          Find a record ID from its name

          +

          For example:

          +
          $records = $domain->recordList(array(
          +    'name' => 'imap.example.com',
          +    'type' => 'MX'
          +));
          +
          +foreach ($records as $record) {
          +    $recordId = $record->id;
          +}
          +
          +
          +
          +
          +
          +

          Add record

          +

          This call adds a new record to the specified domain:

          +
          $record = $domain->record(array(
          +    'type' => 'A',
          +    'name' => 'example.com',
          +    'data' => '192.0.2.17',
          +    'ttl'  => 3600
          +));
          +
          +$record->create();
          +
          +
          +

          Please be aware that records that are added with a different hostname +than the parent domain might fail silently.

          +
          +
          +

          Modify record

          +
          $record = $domain->record('{recordId}');
          +$record->ttl -= 100;
          +$record->update();
          +
          +
          +
          +
          +

          Delete record

          +
          $record->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/dns/reverse-dns.html b/doc/_build/html/services/dns/reverse-dns.html new file mode 100644 index 000000000..b5f81390d --- /dev/null +++ b/doc/_build/html/services/dns/reverse-dns.html @@ -0,0 +1,394 @@ + + + + + + + + + + Reverse DNS — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Reverse DNS

          +
          +

          Get PTR record

          +

          PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one’s PTR record:

          +
          /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */
          +
          +$ptr = $service->ptrRecord(array(
          +    'parent' => $parent
          +));
          +
          +
          +

          So, in the above example, the $parent object could be an instance of +OpenCloud\Compute\Resource\Server or +OpenCloud\LoadBalancer\Resource\LoadBalancer - because they both +implement OpenCloud\DNS\Resource\HadPtrRecordsInterface. Please +consult the server documentation and load +balancer documentation for more +detailed usage instructions.

          +
          +
          +

          List PTR records

          +
          /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */
          +
          +$ptrRecords = $service->ptrRecordList($parent);
          +
          +foreach ($ptrRecords as $ptrRecord) {
          +
          +}
          +
          +
          +
          +
          +

          Add PTR record

          +
          $parent = $computeService->server('foo-server-id');
          +
          +$ptr = $dnsService->ptrRecord(array(
          +    'parent' => $parent,
          +    'ttl'    => 3600,
          +    'name'   => 'example.com',
          +    'type'   => 'PTR',
          +    'data'   => '192.0.2.7'
          +));
          +
          +$ptr->create();
          +
          +
          +

          Here is a table that explains the above attributes:

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired
          typeSpecifies the record type as “PTR”.Yes
          nameSpecifies the name for the domain or subdomain. Must be a valid domain name.Yes
          dataThe data field for PTR records must be a valid IPv4 or IPv6 IP address.Yes
          ttlIf specified, must be greater than 300. Defaults to 3600 if no TTL is specified.No
          commentIf included, its length must be less than or equal to 160 characters.No
          +
          +
          +

          Modify PTR record

          +
          $ptr->update(array(
          +    'ttl' => $ptr->ttl * 2
          +));
          +
          +
          +
          +
          +

          Delete PTR record

          +
          $ptr->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/Roles.md.html b/doc/_build/html/services/identity/Roles.md.html new file mode 100644 index 000000000..66da33b10 --- /dev/null +++ b/doc/_build/html/services/identity/Roles.md.html @@ -0,0 +1,273 @@ + + + + + + + + + + Roles — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Roles

          +
          +

          Intro

          +

          A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user’s roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service.

          +
          +
          +

          Setup

          +

          Role objects are instantiated from the Identity service. For more +details, see the Service docs.

          +
          +
          +

          Useful object properties/methods

          + +++++ + + + + + + + + + + + + + + + + + + + + +
          PropertyGetterSetter
          idgetId()setId()
          namegetName()setName()
          descriptiongetDescription()setDescription()
          +
          +
          +

          List roles

          +

          This call lists the global roles available within a specified service.

          +
          $roles = $service->getRoles();
          +
          +foreach ($roles as $role) {
          +   // ...
          +}
          +
          +
          +

          For more information about how to use iterators, see the +documentation.

          +
          +
          +

          Get role

          +

          This call lists detailed information (id, name, description) for a +specified role.

          +
          $roleId = '123abc';
          +$role = $service->getRole($roleId);
          +
          +
          +
          +
          +

          Add/delete user roles

          +

          To add/remove user roles, you must first instantiate a +user object:

          +
          $roleId = '123abc';
          +
          +// add role to user
          +$user->addRole($roleId);
          +
          +// remove role from user
          +$user->removeRole($roleId);
          +
          +
          +
          +
          +

          List user global roles

          +

          This call returns a list of global roles associated with a user:

          +
          $roles = $user->getRoles();
          +
          +foreach ($roles as $role) {
          +   // ...
          +}
          +
          +
          +

          For more information about how to use iterators, see the +documentation.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/Service.md.html b/doc/_build/html/services/identity/Service.md.html new file mode 100644 index 000000000..81c2d8a3b --- /dev/null +++ b/doc/_build/html/services/identity/Service.md.html @@ -0,0 +1,194 @@ + + + + + + + + + + Identity service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Identity service

          +
          +

          Intro

          +

          The Identity service is regionless, so you do not need to specify a +region when instantiating the service object. Although this was +primarily based on Rackspace’s implementation of Cloud Identity, it +should also work for OpenStack Keystone.

          +
          +
          +

          A note on object creation

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/Tenants.md.html b/doc/_build/html/services/identity/Tenants.md.html new file mode 100644 index 000000000..5bf81f980 --- /dev/null +++ b/doc/_build/html/services/identity/Tenants.md.html @@ -0,0 +1,201 @@ + + + + + + + + + + Tenants — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Tenants

          +
          +

          Intro

          +

          A tenant is a container used to group or isolate resources and/or +identity objects. Depending on the service operator, a tenant may map to +a customer, account, organization, or project.

          +
          +
          +

          Setup

          +

          Tenant objects are instantiated from the Identity service. For more +details, see the Service docs.

          +
          +
          +

          List tenants

          +
          $tenants = $service->getTenants();
          +
          +foreach ($tenants as $tenant) {
          +   // ...
          +}
          +
          +
          +

          For more information about how to use iterators, see the +documentation.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/Tokens.md.html b/doc/_build/html/services/identity/Tokens.md.html new file mode 100644 index 000000000..40d57c972 --- /dev/null +++ b/doc/_build/html/services/identity/Tokens.md.html @@ -0,0 +1,287 @@ + + + + + + + + + + Tokens — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Tokens

          +
          +

          Intro

          +

          A token is an opaque string that represents an authorization to access +cloud resources. Tokens may be revoked at any time and are valid for a +finite duration.

          +
          +
          +

          Setup

          +

          Token objects are instantiated from the Identity service. For more +details, see the Service docs.

          +
          +
          +

          Useful object properties/methods

          + ++++++ + + + + + + + + + + + + + + + + + + + +
          PropertyDescriptionGetterSetter
          idThe unique ID of the tokengetId()setId()
          expiresTimestamp of when the token will expiregetExpires() or hasExpired()setExpires()
          +
          +
          +

          Create token (authenticate)

          +

          In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace’s operation expects a +slightly different entity body than OpenStack Keystone.

          +

          Request body for Rackspace’s generate token operation:

          +
          {
          +    "auth": {
          +        "RAX-KSKEY:apiKeyCredentials": {
          +            "username": "foo",
          +            "apiKey": "aaaaa-bbbbb-ccccc-12345678"
          +        },
          +        "tenantId": "1100111"
          +    }
          +}
          +
          +
          +

          Request body for Keystone’s generate token operation:

          +
          {
          +    "auth": {
          +        "passwordCredentials":{
          +            "username":"demoauthor",
          +            "password":"theUsersPassword"
          +        },
          +        "tenantId": "12345678"
          +    }
          +}
          +
          +
          +

          The only real differences you’ll notice is the name of the object key +(RAX-KSKEY:apiKeyCredentials/passwordCredentials) and the secret +(apiKey/password). The tenantId property in both templates +are optional. You can also add tenantName too.

          +
          use OpenCloud\Common\Http\Message\Formatter;
          +
          +$template = sprintf(
          +   '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}',
          +   'my_username',
          +   'my_api_key'
          +);
          +
          +$response = $service->generateToken($template);
          +
          +$body = Formatter::decode($response);
          +
          +// service catalog
          +$catalog = $body->access->serviceCatalog;
          +
          +// token
          +$token = $body->access->token;
          +
          +// user
          +$user = $body->access->user;
          +
          +
          +

          As you will notice, these variables will be stdClass objects - for fully +fledged functionality, let the client authenticate by itself because it +ends up stocking the necessary models for you.

          +

          To see the response body structure, consult the official +docs.

          +
          +
          +

          Revoke token (destroy session)

          +
          $tokenId = '1234567';
          +$service->revokeToken($tokenId);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/Users.md.html b/doc/_build/html/services/identity/Users.md.html new file mode 100644 index 000000000..e7b735280 --- /dev/null +++ b/doc/_build/html/services/identity/Users.md.html @@ -0,0 +1,358 @@ + + + + + + + + + + Users — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Users

          +
          +

          Intro

          +

          A user is a digital representation of a person, system, or service who +consumes cloud services. Users have credentials and may be assigned +tokens; based on these credentials and tokens, the authentication +service validates that incoming requests are being made by the user who +claims to be making the request, and that the user has the right to +access the requested resources. Users may be directly assigned to a +particular tenant and behave as if they are contained within that +tenant.

          +
          +
          +

          Setup

          +

          User objects are instantiated from the Identity service. For more +details, see the Service docs.

          +
          +
          +

          Useful object properties/methods

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          PropertyDescriptionGetterSetter
          idThe unique ID for this usergetId()setId()
          usernameUsername for this usergetUsername()setUsername()
          emailUser’s email addressgetEmail()setEmail()
          enabledWhether or not this user can consume API functionalitygetEnabled() or isEnabled()setEnabled()
          passwordEither a user-defined string, or an automatically generated one, that provides security when authenticating.getPassword() only valid on creationsetPassword() to set local property only. To set password on API (retention), use updatePassword().
          defaultRegionDefault region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has NOT explicitly specified a region when creating a service object, the user will obtain the service from the default region.getDefaultRegion()setDefaultRegion()
          domainIdDomain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer’s tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account.getDomainId()setDomainId()
          +
          +
          +

          List users

          +
          $users = $service->getUsers();
          +
          +foreach ($users as $user) {
          +   // ...
          +}
          +
          +
          +

          For more information about how to use iterators, see the +documentation.

          +
          +
          +

          Get user

          +

          There are various ways to get a specific user: by name, ID and email +address.

          +
          use OpenCloud\Identity\Constants\User as UserConst;
          +
          +// Get user by name
          +$user1 = $service->getUser('jamie');
          +
          +// Get user by ID
          +$user2 = $service->getUser(123456, UserConst::MODE_ID);
          +
          +// Get user by email
          +$user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL);
          +
          +
          +
          +
          +

          Create user

          +

          There are a few things to remember when creating a user:

          +
            +
          • This operation is available only to users who hold the +identity:user-admin role. This admin can create a user who holds +the identity:default user role.
          • +
          • The created user will have access to APIs but will not have +access to the Cloud Control Panel.
          • +
          • Within an account, a maximum of 100 account users can be added.
          • +
          • If you attempt to add a user who already exists, an HTTP error 409 +results.
          • +
          +

          The username and email properties are required for creating a +user. Providing a password is optional; if omitted, one will be +automatically generated and provided in the response.

          +
          use Guzzle\Http\Exception\ClientErrorResponseException;
          +
          +try {
          +   // execute operation
          +   $user = $service->createUser(array(
          +      'username' => 'newUser',
          +      'email'    => 'foo@bar.com'
          +   ));
          +} catch (ClientErrorResponseException $e) {
          +   // catch 4xx HTTP errors
          +   echo $e->getResponse()->toString();
          +}
          +
          +// show generated password
          +echo $user->getPassword();
          +
          +
          +
          +
          +

          Update user

          +

          When updating a user, specify which attribute/property you want to +update:

          +
          $user->update(array(
          +   'email' => 'new_email@bar.com'
          +));
          +
          +
          +
          +

          Updating a user password

          +

          Updating a user password requires calling a distinct method:

          +
          $user->updatePassword('password123');
          +
          +
          +
          +
          +
          +

          Delete user

          +
          $user->delete();
          +
          +
          +
          +
          +

          List credentials

          +

          This operation allows you to see your non-password credential types for +all authentication methods available.

          +
          $creds = $user->getOtherCredentials();
          +
          +
          +
          +
          +

          Get user API key

          +
          echo $user->getApiKey();
          +
          +
          +
          +
          +

          Reset user API key

          +

          When resetting an API key, a new one will be automatically generated for +you:

          +
          $user->resetApiKey();
          +echo $user->getApiKey();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/index.html b/doc/_build/html/services/identity/index.html new file mode 100644 index 000000000..c5bb6ce92 --- /dev/null +++ b/doc/_build/html/services/identity/index.html @@ -0,0 +1,395 @@ + + + + + + + + + + Identity v2 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Identity v2

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Identity service

          +

          Now to instantiate the Identity service:

          +
          $service = $client->identityService();
          +
          +
          +
          +
          + +
          +

          Glossary

          +
          +
          token
          +
          A token is an opaque string that represents an authorization to access +cloud resources. Tokens may be revoked at any time and are valid for a +finite duration.
          +
          tenant
          +
          A tenant is a container used to group or isolate resources and/or +identity objects. Depending on the service operator, a tenant may map to +a customer, account, organization, or project.
          +
          user
          +
          A user is a digital representation of a person, system, or service who +consumes cloud services. Users have credentials and may be assigned +tokens; based on these credentials and tokens, the authentication +service validates that incoming requests are being made by the user who +claims to be making the request, and that the user has the right to +access the requested resources. Users may be directly assigned to a +particular tenant and behave as if they are contained within that +tenant.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/roles.html b/doc/_build/html/services/identity/roles.html new file mode 100644 index 000000000..69bf9b7ed --- /dev/null +++ b/doc/_build/html/services/identity/roles.html @@ -0,0 +1,364 @@ + + + + + + + + + + Roles — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Roles

          +

          A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user’s roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service.

          +
          +

          Useful object properties/methods

          + +++++ + + + + + + + + + + + + + + + + + + + + +
          PropertyGetterSetter
          idgetId()setId()
          namegetName()setName()
          descriptiongetDescription()setDescription()
          +
          +
          +

          List roles

          +

          This call lists the global roles available within a specified service.

          +
          $roles = $service->getRoles();
          +
          +foreach ($roles as $role) {
          +   // ...
          +}
          +
          +
          +
          +
          +

          Get role

          +

          This call lists detailed information (id, name, description) for a +specified role.

          +
          $role = $service->getRole('{roleId}');
          +
          +
          +
          +
          +

          Add/delete user roles

          +
          $user = $service->getUser('{userId}');
          +
          +$roleId = '{roleId}';
          +
          +// add role to user
          +$user->addRole($roleId);
          +
          +// remove role from user
          +$user->removeRole($roleId);
          +
          +
          +
          +
          +

          List user global roles

          +

          This call returns a list of global roles associated with a user:

          +
          $roles = $user->getRoles();
          +
          +foreach ($roles as $role) {
          +   // ...
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/tenants.html b/doc/_build/html/services/identity/tenants.html new file mode 100644 index 000000000..8277813ca --- /dev/null +++ b/doc/_build/html/services/identity/tenants.html @@ -0,0 +1,317 @@ + + + + + + + + + + Tenants — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Tenants

          +
          +

          List tenants

          +
          $tenants = $service->getTenants();
          +
          +foreach ($tenants as $tenant) {
          +   // ...
          +}
          +
          +
          +
          +
          +

          Tenant object properties and methods

          +

          Once you have a OpenCloud\Identity\Resource\Tenant object, you can retrieve +information like so:

          +
          $tenant->getId();
          +$tenant->getName();
          +$tenant->getDescription();
          +$tenant->isEnabled();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/tokens.html b/doc/_build/html/services/identity/tokens.html new file mode 100644 index 000000000..f6c0a210c --- /dev/null +++ b/doc/_build/html/services/identity/tokens.html @@ -0,0 +1,379 @@ + + + + + + + + + + Tokens — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Tokens

          +
          +

          Create token (authenticate)

          +

          In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace’s operation expects a +slightly different entity body than OpenStack Keystone.

          +

          To do this, and then generate a token:

          +
          $json = $client->getCredentials();
          +
          +/** @var $response Guzzle\Http\Message\Response */
          +$response = $service->generateToken($json);
          +$jsonBody = $response->json();
          +
          +
          +

          When a token is generated by the API, there are a few things returned:

          +
            +
          • a service catalog +outlining all of the services you can interact with, +including their names, service types, and endpoint URLs. Which services +make up your catalog, and how your catalog is structured, will depend on +your service provider.
          • +
          • details about your token, such as its ID, created and expiration date
          • +
          • details about your user account
          • +
          • details about your tenant
          • +
          +
          +

          Interacting with the service catalog

          +

          Once you have the $jsonBody, you can construct a Catalog object for +easier interaction:

          +
          $data = $jsonBody->access->serviceCatalog;
          +$catalog = OpenCloud\Common\Service\Catalog::factory($data);
          +
          +foreach ($catalog->getItems() as $service) {
          +  /** @param $service OpenCloud\Common\Service\CatalogItem */
          +  printf("Catalog item: Name [%s] Type [%s]\n", $service->getName(), $service->getType());
          +
          +  foreach ($service->getEndpoints() as $endpoint) {
          +    printf("  Endpoint provided: Region [%s] PublicURL [%s] PrivateURL [%s]\n",
          +      $endpoint->getRegion(), $endpoint->getPublicUrl(), $endpoint->getPrivateUrl());
          +  }
          +}
          +
          +
          +
          +
          +

          Interacting with tokens

          +
          $data = $jsonBody->access->token;
          +$token = $service->resource('Token', $data);
          +
          +printf("Token ID: %s - Token expiry %s", $token->getId(), $token->getExpires());
          +
          +if ($token->hasExpired()) {
          +  // ...
          +}
          +
          +
          +
          +
          +

          Interacting with users

          +
          $data = $jsonBody->access->user;
          +$user = $service->resource('User', $data);
          +
          +
          +

          To see which methods you can call on $user (which implements +OpenCloud\Identity\Resource\User), see our user documentation +which accompanies this guide.

          +
          +
          +

          Interacting with tenants

          +
          $data = $jsonBody->access->tenant;
          +$tenant = $service->resource('Tenant', $data);
          +
          +
          +

          To see which methods you can call on $tenant (which implements +OpenCloud\Identity\Resource\Tenant), see our user documentation +which accompanies this guide.

          +
          +
          +
          +

          Revoke token (destroy session)

          +
          $service->revokeToken('{tokenId}');
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/identity/users.html b/doc/_build/html/services/identity/users.html new file mode 100644 index 000000000..d46d9b80d --- /dev/null +++ b/doc/_build/html/services/identity/users.html @@ -0,0 +1,464 @@ + + + + + + + + + + Users — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Users

          +
          +

          Object properties/methods

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          PropertyDescriptionGetterSetter
          idThe unique ID for this usergetId()setId()
          usernameUsername for this usergetUsername()setUsername()
          emailUser’s email addressgetEmail()setEmail()
          enabledWhether or not this user can consume API functionalitygetEnabled() or isEnabled()setEnabled()
          passwordEither a user-defined string, or an automatically generated one, that provides security when authenticating.getPassword() only valid on creationsetPassword() to set local property only. To set password on API (retention), use updatePassword().
          defaultRegionDefault region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has NOT explicitly specified a region when creating a service object, the user will obtain the service from the default region.getDefaultRegion()setDefaultRegion()
          domainIdDomain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer’s tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account.getDomainId()setDomainId()
          +
          +
          +

          List users

          +
          $users = $service->getUsers();
          +
          +foreach ($users as $user) {
          +   // ...
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieve a user by username

          +
          $user = $service->getUser('jamie');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieve a user by user ID

          +
          use OpenCloud\Identity\Constants\User as UserConst;
          +
          +$user = $service->getUser('{userId}', UserConst::MODE_ID);
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieve a user by email address

          +
          use OpenCloud\Identity\Constants\User as UserConst;
          +
          +$user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL);
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Create user

          +

          There are a few things to bear in mind when creating a user:

          +
            +
          • This operation is available only to users who hold the +identity:user-admin role. This admin can create a user who holds +the identity:default user role.
          • +
          • The created user will have access to APIs but will not have +access to the Cloud Control Panel.
          • +
          • A maximum of 100 account users can be added per account.
          • +
          • If you attempt to add a user who already exists, an HTTP error 409 +results.
          • +
          +

          The username and email properties are required for creating a +user. Providing a password is optional; if omitted, one will be +automatically generated and provided in the response.

          +
          use Guzzle\Http\Exception\ClientErrorResponseException;
          +
          + $user = $service->createUser(array(
          +    'username' => 'newUser',
          +    'email'    => 'foo@bar.com'
          + ));
          +
          +// show generated password
          +echo $user->getPassword();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update user

          +

          When updating a user, specify which attribute/property you want to +update:

          +
          $user->update(array(
          +   'email' => 'new_email@bar.com'
          +));
          +
          +
          +
          +
          +

          Updating a user password

          +

          Updating a user password requires calling a distinct method:

          +
          $user->updatePassword('password123');
          +
          +
          +
          +
          +

          Delete user

          +
          $user->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List credentials

          +

          This operation allows you to see your non-password credential types for +all authentication methods available.

          +
          $creds = $user->getOtherCredentials();
          +
          +
          +
          +
          +

          Get user API key

          +
          echo $user->getApiKey();
          +
          +
          +
          +
          +

          Reset user API key

          +

          When resetting an API key, a new one will be automatically generated for +you:

          +
          $user->resetApiKey();
          +echo $user->getApiKey();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/Images.md.html b/doc/_build/html/services/image/Images.md.html new file mode 100644 index 000000000..b9de74c58 --- /dev/null +++ b/doc/_build/html/services/image/Images.md.html @@ -0,0 +1,273 @@ + + + + + + + + + + Images — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Images

          +

          A virtual machine image is a single file which contains a virtual disk +that has an installed bootable operating system. In the Cloud Images +API, an image is represented by a JSON-encoded data structure (the image +schema) and its raw binary data (the image file).

          +

          An Image is represented by the OpenCloud\Image\Resource\Image class.

          +
          +

          Setup

          +

          You instantiate an Image object from its OpenCloud\Image\Service +class, which is available from the OpenStack/Rackspace client:

          +
          $service = $client->imageService('cloudImages', 'IAD');
          +
          +
          +

          View the guides for more information about clients +or services.

          +
          +
          +

          List images

          +
          $images = $service->listImages();
          +
          +foreach ($images as $image) {
          +   /** @param $image OpenCloud\Image\Resource\Image */
          +}
          +
          +
          +

          For more information about working with iterators, please see the +iterators documentation.

          +
          +
          +

          Get image details

          +
          /** @param $image OpenCloud\Image\Resource\Image */
          +$image = $service->getImage('<image_id>');
          +
          +
          +
          +

          A note on schema classes

          +

          Both OpenCloud\Image\Resource\Image and +OpenCloud\Image\Resource\Member extend the +AbstractSchemaResource abstract class, which offers some unique +functionality.

          +

          Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a way +different than conventional getter/setter methods, and even class +properties. For this reason, they implement SPL’s native +`ArrayAccess <http://www.php.net/manual/en/class.arrayaccess.php>`__ +interface which allows you to access their state as a conventional +array:

          +
          $image = $service->getImage('<image_id>');
          +
          +$id = $image['id'];
          +$tags = $image['tags'];
          +
          +
          +
          +
          +
          +

          Update image

          +

          You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its schema.

          +

          Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden:

          +
          use OpenCloud\Common\Exceptions\ForbiddenOperationException;
          +
          +try {
          +    $image->update(array(
          +        'name'        => 'foo',
          +        'newProperty' => 'bar'
          +    ));
          +} catch (ForbiddenOperationException $e) {
          +    // A 403 Forbidden was returned
          +}
          +
          +
          +

          There are three operations that can take place for each Image property:

          +
            +
          • If a false or null value is provided, a REMOVE operation +will occur, removing the property from the JSON document
          • +
          • If a non-false value is provided and the property does not exist, an +ADD operation will add it to the document
          • +
          • If a non-false value is provided and the property does exist, a +REPLACE operation will modify the property in the document
          • +
          +
          +
          +

          Delete image

          +
          $image->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/Schemas.md.html b/doc/_build/html/services/image/Schemas.md.html new file mode 100644 index 000000000..e3e7d1427 --- /dev/null +++ b/doc/_build/html/services/image/Schemas.md.html @@ -0,0 +1,331 @@ + + + + + + + + + + JSON schemas — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          JSON schemas

          +

          The Cloud Images API supplies json documents describing the JSON-encoded +data structures that represent domain objects, so that a client knows +exactly what to expect in an API response.

          +

          A JSON Schema is represented by the +OpenCloud\Image\Resource\Schema\Schema class.

          +
          +

          Schema types

          +

          There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema.

          +
          +
          +

          Example response from the API

          +

          A sample response from the API, for an Images schema might be:

          +
          {
          +    "name": "images",
          +    "properties": {
          +        "images": {
          +            "items": {
          +                "type": "array",
          +                "name": "image",
          +                "properties": {
          +                    "id": {"type": "string"},
          +                    "name": {"type": "string"},
          +                    "visibility": {"enum": ["public", "private"]},
          +                    "status": {"type": "string"},
          +                    "protected": {"type": "boolean"},
          +                    "tags": {
          +                        "type": "array",
          +                        "items": {"type": "string"}
          +                    },
          +                    "checksum": {"type": "string"},
          +                    "size": {"type": "integer"},
          +                    "created_at": {"type": "string"},
          +                    "updated_at": {"type": "string"},
          +                    "file": {"type": "string"},
          +                    "self": {"type": "string"},
          +                    "schema": {"type": "string"}
          +                },
          +                "additionalProperties": {"type": "string"},
          +                "links": [
          +                    {"href": "{self}", "rel": "self"},
          +                    {"href": "{file}", "rel": "enclosure"},
          +                    {"href": "{schema}", "rel": "describedby"}
          +                ]
          +            }
          +        },
          +        "schema": {"type": "string"},
          +        "next": {"type": "string"},
          +        "first": {"type": "string"}
          +    },
          +    "links": [
          +        {"href": "{first}", "rel": "first"},
          +        {"href": "{next}", "rel": "next"},
          +        {"href": "{schema}", "rel": "describedby"}
          +    ]
          +}
          +
          +
          +

          The top-level schema is called images, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level images object. So we know that it will +take this form:

          +
          {
          +   "images": [something...]
          +}
          +
          +
          +

          Within this object, we can see that it contains an array of anonymous +objects, each of which is called image and has its own set of nested +properties:

          +
          {
          +    "images": [
          +        {
          +            [object 1...]
          +        },
          +        {
          +            [object 2...]
          +        },
          +        {
          +            [object 3...]
          +        }
          +    ]
          +}
          +
          +
          +

          The structure of these nested objects are defined as another schema - +i.e. a subschema. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +private or public), etc.

          +
          {
          +    "images": [
          +        {
          +            "id": "foo",
          +            "name": "bar",
          +            "visibility": "private",
          +            // etc.
          +        },
          +        {
          +            "id": "foo",
          +            "name": "bar",
          +            "visibility": "private",
          +            // etc.
          +        },
          +        {
          +            "id": "foo",
          +            "name": "bar",
          +            "visibility": "private",
          +            // etc.
          +        }
          +    ]
          +}
          +
          +
          +

          Each nested property of a schema is represented by the +OpenCloud\Image\Resource\Schema\Property class.

          +

          If you would like to find out more about schemas, Guzzle has good +documentation about service +descriptions, +which is fairly analogous.

          +
          +
          +

          JSON Patch

          +

          The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in RFC +6902.

          +

          Requests need to use the +application/openstack-images-v2.1-json-patch content-type.

          +

          In order for the operation to occur, the request entity body needs to +contain a very particular structure:

          +
          [
          +    {"op": "replace", "path": "/name", "value": "Fedora 17"},
          +    {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]}
          +]
          +
          +
          +

          The op key refers to the type of Operation (see +OpenCloud\Image\Enum\OperationType for a full list).

          +

          The path key is a JSON pointer to the document property you want to +modify or insert. JSON pointers are defined in RFC +6901.

          +

          The value key is the value.

          +

          Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the official +documentation +for additional information.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/Sharing.md.html b/doc/_build/html/services/image/Sharing.md.html new file mode 100644 index 000000000..743793072 --- /dev/null +++ b/doc/_build/html/services/image/Sharing.md.html @@ -0,0 +1,311 @@ + + + + + + + + + + Sharing images — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Sharing images

          +

          Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers:

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          OperationProducer can?Consumer can?
          CreatedYesNo
          DeletedYesNo
          UpdatedNoYes
          ListedYesYes
          +

          The producer shares an image with the consumer by making the consumer a +member of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer’s image list.

          +
          +

          Typical workflow

          +
            +
          1. The producer posts the availability of specific images on a public +website.
          2. +
          3. A potential consumer provides the producer with his/her tenant ID and +email address.
          4. +
          5. The producer `creates a new Image Member <>`__ with the consumer’s +details
          6. +
          7. The producer notifies the consumer via email that the image has been +shared and provides the image’s ID.
          8. +
          9. If the consumer wishes the image to appear in his/her image list, the +consumer `updates their own Member status <>`__ to ACCEPTED.
          10. +
          +
          +

          Additional notes

          +
            +
          • If the consumer subsequently wishes to hide the image, the consumer +can change their Member status to REJECTED.
          • +
          • If the consumer wishes to hide the image, but is open to the +possibility of being reminded by the producer that the image is +available, the consumer can change their Member status to +PENDING.
          • +
          • Image producers add or remove image members, but may not modify the +member status of an image member.
          • +
          • Image consumers change their own member status, but may not add or +remove themselves as an image member.
          • +
          • Image consumers can boot from any image shared by the image producer, +regardless of the member status, as long as the image consumer knows +the image ID.
          • +
          +
          +
          +
          +

          Setup

          +

          All member operations are executed against an Image, so +you will need to set this up first.

          +
          +
          +

          List image members

          +

          This operation is available for both producers and consumers.

          +
          $members = $image->listMembers();
          +
          +foreach ($members as $member) {
          +    /** @param $member OpenCloud\Image\Resource\Member */
          +}
          +
          +
          +

          For more information about working with iterators, please see the +iterators documentation.

          +
          +
          +

          Create image member

          +

          This operation is only available for producers.

          +
          $tenantId = 12345;
          +
          +/** @param $response Guzzle\Http\Message\Response */
          +$response = $image->createMember($tenantId);
          +
          +
          +
          +
          +

          Delete image member

          +

          This operation is only available for producers.

          +
          $tenantId = 12345;
          +
          +/** @param $member OpenCloud\Image\Resource\Member */
          +$member = $image->getMember($tenantId);
          +
          +$member->delete();
          +
          +
          +
          +
          +

          Update image member status

          +

          This operation is only available for consumers.

          +
          use OpenCloud\Images\Enum\MemberStatus;
          +
          +$tenantId = 12345;
          +
          +/** @param $member OpenCloud\Image\Resource\Member */
          +$member = $image->getMember($tenantId);
          +
          +$member->updateStatus(MemberStatus::ACCEPTED);
          +
          +
          +

          The acceptable states you may pass in are made available to you through +the constants defined in the OpenCloud\Images\Enum\MemberStatus +class.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/Tags.md.html b/doc/_build/html/services/image/Tags.md.html new file mode 100644 index 000000000..85836ec8e --- /dev/null +++ b/doc/_build/html/services/image/Tags.md.html @@ -0,0 +1,205 @@ + + + + + + + + + + Image tags — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Image tags

          +

          An image tag is a string of characters used to identify a specific image +or images.

          +
          +

          Setup

          +

          All member operations are executed against an Image, so +you will need to set this up first.

          +
          +
          +

          Add image tag

          +
          /** @param $response Guzzle\Http\Message\Response */
          +$response = $image->addTag('jamie_dev');
          +
          +
          +
          +
          +

          Delete image tag

          +
          /** @param $response Guzzle\Http\Message\Response */
          +$response = $image->deleteTag('jamie_dev');
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/images.html b/doc/_build/html/services/image/images.html new file mode 100644 index 000000000..9fe53b19c --- /dev/null +++ b/doc/_build/html/services/image/images.html @@ -0,0 +1,366 @@ + + + + + + + + + + Images — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Images

          +
          +

          List images

          +
          $images = $service->listImages();
          +
          +foreach ($images as $image) {
          +   /** @param $image OpenCloud\Image\Resource\Image */
          +}
          +
          +
          +
          +
          +

          Get image details

          +
          /** @param $image OpenCloud\Image\Resource\Image */
          +$image = $service->getImage('{imageId}');
          +
          +
          +
          +

          A note on schema classes

          +

          Both OpenCloud\Image\Resource\Image and OpenCloud\Image\Resource\Member +extend the AbstractSchemaResource class, which offers some unique functionality.

          +

          Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a different way +than conventional getter/setter methods, and even class properties. For this +reason, they implement SPL’s native +ArrayAccess +interface which allows you to access their state as a conventional +array:

          +
          $image = $service->getImage('{imageId}');
          +
          +$id = $image['id'];
          +$tags = $image['tags'];
          +
          +
          +
          +
          +
          +

          Update image

          +

          You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its schema.

          +

          Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden:

          +
          use OpenCloud\Common\Exceptions\ForbiddenOperationException;
          +
          +try {
          +    $image->update(array(
          +        'name'        => 'foo',
          +        'newProperty' => 'bar'
          +    ));
          +} catch (ForbiddenOperationException $e) {
          +    // A 403 Forbidden was returned
          +}
          +
          +
          +

          There are three operations that can take place for each Image property:

          +
            +
          • If a false or null value is provided, a REMOVE operation +will occur, removing the property from the JSON document
          • +
          • If a non-false value is provided and the property does not exist, an +ADD operation will add it to the document
          • +
          • If a non-false value is provided and the property does exist, a +REPLACE operation will modify the property in the document
          • +
          +
          +
          +

          Delete image

          +
          $image->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/index.html b/doc/_build/html/services/image/index.html new file mode 100644 index 000000000..1ac5b3af7 --- /dev/null +++ b/doc/_build/html/services/image/index.html @@ -0,0 +1,397 @@ + + + + + + + + + + Images v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Images v1

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Images service

          +

          Now to instantiate the Images service:

          +
          $service = $client->imageService(null, '{region}');
          +
          +
          +
          +
          + +
          +

          Glossary

          +
          +
          +
          image
          +
          A virtual machine image is a single file which contains a virtual disk +that has an installed bootable operating system. In the Cloud Images +API, an image is represented by a JSON-encoded data structure (the image +schema) and its raw binary data (the image file).
          +
          schema
          +
          The Cloud Images API supplies JSON documents describing the JSON-encoded +data structures that represent domain objects, so that a client knows +exactly what to expect in an API response.
          +
          tag
          +
          An image tag is a string of characters used to identify a specific image +or images.
          +
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/schemas.html b/doc/_build/html/services/image/schemas.html new file mode 100644 index 000000000..b73adabc1 --- /dev/null +++ b/doc/_build/html/services/image/schemas.html @@ -0,0 +1,440 @@ + + + + + + + + + + JSON schemas — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          JSON schemas

          +
          +

          Schema types

          +

          There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema.

          +
          +
          +

          Example response from the API

          +

          A sample response from the API, for an Images schema might be:

          +
          {
          +    "name": "images",
          +    "properties": {
          +        "images": {
          +            "items": {
          +                "type": "array",
          +                "name": "image",
          +                "properties": {
          +                    "id": {"type": "string"},
          +                    "name": {"type": "string"},
          +                    "visibility": {"enum": ["public", "private"]},
          +                    "status": {"type": "string"},
          +                    "protected": {"type": "boolean"},
          +                    "tags": {
          +                        "type": "array",
          +                        "items": {"type": "string"}
          +                    },
          +                    "checksum": {"type": "string"},
          +                    "size": {"type": "integer"},
          +                    "created_at": {"type": "string"},
          +                    "updated_at": {"type": "string"},
          +                    "file": {"type": "string"},
          +                    "self": {"type": "string"},
          +                    "schema": {"type": "string"}
          +                },
          +                "additionalProperties": {"type": "string"},
          +                "links": [
          +                    {"href": "{self}", "rel": "self"},
          +                    {"href": "{file}", "rel": "enclosure"},
          +                    {"href": "{schema}", "rel": "describedby"}
          +                ]
          +            }
          +        },
          +        "schema": {"type": "string"},
          +        "next": {"type": "string"},
          +        "first": {"type": "string"}
          +    },
          +    "links": [
          +        {"href": "{first}", "rel": "first"},
          +        {"href": "{next}", "rel": "next"},
          +        {"href": "{schema}", "rel": "describedby"}
          +    ]
          +}
          +
          +
          +

          The top-level schema is called images, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level images object. So we know that it will +take this form:

          +
          {
          +   "images": [something...]
          +}
          +
          +
          +

          Within this object, we can see that it contains an array of anonymous +objects, each of which is called image and has its own set of nested +properties:

          +
          {
          +    "images": [
          +        {
          +            [object 1...]
          +        },
          +        {
          +            [object 2...]
          +        },
          +        {
          +            [object 3...]
          +        }
          +    ]
          +}
          +
          +
          +

          The structure of these nested objects are defined as another schema - +i.e. a subschema. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +private or public), etc.

          +
          {
          +    "images": [
          +        {
          +            "id": "foo",
          +            "name": "bar",
          +            "visibility": "private",
          +            // etc.
          +        },
          +        {
          +            "id": "foo",
          +            "name": "bar",
          +            "visibility": "private",
          +            // etc.
          +        },
          +        {
          +            "id": "foo",
          +            "name": "bar",
          +            "visibility": "private",
          +            // etc.
          +        }
          +    ]
          +}
          +
          +
          +

          Each nested property of a schema is represented by the +OpenCloud\Image\Resource\Schema\Property class.

          +

          If you would like to find out more about schemas, Guzzle has good +documentation about service +descriptions, +which is fairly analogous.

          +
          +
          +

          JSON Patch

          +

          The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in RFC +6902.

          +

          Requests need to use the +application/openstack-images-v2.1-json-patch content-type.

          +

          In order for the operation to occur, the request entity body needs to +contain a very particular structure:

          +
          [
          +    {"op": "replace", "path": "/name", "value": "Fedora 17"},
          +    {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]}
          +]
          +
          +
          +
            +
          • The op key refers to the type of Operation (see +OpenCloud\Image\Enum\OperationType for a full list).
          • +
          • The path key is a JSON pointer to the document property you want to +modify or insert. JSON pointers are defined in RFC +6901.
          • +
          • The value key is the value.
          • +
          +

          Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the official +documentation +for additional information.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/sharing.html b/doc/_build/html/services/image/sharing.html new file mode 100644 index 000000000..01e693f02 --- /dev/null +++ b/doc/_build/html/services/image/sharing.html @@ -0,0 +1,417 @@ + + + + + + + + + + Sharing images — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Sharing images

          +

          Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers:

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          OperationProducer can?Consumer can?
          CreatedYesNo
          DeletedYesNo
          UpdatedNoYes
          ListedYesYes
          +

          The producer shares an image with the consumer by making the consumer a +member of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer’s image list.

          +
          +

          Typical workflow

          +
            +
          1. The producer posts the availability of specific images on a public +website.
          2. +
          3. A potential consumer provides the producer with his/her tenant ID and +email address.
          4. +
          5. The producer `creates a new Image Member <>`__ with the consumer’s +details
          6. +
          7. The producer notifies the consumer via email that the image has been +shared and provides the image’s ID.
          8. +
          9. If the consumer wishes the image to appear in his/her image list, the +consumer `updates their own Member status <>`__ to ACCEPTED.
          10. +
          +
          +

          Additional notes

          +
            +
          • If the consumer subsequently wishes to hide the image, the consumer +can change their Member status to REJECTED.
          • +
          • If the consumer wishes to hide the image, but is open to the +possibility of being reminded by the producer that the image is +available, the consumer can change their Member status to +PENDING.
          • +
          • Image producers add or remove image members, but may not modify the +member status of an image member.
          • +
          • Image consumers change their own member status, but may not add or +remove themselves as an image member.
          • +
          • Image consumers can boot from any image shared by the image producer, +regardless of the member status, as long as the image consumer knows +the image ID.
          • +
          +
          +
          +
          +

          Setup

          +

          All member operations are executed against an Image, so you will +need to set one up first:

          +
          $image = $service->getImage('{imageId}');
          +
          +
          +
          +
          +

          List image members

          +

          This operation is available for both producers and consumers.

          +
          $members = $image->listMembers();
          +
          +foreach ($members as $member) {
          +    /** @param $member OpenCloud\Image\Resource\Member */
          +}
          +
          +
          +
          +
          +

          Create image member

          +

          This operation is only available for producers.

          +
          /** @param $response Guzzle\Http\Message\Response */
          +$response = $image->createMember('{tenantId}');
          +
          +
          +
          +
          +

          Delete image member

          +

          This operation is only available for producers.

          +
          /** @param $member OpenCloud\Image\Resource\Member */
          +$member = $image->getMember('{tenantId}');
          +$member->delete();
          +
          +
          +
          +
          +

          Update image member status

          +

          This operation is only available for consumers.

          +
          use OpenCloud\Images\Enum\MemberStatus;
          +
          +/** @param $member OpenCloud\Image\Resource\Member */
          +$member = $image->getMember('{tenantId}');
          +
          +$member->updateStatus(MemberStatus::ACCEPTED);
          +
          +
          +

          The acceptable states you may pass in are made available to you through +the constants defined in the OpenCloud\Images\Enum\MemberStatus +class.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/image/tags.html b/doc/_build/html/services/image/tags.html new file mode 100644 index 000000000..a4c59e644 --- /dev/null +++ b/doc/_build/html/services/image/tags.html @@ -0,0 +1,318 @@ + + + + + + + + + + Image tags — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Image tags

          +
          +

          Setup

          +

          All member operations are executed against an Image, so you will +need to set one up first:

          +
          $image = $service->getImage('{imageId}');
          +
          +
          +
          +
          +

          Add image tag

          +
          /** @param $response Guzzle\Http\Message\Response */
          +$response = $image->addTag('jamie_dev');
          +
          +
          +
          +
          +

          Delete image tag

          +
          /** @param $response Guzzle\Http\Message\Response */
          +$response = $image->deleteTag('jamie_dev');
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/index.html b/doc/_build/html/services/index.html new file mode 100644 index 000000000..e56bd9a40 --- /dev/null +++ b/doc/_build/html/services/index.html @@ -0,0 +1,177 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          + + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/README.md.html b/doc/_build/html/services/load-balancer/README.md.html new file mode 100644 index 000000000..cba6ca120 --- /dev/null +++ b/doc/_build/html/services/load-balancer/README.md.html @@ -0,0 +1,262 @@ + + + + + + + + + + Load Balancers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Load Balancers

          +

          A load balancer is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the nodes of the load balancer.

          +
          +

          Getting started

          +
          +

          1. Instantiate a Rackspace client.

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
          +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
          +));
          +
          +
          +
          +
          +

          2. Retrieve the server instances you want to add as nodes of the load balancer.

          +
          $computeService = $client->computeService('cloudServersOpenStack', 'DFW');
          +
          +$serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216');
          +$serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74');
          +
          +
          +

          The example above uses two server instances that have already been +created. It retrieves the server instances using their IDs. See also: +`creating server instances <>`__.

          +
          +
          +

          3. Obtain a Load Balancer service object from the client.

          +

          This object will be used to first define the load balancer nodes and +later create the load balancer itself.

          +
          $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW');
          +
          +
          +
          +
          +

          4. Define a load balancer node for each server.

          +
          $loadBalancer = $loadBalancerService->loadBalancer();
          +
          +$serverOneNode = $loadBalancer->node();
          +$serverOneNode->address = $serverOne->addresses->private[0]->addr;
          +$serverOneNode->port = 8080;
          +$serverOneNode->condition = 'ENABLED';
          +
          +$serverTwoNode = $loadBalancer->node();
          +$serverTwoNode->address = $serverTwo->addresses->private[0]->addr;
          +$serverTwoNode->port = 8080;
          +$serverTwoNode->condition = 'ENABLED';
          +
          +
          +

          In the example above, each node runs a service that listens on port +8080. Further, each node will start out as ENABLED, which means it +will be ready to receive network traffic from the load balancer as soon +as it is created.

          +
          +
          +

          5. Create the load balancer with the two nodes.

          +
          $loadBalancer->addVirtualIp('PUBLIC');
          +$loadBalancer->create(array(
          +    'name' => 'My smart load balancer',
          +    'port' => 80,
          +    'protocol' => 'HTTP',
          +    'nodes' => array($serverOneNode, $serverTwoNode)
          +));
          +
          +
          +

          In the example above, the load balancer will have a virtual IP address +accessible from the public Internet. Also notice that the port the load +balancer listens on (80) does not need to match the ports of its nodes +(8080).

          +
          +
          +
          +

          Next steps

          +

          Once you have created a load balancer, there is a lot you can do with +it. See the complete user guide for load balancers.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/USERGUIDE.md.html b/doc/_build/html/services/load-balancer/USERGUIDE.md.html new file mode 100644 index 000000000..99c1c5ed7 --- /dev/null +++ b/doc/_build/html/services/load-balancer/USERGUIDE.md.html @@ -0,0 +1,959 @@ + + + + + + + + + + The Complete User Guide to Load Balancers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          The Complete User Guide to Load Balancers

          +
          +

          Prerequisites

          +
          +

          Client

          +

          To use the load balancers service, you must first instantiate a +Rackspace client object.

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
          +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
          +));
          +
          +
          +
          +
          +

          Load Balancer Service

          +

          All operations on load balancers are done via a load balancer service +object.

          +
          $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW');
          +
          +
          +
          +
          +

          Cloud Servers

          +

          Many of the examples in this document use two cloud servers as nodes for +the load balancer. The variables $serverOne and $serverTwo refer +to these two cloud servers.

          +
          +
          +
          +

          Load Balancers

          +

          A load balancer is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the nodes of the load balancer.

          +
          +

          Create Load Balancer

          +
          $loadBalancer = $loadBalancerService->loadBalancer();
          +
          +$serverOneNode = $loadBalancer->node();
          +$serverOneNode->address = $serverOne->addresses->private[0]->addr;
          +$serverOneNode->port = 8080;
          +$serverOneNode->condition = 'ENABLED';
          +
          +$serverTwoNode = $loadBalancer->node();
          +$serverTwoNode->address = $serverTwo->addresses->private[0]->addr;
          +$serverTwoNode->port = 8080;
          +$serverTwoNode->condition = 'ENABLED';
          +
          +$loadBalancer->addVirtualIp('PUBLIC');
          +$loadBalancer->create(array(
          +    'name'     => 'My smart load balancer',
          +    'port'     => 80,
          +    'protocol' => 'HTTP',
          +    'nodes'    => array($serverOneNode, $serverTwoNode)
          +));
          +
          +
          +
          +
          +

          List Load Balancer Details

          +

          You can retrieve a single load balancer’s details by using its ID.

          +
          $loadBalancer = $loadBalancerService->loadBalancer('254889');
          +
          +/** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/
          +
          +
          +
          +
          +

          List Load Balancers

          +

          You can retrieve a list of all your load balancers. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $loadBalancers = $loadBalancerService->loadBalancerList();
          +foreach ($loadBalancers as $loadBalancer) {
          +    /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/
          +}
          +
          +
          +
          +
          +

          Update Load Balancer Attributes

          +

          You can update one or more of the following load balancer attributes:

          +
            +
          • name: The name of the load balancer
          • +
          • algorithm: The algorithm used by the load balancer to distribute +traffic amongst its nodes. See also: Load balancing +algorithms.
          • +
          • protocol: The network protocol used by traffic coming in to the +load balancer. See also: Protocols.
          • +
          • port: The network port on which the load balancer listens for +incoming traffic.
          • +
          • halfClosed: Enable or Disable Half-Closed support for the load +balancer.
          • +
          • timeout: The timeout value for the load balancer to communicate +with its nodes.
          • +
          • httpsRedirect: Enable or disable HTTP to HTTPS redirection for +the load balancer. When enabled, any HTTP request will return status +code 301 (Moved Permanently), and the requestor will be redirected to +the requested URL via the HTTPS protocol on port 443. For example, +http://example.com/page.html would be redirected to https:// +example.com/page.html. Only available for HTTPS protocol (port = +443), or HTTP Protocol with a properly configured SSL Termination +(`secureTrafficOnly=true, securePort=443). See also: SSL +Termination.
          • +
          +
          +

          Updating a single attribute of a load balancer

          +
          $loadBalancer->update(array(
          +    'name' => 'New name'
          +));
          +
          +
          +
          +
          +

          Updating multiple attributes of a load balancer

          +
          $loadBalancer->update(array(
          +    'name'      => 'New name',
          +    'algorithm' => 'ROUND_ROBIN'
          +));
          +
          +
          +
          +
          +
          +

          Remove Load Balancer

          +

          When you no longer have a need for the load balancer, you can remove it.

          +
          $loadBalancer->delete();
          +
          +
          +
          +
          +
          +

          Nodes

          +

          A node is a backend device that provides a service on specified IP +and port. An example of a load balancer node might be a web server +serving HTTP traffic on port 8080.

          +

          A load balancer typically has multiple nodes attached to it so it can +distribute incoming network traffic amongst them.

          +
          +

          List Nodes

          +

          You can list the nodes attached to a load balancer. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $nodes = $loadBalancer->nodeList();
          +foreach ($nodes as $node) {
          +    /** @var $node OpenCloud\LoadBalancer\Resource\Node **/
          +}
          +
          +
          +
          +
          +

          Add Nodes

          +

          You can attach additional nodes to a load balancer. Assume +$loadBalancer already has two nodes attached to it - $serverOne +and $serverTwo - and you want to attach a third node to it, say +$serverThree, which provides a service on port 8080.

          +

          Important: Remember to call $loadBalancer->addNodes() after all +the calls to $loadBalancer->addNode() as shown below.

          +
          $address = $serverThree->addresses->private[0]->addr;
          +$loadBalancer->addNode($address, 8080);
          +$loadBalancer->addNodes();
          +
          +
          +

          The addNode method accepts three more optional parameters, in +addition to the two shown above:

          +
          +
          Position | Description | Data type | Required? | Default value |
          +
          ———– | ————— | ————–| ————– |
          +
          +

          —————– | +|  1 | IP address of node | String | Yes | - | +|  2 | Port used by node’s service | Integer | Yes | - | +|  3 | Starting condition of node: +|  4 | Type of node to add: +|  5 | Weight, between 1 and 100, given to node when distributing +traffic using either the WEIGHTED_ROUND_ROBIN or the +WEIGHTED_LEAST_CONNECTIONS load balancing algorithm. | Integer | +No | 1 |

          +
          +
          +

          Modify Nodes

          +

          You can modify one or more of the following node attributes:

          +
            +
          • condition: The condition of the load balancer:
              +
            • ENABLED – Node is ready to receive traffic from the load +balancer.
            • +
            • DISABLED – Node should not receive traffic from the load +balancer.
            • +
            • DRAINING – Node should process any traffic it is already +receiving but should not receive any further traffic from the load +balancer.
            • +
            +
          • +
          • type: The type of the node:
              +
            • PRIMARY – Nodes defined as PRIMARY are in the normal rotation +to receive traffic from the load balancer.
            • +
            • SECONDARY – Nodes defined as SECONDARY are only in the +rotation to receive traffic from the load balancer when all the +primary nodes fail.
            • +
            +
          • +
          • weight: The weight, between 1 and 100, given to node when +distributing traffic using either the WEIGHTED_ROUND_ROBIN or the +WEIGHTED_LEAST_CONNECTIONS load balancing algorithm.
          • +
          +
          +

          Modifying a single attribute of a node

          +
          use OpenCloud\LoadBalancer\Enum\NodeCondition;
          +
          +$node->update(array(
          +    'condition' => NodeCondition::DISABLED
          +));
          +
          +
          +
          +
          +

          Modifying multiple attributes of a node

          +
          use OpenCloud\LoadBalancer\Enum\NodeCondition;
          +use OpenCloud\LoadBalancer\Enum\NodeType;
          +
          +$node->update(array(
          +    'condition' => NodeCondition::DISABLED,
          +    'type'      => NodeType::SECONDARY
          +));
          +
          +
          +
          +
          +
          +

          Remove Nodes

          +

          There are two ways to remove a node.

          +
          +

          Given an OpenCloud\LoadBalancer\Resource\Node instance

          +
          $node->delete();
          +
          +
          +
          +
          +

          Given an OpenCloud\LoadBalancer\Resource\LoadBalancer instance and a node ID

          +
          $loadBalancer->removeNode(490639);
          +
          +
          +

          The removeNode method, as shown above, accepts the following +arguments:

          + +++++++ + + + + + + + + + + + + + + + + +
          PositionDescriptionData typeRequired?Default value
          1ID of nodeIntegerYes
            +
          • +
          +
          +
          +
          +
          +

          View Node Service Events

          +

          You can view events associated with the activity between a node and a +load balancer. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $nodeEvents = $loadBalancer->nodeEventList();
          +foreach ($nodeEvents as $nodeEvent) {
          +    /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/
          +}
          +
          +
          +
          +
          +
          +

          Virtual IPs

          +

          A virtual IP (VIP) makes a load balancer accessible by clients. The +load balancing service supports either a public VIP address +(PUBLIC), routable on the public Internet, or a ServiceNet VIP +address (SERVICENET), routable only within the region in which the +load balancer resides.

          +
          +

          List Virtual IPs

          +

          You can list the VIPs associated with a load balancer. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $vips = $loadBalancer->virtualIpList();
          +foreach ($vips as $vip) {
          +    /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/
          +}
          +
          +
          +
          +
          +

          Add Virtual IPv6

          +

          You can add additional IPv6 VIPs to a load balancer.

          +
          use OpenCloud\LoadBalancer\Enum\IpType;
          +
          +$loadBalancer->addVirtualIp(IpType::PUBLIC, 6);
          +
          +
          +

          The addVirtualIp method, as shown above, accepts the following +arguments:

          +
          +
          Position | Description | Data type | Required? | Default value |
          +
          ———– | ————— | ————– |————– |
          +
          +

          —————– | +|  1 | Type of VIP: +|  2 | IP version: Must be 6 | Integer | Yes | - |

          +
          +
          +

          Remove Virtual IPs

          +

          You can remove a VIP from a load balancer.

          +
          $vip->remove();
          +
          +
          +

          Please note that a load balancer must have at least one VIP associated +with it. If you try to remove a load balancer’s last VIP, a +ClientErrorResponseException will be thrown.

          +
          +
          +
          +

          Algorithms

          +

          Load balancers use an algorithm to determine how incoming traffic is +distributed amongst the back-end nodes.

          +
          +

          List Load Balancing Algorithms

          +

          You can list all supported load balancing algorithms using a load +balancer service object. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $algorithms = $loadBalancerService->algorithmList();
          +foreach ($algorithms as $algorithm) {
          +    /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/
          +}
          +
          +
          +
          +
          +
          +

          Protocols

          +

          When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced.

          +
          +

          List Load Balancing Protocols

          +

          You can list all supported network protocols using a load balancer +service object. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $protocols = $loadBalancerService->protocolList();
          +foreach ($protocols as $protocol) {
          +    /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/
          +}
          +
          +
          +
          +
          +
          +

          Session Persistence

          +

          Session persistence is a feature of the load balancing service that +forces multiple requests, of the same protocol, from clients to be +directed to the same node. This is common with many web applications +that do not inherently share application state between back-end servers.

          +

          There are two types (or modes) of session persistence:

          + ++++ + + + + + + + + + + + + + +
          NameDescription
          HTTP_COOKIEA session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only.
          SOURCE_IPA session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only.
          +
          +

          List Session Persistence Configuration

          +
          $sessionPersistence = $loadBalancer->sessionPersistence();
          +$sessionPersistenceType = $sessionPersistence->persistenceType;
          +
          +/** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/
          +
          +
          +

          In the example above:

          +
            +
          • If session persistence is enabled, the value of +$sessionPersistenceType is the type of session persistence: +either HTTP_COOKIE or SOURCE_IP.
          • +
          • If session persistence is disabled, the value of +$sessionPersistenceType is null.
          • +
          +
          +
          +

          Enable Session Persistence

          +
          $sessionPersistence = $loadBalancer->sessionPersistence();
          +$sessionPersistence->update(array(
          +    'persistenceType' => 'HTTP_COOKIE'
          +));
          +
          +
          +
          +
          +

          Disable Session Persistence

          +
          $sessionPersistence = $loadBalancer->sessionPersistence();
          +$sessionPersistence->delete();
          +
          +
          +
          +
          +
          +

          Connection Logging

          +

          The connection logging feature allows logs to be delivered to a +Cloud Files account every hour. For HTTP-based protocol traffic, these +are Apache-style access logs. For all other traffic, this is connection +and transfer logging.

          +
          +

          Check Logging Configuration

          +
          /** @var $connectionLogging bool **/
          +
          +$connectionLogging = $loadBalancer->hasConnectionLogging();
          +
          +
          +

          In the example above:

          +
            +
          • If connection logging is enabled, the value of $connectionLogging +is true.
          • +
          • If connection logging is disabled, the value of +$connectionLogging is false.
          • +
          +
          +
          +

          Enable Connection Logging

          +
          $loadBalancer->enableConnectionLogging(true);
          +
          +
          +
          +
          +

          Disable Connection Logging

          +
          $loadBalancer->enableConnectionLogging(false);
          +
          +
          +
          +
          +
          +

          Error Page

          +

          An error page is the html file that is shown to the end user when an +error in the service has been thrown. By default every virtual server is +provided with the default error file. It is also possible to set a +custom error page for a load balancer.

          +
          +

          View Error Page Content

          +
          $errorPage = $loadBalancer->errorPage();
          +$errorPageContent = $errorPage->content;
          +
          +/** @var $errorPageContent string **/
          +
          +
          +

          In the example above the value of $errorPageContent is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page.

          +
          +
          +

          Set Custom Error Page

          +
          $errorPage = $loadBalancer->errorPage();
          +$errorPage->update(array(
          +    'content' => '<HTML content of custom error page>'
          +));
          +
          +
          +
          +
          +

          Delete Custom Error Page

          +
          $errorPage = $loadBalancer->errorPage();
          +$errorPage->delete();
          +
          +
          +
          +
          +
          +

          Allowed Domains

          +

          Allowed domains are a restricted set of domain names that are +allowed to add load balancer nodes.

          +
          +

          List Allowed Domains

          +

          You can list all allowed domains using a load balancer service object. +An instance of OpenCloud\Common\Collection\PaginatedIterator is +returned.

          +
          $allowedDomains = $loadBalancerService->allowedDomainList();
          +foreach ($allowedDomains as $allowedDomain) {
          +    /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/
          +}
          +
          +
          +
          +
          +
          +

          Access Lists

          +

          Access Lists allow fine-grained network access to a load balancer’s +VIP. Using access lists, network traffic to a load balancer’s VIP can be +allowed or denied from a single IP address, multiple IP addresses or +entire network subnets.

          +

          Note that ALLOW network items will take precedence over DENY +network items in an access list.

          +

          To reject traffic from all network items except those with the ALLOW +type, add a DENY network item with the address of 0.0.0.0/0.

          +
          +

          View Access List

          +

          You can view a load balancer’s access list. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $accessList = $loadBalancer->accessList();
          +foreach ($accessList as $networkItem) {
          +    /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/
          +}
          +
          +
          +
          +
          +

          Add Network Items To Access List

          +

          You can add network items to a load balancer’s access list very easily:

          +
          $loadBalancer->createAccessList(array(
          +    (object) array(
          +        'type'    => 'ALLOW',
          +        'address' => '206.160.165.1/24'
          +    ),
          +    (object) array(
          +        'type'    => 'DENY',
          +        'address' => '0.0.0.0/0'
          +    )
          +));
          +
          +
          +

          In the above example, we allowed access for 1 IP address, and used the +“0.0.0.0” wildcard to blacklist all other traffic.

          +
          +
          +

          Remove Network Item From Access List

          +

          You an remove a network item from a load balancer’s access list.

          +
          $networkItem->delete();
          +
          +
          +
          +
          +
          +

          Content Caching

          +

          When content caching is enabled on a load balancer, +recently-accessed files are stored on the load balancer for easy +retrieval by web clients. Requests to the load balancer for these files +are serviced by the load balancer itself, which reduces load off its +back-end nodes and improves response times as well.

          +
          +

          Check Content Caching Configuration

          +
          /** @var $contentCaching bool **/
          +
          +$contentCaching = $loadBalancer->hasContentCaching();
          +
          +
          +

          In the example above:

          +
            +
          • If content caching is enabled, the value of $contentCaching is +true.
          • +
          • If content caching is disabled, the value of $contentCaching is +false.
          • +
          +
          +
          +

          Enable Content Caching

          +
          $loadBalancer->enableContentCaching(true);
          +
          +
          +
          +
          +

          Disable Content Caching

          +
          $loadBalancer->enableContentCaching(false);
          +
          +
          +
          +
          +
          +

          SSL Termination

          +

          The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate.

          +

          When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both.

          +
          +

          View current SSL termination config

          +
          /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/
          +
          +$sslConfig = $loadBalancer->SSLTermination();
          +
          +
          +
          +
          +

          Update SSL termination config

          +
          $sslConfig->update(array(
          +    'enabled'     => true,
          +    'securePort'  => 443,
          +    'privateKey'  => $key,
          +    'certificate' => $cert
          +));
          +
          +
          +

          For a full list, with explanations, of required and optional attributes, +please consult the official +documentation

          +
          +
          +

          Delete SSL termination config

          +
          $sslConfig->delete();
          +
          +
          +
          +
          +
          +

          Metadata

          +

          Metadata can be associated with each load balancer and each node for the +client’s personal use. It is defined using key-value pairs where the key +and value consist of alphanumeric characters. A key is unique per load +balancer.

          +
          +

          List metadata

          +
          $metadataList = $loadBalancer->metadataList();
          +
          +foreach ($metadataList as $metadataItem) {
          +    printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value);
          +}
          +
          +
          +

          Please consult the iterator +documentation for more information +about iterators.

          +
          +
          +

          Add metadata

          +
          $metadataItem = $loadBalancer->metadata();
          +$metadataItem->create(array(
          +    'key'   => 'foo',
          +    'value' => 'bar'
          +));
          +
          +
          +
          +
          +

          Modify metadata

          +
          $metadataItem = $loadBalancer->metadata('foo');
          +$metadataItem->update(array(
          +    'value' => 'baz'
          +));
          +
          +
          +
          +
          +

          Remove metadata

          +
          $metadataItem->delete();
          +
          +
          +
          +
          +
          +

          Monitors

          +

          The load balancing service includes a health monitoring operation which +periodically checks your back-end nodes to ensure they are responding +correctly. If a node is not responding, it is removed from rotation +until the health monitor determines that the node is functional. In +addition to being performed periodically, the health check also is +performed against every node that is added to ensure that the node is +operating properly before allowing it to service traffic. Only one +health monitor is allowed to be enabled on a load balancer at a time.

          +
          /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/
          +
          +$healthMonitor = $loadBalancer->healthMonitor();
          +
          +printf(
          +    "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s",
          +    $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout
          +);
          +
          +
          +

          For a full list, with explanations, of required and optional attributes, +please consult the official +documentation

          +
          +

          Update or delete

          +
          // Update
          +$healthMonitor->update(array(
          +    'delay'   => 120,
          +    'timeout' => 60,
          +    'type'    => 'CONNECT'
          +    'attemptsBeforeDeactivation' => 3
          +));
          +
          +// Delete
          +$healthMonitor->delete();
          +
          +
          +
          +
          +
          +

          Statistics

          +

          You can retrieve detailed stats about your load balancer, including the +following information:

          +
            +
          • connectTimeOut – Connections closed by this load balancer because +the ‘connect_timeout’ interval was exceeded.
          • +
          • connectError – Number of transaction or protocol errors in this +load balancer.
          • +
          • connectFailure – Number of connection failures in this load +balancer.
          • +
          • dataTimedOut – Connections closed by this load balancer because +the ‘timeout’ interval was exceeded.
          • +
          • keepAliveTimedOut – Connections closed by this load balancer +because the ‘keepalive_timeout’ interval was exceeded.
          • +
          • maxConn – Maximum number of simultaneous TCP connections this +load balancer has processed at any one time.
          • +
          +
          /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/
          +
          +$stats = $loadBalancer->stats();
          +
          +
          +
          +
          +

          Usage Reports

          +

          The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred.

          +

          The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified.

          +
          # View billable LBs
          +$billable = $service->billableLoadBalancerList();
          +
          +foreach ($billable as $loadBalancer) {
          +   /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/
          +
          +   # View usage
          +   /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/
          +   $usage = $loadBalancer->usage();
          +
          +   echo $usage->averageNumConnections, PHP_EOL;
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/access.html b/doc/_build/html/services/load-balancer/access.html new file mode 100644 index 000000000..9a07d5eda --- /dev/null +++ b/doc/_build/html/services/load-balancer/access.html @@ -0,0 +1,368 @@ + + + + + + + + + + Allowed Domains — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Allowed Domains

          +
          +

          List Allowed Domains

          +

          You can list all allowed domains using a load balancer service object. +An instance of OpenCloud\Common\Collection\PaginatedIterator is +returned.

          +
          $allowedDomains = $service->allowedDomainList();
          +
          +foreach ($allowedDomains as $allowedDomain) {
          +    /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/
          +}
          +
          +
          +
          +
          +
          +

          Access Lists

          +

          Access Lists allow fine-grained network access to a load balancer’s VIP. Using +access lists, network traffic to a load balancer’s VIP can be allowed or denied +from a single IP address, multiple IP addresses or entire network subnets.

          +

          Note that ALLOW network items will take precedence over DENY network +items in an access list.

          +

          To reject traffic from all network items except those with the ALLOW +type, add a DENY network item with the address of 0.0.0.0/0.

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          View Access List

          +

          You can view a load balancer’s access list:

          +
          $accessList = $loadBalancer->accessList();
          +
          +foreach ($accessList as $networkItem) {
          +    /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/
          +}
          +
          +
          +
          +
          +

          Add Network Items To Access List

          +

          You can add network items to a load balancer’s access list very easily:

          +
          $loadBalancer->createAccessList(array(
          +    (object) array(
          +        'type'    => 'ALLOW',
          +        'address' => '206.160.165.1/24'
          +    ),
          +    (object) array(
          +        'type'    => 'DENY',
          +        'address' => '0.0.0.0/0'
          +    )
          +));
          +
          +
          +

          In the above example, we allowed access for 1 IP address, and used the +“0.0.0.0” wildcard to blacklist all other traffic.

          +

          Get the executable PHP scripts for this example:

          + +
          +
          +

          Remove Network Item From Access List

          +

          You an remove a network item from a load balancer’s access list:

          +
          $networkItem->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/caching.html b/doc/_build/html/services/load-balancer/caching.html new file mode 100644 index 000000000..5f62a9f07 --- /dev/null +++ b/doc/_build/html/services/load-balancer/caching.html @@ -0,0 +1,327 @@ + + + + + + + + + + Content Caching — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Content Caching

          +

          When content caching is enabled on a load balancer, recently-accessed files are +stored on the load balancer for easy retrieval by web clients. Requests to the +load balancer for these files are serviced by the load balancer itself, which +reduces load off its back-end nodes and improves response times as well.

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          Check Configuration

          +
          // TRUE if enabled, FALSE if not
          +$contentCaching = $loadBalancer->hasContentCaching();
          +
          +
          +
          +
          +

          Enable Content Caching

          +
          $loadBalancer->enableContentCaching(true);
          +
          +
          +
          +
          +

          Disable Content Caching

          +
          $loadBalancer->enableContentCaching(false);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/errors.html b/doc/_build/html/services/load-balancer/errors.html new file mode 100644 index 000000000..03d119805 --- /dev/null +++ b/doc/_build/html/services/load-balancer/errors.html @@ -0,0 +1,336 @@ + + + + + + + + + + Error Pages — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Error Pages

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +

          An error page is the html file that is shown to the end user when an error in +the service has been thrown. By default every virtual server is provided with +the default error file. It is also possible to set a custom error page for a +load balancer.

          +
          +
          +

          View Error Page Content

          +
          $errorPage = $loadBalancer->errorPage();
          +$errorPageContent = $errorPage->content;
          +
          +/** @var $errorPageContent string **/
          +
          +
          +

          In the example above the value of $errorPageContent is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page.

          +
          +
          +

          Set Custom Error Page

          +
          $errorPage = $loadBalancer->errorPage();
          +$errorPage->update(array(
          +    'content' => '<HTML content of custom error page>'
          +));
          +
          +
          +
          +
          +

          Delete Custom Error Page

          +
          $errorPage = $loadBalancer->errorPage();
          +$errorPage->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/index.html b/doc/_build/html/services/load-balancer/index.html new file mode 100644 index 000000000..96de80255 --- /dev/null +++ b/doc/_build/html/services/load-balancer/index.html @@ -0,0 +1,493 @@ + + + + + + + + + + Load Balancer v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Load Balancer v1

          +
          +

          Note

          +

          This service is only available for Rackspace users.

          +
          +
          +

          Setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +

          Load Balancer service

          +

          Now to instantiate the Load Balancer service:

          +
          $service = $client->loadBalancerService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          +
          allowed domain
          +
          Allowed domains are a restricted set of domain names that are allowed to add +load balancer nodes.
          +
          content caching
          +
          When content caching is enabled on a load balancer, recently-accessed files +are stored on the load balancer for easy retrieval by web clients. Requests to +the load balancer for these files are serviced by the load balancer itself, +which reduces load off its back-end nodes and improves response times as well.
          +
          health monitor
          +
          The load balancing service includes a health monitoring operation which +periodically checks your back-end nodes to ensure they are responding +correctly. If a node is not responding, it is removed from rotation until the +health monitor determines that the node is functional. In addition to being +performed periodically, the health check also is performed against every node +that is added to ensure that the node is operating properly before allowing it +to service traffic. Only one health monitor is allowed to be enabled on a load +balancer at a time.
          +
          load balancer
          +
          A load balancer is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the nodes of the load balancer.
          +
          metadata
          +
          Metadata can be associated with each load balancer and each node for the +client’s personal use. It is defined using key-value pairs where the key +and value consist of alphanumeric characters. A key is unique per load +balancer.
          +
          node
          +
          A node is a backend device that provides a service on specified IP and port. +An example of a load balancer node might be a web server serving HTTP +traffic on port 8080. A load balancer typically has multiple nodes attached +to it so it can distribute incoming network traffic amongst them.
          +
          session persistence
          +
          Session persistence is a feature of the load balancing service that forces +multiple requests, of the same protocol, from clients to be directed to the +same node. This is common with many web applications that do not inherently +share application state between back-end servers.
          +
          virtual IP
          +
          A virtual IP (VIP) makes a load balancer accessible by clients. The +load balancing service supports either a public VIP address +(PUBLIC), routable on the public Internet, or a ServiceNet VIP +address (SERVICENET), routable only within the region in which the +load balancer resides.
          +
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/lb-setup.sample.html b/doc/_build/html/services/load-balancer/lb-setup.sample.html new file mode 100644 index 000000000..56eadb9cf --- /dev/null +++ b/doc/_build/html/services/load-balancer/lb-setup.sample.html @@ -0,0 +1,287 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/load-balancer.html b/doc/_build/html/services/load-balancer/load-balancer.html new file mode 100644 index 000000000..5134ad239 --- /dev/null +++ b/doc/_build/html/services/load-balancer/load-balancer.html @@ -0,0 +1,433 @@ + + + + + + + + + + Load Balancer — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Load Balancer

          +
          +

          Note

          +

          Many of the examples in this document use two cloud servers as nodes for +the load balancer. The variables $serverOne and $serverTwo refer +to these two cloud servers.

          +
          +
          +

          Create Load Balancer

          +

          The first step is to instantiate an empty object, like so:

          +
          $loadBalancer = $service->loadBalancer();
          +
          +
          +

          In essence, all a load balancer does is evenly distribute traffic between +various back-end nodes - which can be Compute or Database instances. So we will +need to add a few when creating our load balancer:

          +
          $serverOneNode = $loadBalancer->node();
          +$serverOneNode->address = $serverOne->addresses->private[0]->addr;
          +$serverOneNode->port = 8080;
          +$serverOneNode->condition = 'ENABLED';
          +
          +$serverTwoNode = $loadBalancer->node();
          +$serverTwoNode->address = $serverTwo->addresses->private[0]->addr;
          +$serverTwoNode->port = 8080;
          +$serverTwoNode->condition = 'ENABLED';
          +
          +
          +

          All that remains is apply final configuration touches, such as name and the +port number, before submitting to the API:

          +
          $loadBalancer->addVirtualIp('PUBLIC');
          +$loadBalancer->create(array(
          +    'name'      => 'My load balancer',
          +    'port'      => 80,
          +    'protocol'  => 'HTTP',
          +    'nodes'     => array($serverOneNode, $serverTwoNode),
          +    'algorithm' => 'ROUND_ROBIN',
          +));
          +
          +
          +

          For a full list of available protocols and algorithms +please see the sections below.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Get Load Balancer Details

          +

          You can retrieve a single load balancer’s details by using its ID:

          +
          /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/
          +$loadBalancer = $service->loadBalancer('{loadBalancerId}');
          +
          +
          +
          +
          +

          List Load Balancers

          +

          You can retrieve a list of all your load balancers:

          +
          $loadBalancers = $service->loadBalancerList();
          +
          +foreach ($loadBalancers as $loadBalancer) {
          +    /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update a Load Balancer

          +

          You can update one or more of the following load balancer attributes:

          +
            +
          • name: The name of the load balancer
          • +
          • algorithm: The algorithm used by the load balancer to distribute +traffic amongst its nodes. See also: Load balancing +algorithms.
          • +
          • protocol: The network protocol used by traffic coming in to the +load balancer. See also: Protocols.
          • +
          • port: The network port on which the load balancer listens for +incoming traffic.
          • +
          • halfClosed: Enable or Disable Half-Closed support for the load +balancer.
          • +
          • timeout: The timeout value for the load balancer to communicate +with its nodes.
          • +
          • httpsRedirect: Enable or disable HTTP to HTTPS redirection for +the load balancer. When enabled, any HTTP request will return status +code 301 (Moved Permanently), and the requestor will be redirected to +the requested URL via the HTTPS protocol on port 443. For example, +http://example.com/page.html would be redirected to https:// +example.com/page.html. Only available for HTTPS protocol (port = +443), or HTTP Protocol with a properly configured SSL Termination +(`secureTrafficOnly=true, securePort=443). See also: SSL +Termination.
          • +
          +
          $loadBalancer->update(array(
          +    'name'      => 'New name',
          +    'algorithm' => 'ROUND_ROBIN'
          +));
          +
          +
          +
          +

          Remove Load Balancer

          +

          When you no longer have a need for the load balancer, you can remove it:

          +
          $loadBalancer->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Protocols

          +

          When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. Common protocols are HTTP, HTTPS +and MYSQL. A full list is available here.

          +
          +

          List Load Balancing Protocols

          +

          You can list all supported network protocols like so:

          +
          $protocols = $service->protocolList();
          +
          +foreach ($protocols as $protocol) {
          +    /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/
          +}
          +
          +
          +
          +
          +
          +

          Algorithms

          +

          Load balancers use an algorithm to determine how incoming traffic is +distributed amongst the back-end nodes. A full list is available here.

          +
          +

          List Load Balancing Algorithms

          +

          You can programmatically list all supported load balancing algorithms:

          +
          $algorithms = $service->algorithmList();
          +
          +foreach ($algorithms as $algorithm) {
          +    /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/
          +}
          +
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/logging.html b/doc/_build/html/services/load-balancer/logging.html new file mode 100644 index 000000000..a987dcef0 --- /dev/null +++ b/doc/_build/html/services/load-balancer/logging.html @@ -0,0 +1,326 @@ + + + + + + + + + + Connection Logging — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Connection Logging

          +

          The connection logging feature allows logs to be delivered to a Cloud Files +account every hour. For HTTP-based protocol traffic, these are Apache-style +access logs. For all other traffic, this is connection and transfer logging.

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          Check Configuration

          +
          // TRUE if enabled, FALSE if not
          +$connectionLogging = $loadBalancer->hasConnectionLogging();
          +
          +
          +
          +
          +

          Enable Connection Logging

          +
          $loadBalancer->enableConnectionLogging(true);
          +
          +
          +
          +
          +

          Disable Connection Logging

          +
          $loadBalancer->enableConnectionLogging(false);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/metadata.html b/doc/_build/html/services/load-balancer/metadata.html new file mode 100644 index 000000000..70866abda --- /dev/null +++ b/doc/_build/html/services/load-balancer/metadata.html @@ -0,0 +1,339 @@ + + + + + + + + + + Metadata — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Metadata

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          List metadata

          +
          $metadataList = $loadBalancer->metadataList();
          +
          +foreach ($metadataList as $metadataItem) {
          +    printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value);
          +}
          +
          +
          +
          +
          +

          Add metadata

          +
          $metadataItem = $loadBalancer->metadata();
          +$metadataItem->create(array(
          +    'key'   => 'foo',
          +    'value' => 'bar'
          +));
          +
          +
          +
          +
          +

          Modify metadata

          +
          $metadataItem = $loadBalancer->metadata('foo');
          +$metadataItem->update(array(
          +    'value' => 'baz'
          +));
          +
          +
          +
          +
          +

          Remove metadata

          +
          $metadataItem->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/monitors.html b/doc/_build/html/services/load-balancer/monitors.html new file mode 100644 index 000000000..cec0452a4 --- /dev/null +++ b/doc/_build/html/services/load-balancer/monitors.html @@ -0,0 +1,337 @@ + + + + + + + + + + Health Monitors — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Health Monitors

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          Retrieve monitor details

          +
          /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/
          +
          +$healthMonitor = $loadBalancer->healthMonitor();
          +
          +printf(
          +    "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s",
          +    $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout
          +);
          +
          +
          +

          For a full list, with explanations, of required and optional attributes, +please consult the official +documentation

          +
          +
          +

          Update monitor

          +
          $healthMonitor->update(array(
          +    'delay'   => 120,
          +    'timeout' => 60,
          +    'type'    => 'CONNECT'
          +    'attemptsBeforeDeactivation' => 3
          +));
          +
          +
          +
          +
          +

          Delete monitor

          +
          $healthMonitor->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/nodes.html b/doc/_build/html/services/load-balancer/nodes.html new file mode 100644 index 000000000..d0df9139d --- /dev/null +++ b/doc/_build/html/services/load-balancer/nodes.html @@ -0,0 +1,415 @@ + + + + + + + + + + Nodes — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Nodes

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          List Nodes

          +

          You can list the nodes attached to a load balancer:

          +
          $nodes = $loadBalancer->nodeList();
          +
          +foreach ($nodes as $node) {
          +    /** @var $node OpenCloud\LoadBalancer\Resource\Node **/
          +}
          +
          +
          +
          +
          +

          Add Nodes

          +

          You can attach additional nodes to a load balancer. Assume +$loadBalancer already has two nodes attached to it - $serverOne +and $serverTwo - and you want to attach a third node to it, say +$serverThree, which provides a service on port 8080.

          +

          Important: Remember to call $loadBalancer->addNodes() after all +the calls to $loadBalancer->addNode() as shown below.

          +
          $address = $serverThree->addresses->private[0]->addr;
          +$loadBalancer->addNode($address, 8080);
          +$loadBalancer->addNodes();
          +
          +
          +

          The signature for addNodes is as follows:

          +
          +
          +addNodes($address, $port[, $condition = 'ENABLED'[, $type = null[, $weight = null]]])
          +

          Add a node to a load balancer

          + +++ + + + +
          Parameters:
            +
          • $address (string) – the IP address of the node
          • +
          • $port (integer) – the port number of the node
          • +
          • $condition (string) – the initial condition of the code. Defaults to ENABLED
          • +
          • $type (string) – either PRIMARY or SECONDARY
          • +
          • $weight (integer) – the node weight (for round-robin algorithm)
          • +
          +
          +
          + +

          The addNode method accepts three more optional parameters, in +addition to the two shown above:

          +
          +
          +

          Modify Nodes

          +

          You can modify one or more of the following node attributes:

          +
            +
          • condition: The condition of the load balancer:
              +
            • ENABLED – Node is ready to receive traffic from the load +balancer.
            • +
            • DISABLED – Node should not receive traffic from the load +balancer.
            • +
            • DRAINING – Node should process any traffic it is already +receiving but should not receive any further traffic from the load +balancer.
            • +
            +
          • +
          • type: The type of the node:
              +
            • PRIMARY – Nodes defined as PRIMARY are in the normal rotation +to receive traffic from the load balancer.
            • +
            • SECONDARY – Nodes defined as SECONDARY are only in the +rotation to receive traffic from the load balancer when all the +primary nodes fail.
            • +
            +
          • +
          • weight: The weight, between 1 and 100, given to node when +distributing traffic using either the WEIGHTED_ROUND_ROBIN or the +WEIGHTED_LEAST_CONNECTIONS load balancing algorithm.
          • +
          +
          use OpenCloud\LoadBalancer\Enum\NodeCondition;
          +use OpenCloud\LoadBalancer\Enum\NodeType;
          +
          +$node->update(array(
          +    'condition' => NodeCondition::DISABLED,
          +    'type'      => NodeType::SECONDARY
          +));
          +
          +
          +
          +
          +

          Remove Nodes

          +

          There are two ways to remove a node. The first way is on an +OpenCloud\LoadBalancer\Resource\Node instance, like so:

          +
          $node->delete();
          +
          +
          +

          The second is with an OpenCloud\LoadBalancer\Resource\LoadBalancer +instance and the node’s ID, like so:

          +
          $loadBalancer->removeNode('{nodeId}');
          +
          +
          +

          where ‘{nodeId}’ is the integer ID of the node itself - this is a required value.

          +
          +
          +

          View Node Service Events

          +

          You can view events associated with the activity between a node and a +load balancer:

          +
          $nodeEvents = $loadBalancer->nodeEventList();
          +
          +foreach ($nodeEvents as $nodeEvent) {
          +    /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/sessions.html b/doc/_build/html/services/load-balancer/sessions.html new file mode 100644 index 000000000..ff25f8f22 --- /dev/null +++ b/doc/_build/html/services/load-balancer/sessions.html @@ -0,0 +1,357 @@ + + + + + + + + + + Session Persistence — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Session Persistence

          +

          There are two types (or modes) of session persistence:

          + ++++ + + + + + + + + + + + + + +
          NameDescription
          HTTP_COOKIEA session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only.
          SOURCE_IPA session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only.
          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          List Session Persistence Configuration

          +
          $sessionPersistence = $loadBalancer->sessionPersistence();
          +
          +/** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/
          +$sessionPersistenceType = $sessionPersistence->persistenceType;
          +
          +
          +

          In the example above:

          +
            +
          • If session persistence is enabled, the value of +$sessionPersistenceType is the type of session persistence: +either HTTP_COOKIE or SOURCE_IP.
          • +
          • If session persistence is disabled, the value of +$sessionPersistenceType is null.
          • +
          +
          +
          +

          Enable Session Persistence

          +
          $sessionPersistence = $loadBalancer->sessionPersistence();
          +$sessionPersistence->update(array(
          +    'persistenceType' => 'HTTP_COOKIE'
          +));
          +
          +
          +
          +
          +

          Disable Session Persistence

          +
          $sessionPersistence = $loadBalancer->sessionPersistence();
          +$sessionPersistence->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/ssl.html b/doc/_build/html/services/load-balancer/ssl.html new file mode 100644 index 000000000..3eb6a88ca --- /dev/null +++ b/doc/_build/html/services/load-balancer/ssl.html @@ -0,0 +1,344 @@ + + + + + + + + + + SSL Termination — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          SSL Termination

          +

          The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate.

          +

          When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both.

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          View configuration

          +
          /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/
          +$sslConfig = $loadBalancer->SSLTermination();
          +
          +
          +
          +
          +

          Update configuration

          +
          $sslConfig->update(array(
          +    'enabled'     => true,
          +    'securePort'  => 443,
          +    'privateKey'  => $key,
          +    'certificate' => $cert
          +));
          +
          +
          +

          For a full list, with explanations, of required and optional attributes, +please consult the official +documentation

          +

          Get the executable PHP script for this example

          +
          +
          +

          Delete configuration

          +
          $sslConfig->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/stats.html b/doc/_build/html/services/load-balancer/stats.html new file mode 100644 index 000000000..552a3254d --- /dev/null +++ b/doc/_build/html/services/load-balancer/stats.html @@ -0,0 +1,354 @@ + + + + + + + + + + Statistics and Usage Reports — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Statistics and Usage Reports

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          Retrieve LB stats

          +

          You can retrieve detailed stats about your load balancer, including the +following information:

          +
            +
          • connectTimeOut – Connections closed by this load balancer because +the ‘connect_timeout’ interval was exceeded.
          • +
          • connectError – Number of transaction or protocol errors in this +load balancer.
          • +
          • connectFailure – Number of connection failures in this load balancer.
          • +
          • dataTimedOut – Connections closed by this load balancer because +the ‘timeout’ interval was exceeded.
          • +
          • keepAliveTimedOut – Connections closed by this load balancer +because the ‘keepalive_timeout’ interval was exceeded.
          • +
          • maxConn – Maximum number of simultaneous TCP connections this +load balancer has processed at any one time.
          • +
          +
          /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/
          +$stats = $loadBalancer->stats();
          +
          +
          +
          +
          +

          Usage Reports

          +

          The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred.

          +

          The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified.

          +
          # View billable LBs
          +$billable = $service->billableLoadBalancerList();
          +
          +foreach ($billable as $loadBalancer) {
          +   /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/
          +
          +   # View usage
          +   /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/
          +   $usage = $loadBalancer->usage();
          +
          +   echo $usage->averageNumConnections, PHP_EOL;
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/load-balancer/virtual-ips.html b/doc/_build/html/services/load-balancer/virtual-ips.html new file mode 100644 index 000000000..3d662e669 --- /dev/null +++ b/doc/_build/html/services/load-balancer/virtual-ips.html @@ -0,0 +1,354 @@ + + + + + + + + + + Virtual IPs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Virtual IPs

          +
          +

          Setup

          +

          In order to interact with this feature you must first retrieve a particular +load balancer, like so:

          +
          $loadBalancer = $service->loadBalancer('{id}');
          +
          +
          +
          +
          +

          List Virtual IPs

          +

          You can list the VIPs associated with a load balancer like so:

          +
          $vips = $loadBalancer->virtualIpList();
          +
          +foreach ($vips as $vip) {
          +    /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/
          +}
          +
          +
          +
          +
          +

          Get existing VIP

          +

          To retrieve the details of an existing VIP on a load balancer, you will need +its ID:

          +
          +
          +

          Add Virtual IPv6

          +

          You can add additional IPv6 VIPs to a load balancer using the following method:

          +
          use OpenCloud\LoadBalancer\Enum\IpType;
          +
          +$loadBalancer->addVirtualIp(IpType::PUBLIC, 6);
          +
          +
          +

          the first argument is the type of network your IP address will server traffic in +- and can either be PUBLIC or PRIVATE. The second argument is the version +of IP address, either 4 or 6.

          +
          +
          +

          Add Virtual IPv4

          +

          Similar to above:

          +
          use OpenCloud\LoadBalancer\Enum\IpType;
          +
          +$loadBalancer->addVirtualIp(IpType::PUBLIC, 4);
          +
          +
          +
          +
          +

          Remove Virtual IP

          +

          You can remove a VIP from a load balancer.

          +
          $vip->remove();
          +
          +
          +
          +

          Note

          +

          A load balancer must have at least one VIP associated with it. If you try to +remove a load balancer’s last VIP, a ClientErrorResponseException will be +thrown.

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Agents.md.html b/doc/_build/html/services/monitoring/Agents.md.html new file mode 100644 index 000000000..72a31097e --- /dev/null +++ b/doc/_build/html/services/monitoring/Agents.md.html @@ -0,0 +1,439 @@ + + + + + + + + + + Agents — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Agents

          +
          +

          Intro

          +

          The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them.

          +

          For more information about this feature, including a brief overview of +its core design principles and security layers, see the official API +documentation.

          +
          +
          +

          Setup

          +
          $agentId = '00-agent.example.com';
          +$agent   = $service->getAgent($agentId);
          +
          +
          +

          You can view the service page for more information +about setting up the Cloud Monitoring service.

          +
          +
          +

          List agents

          +
          $agents = $service->getAgents();
          +
          +foreach ($agents as $agent) {
          +   echo $agent->getLastConnected();
          +}
          +
          +
          +

          Please consult the iterator doc for +more information about iterators.

          +
          +
          +

          List connections

          +
          $connections = $agent->getConnections();
          +
          +
          +

          Please consult the iterator doc for +more information about iterators.

          +
          +
          +

          Get connection

          +
          /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */
          +$connection = $agent->getConnection('cntl4qsIbA');
          +
          +
          +
          +

          Agent Connection properties

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionTypeMethod
          id
            +
          • +
          +
            +
          • +
          +
          getId()
          guid
            +
          • +
          +
            +
          • +
          +
          getGuid()
          agent_id
            +
          • +
          +
            +
          • +
          +
          getAgentId()
          endpoint
            +
          • +
          +
            +
          • +
          +
          getEndpoint()
          process_version
            +
          • +
          +
            +
          • +
          +
          getProcessVersion()
          bundle_version
            +
          • +
          +
            +
          • +
          +
          getBundleVersion()
          agent_ip
            +
          • +
          +
            +
          • +
          +
          getAgentIp()
          +
          +
          +
          +
          +

          Agent tokens

          +
          +

          Intro

          +

          Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token.

          +
          +
          +

          Setup

          +
          $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234';
          +$agentToken = $service->getAgentToken($tokenId);
          +
          +
          +
          +
          +

          Create agent token

          +
          $newToken = $service->getAgentToken();
          +$newToken->create(array('label' => 'Foobar'));
          +
          +
          +
          +
          +

          List agent tokens

          +
          $agentTokens = $service->getAgentTokens();
          +
          +foreach ($agentTokens as $token) {
          +    echo $token->getLabel();
          +}
          +
          +
          +

          Please consult the iterator doc for +more information about iterators.

          +
          +
          +

          Update and delete Agent Token

          +
          // Update
          +$token->update(array(
          +    'label' => 'New label'
          +));
          +
          +// Delete
          +$token->delete();
          +
          +
          +
          +
          +
          +

          Agent Host Information

          +
          +

          Info

          +

          An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities.

          +
          +
          +

          Setup

          +
          $host = $monitoringService->getAgentHost();
          +
          +
          +
          +
          +

          Get some metrics

          +
          $cpuInfo        = $host->info('cpus');
          +$diskInfo       = $host->info('disks');
          +$filesystemInfo = $host->info('filesystems');
          +$memoryInfo     = $host->info('memory');
          +$networkIntInfo = $host->info('network_interfaces');
          +$processesInfo  = $host->info('processes');
          +$systemInfo     = $host->info('system');
          +$userInfo       = $host->info('who');
          +
          +// What CPU models do we have?
          +foreach ($cpuInfo as $cpuMetric) {
          +    echo $cpuMetric->model, PHP_EOL;
          +}
          +
          +// How many disks do we have?
          +echo $diskInfo->count();
          +
          +// What's the available space on our ext4 filesystem?
          +foreach ($filesystemInfo as $filesystemMetric) {
          +    if ($filesystemMetric->sys_type_name == 'ext4') {
          +        echo $filesystemMetric->avail;
          +    }
          +}
          +
          +
          +
          +
          +
          +

          Agent targets

          +
          +

          Info

          +

          Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +agent.network gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers.

          +
          +

          List agent targets

          +
          $targets = $service->getAgentTargets();
          +
          +foreach ($targets as $target) {
          +    echo $target->getType();
          +}
          +
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Alarms.md.html b/doc/_build/html/services/monitoring/Alarms.md.html new file mode 100644 index 000000000..b84555dd9 --- /dev/null +++ b/doc/_build/html/services/monitoring/Alarms.md.html @@ -0,0 +1,286 @@ + + + + + + + + + + Alarms — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Alarms

          +
          +

          Info

          +

          Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (OK, +WARNING or CRITICAL) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the reference +documentation.

          +
          +
          +

          Setup

          +

          Alarms are sub-resources of Entities:

          +
          $alarmId = 'alAAAA';
          +$alarm = $check->getAlarm();
          +
          +
          +

          For more information about working with Checks, please see the +appropriate documentation.

          +
          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Method
          check_idThe ID of the check to alert on.RequiredgetCheckId()
          notification_plan_idThe ID of the notification plan to execute when the state changes.OptionalgetNotificationPlanId()
          criteriaThe alarm DSL for describing alerting conditions and their output states.OptionalgetCriteria()
          disabledDisable processing and alerts on this alarmOptionalisDisabled() <bool>
          labelA friendly label for an alarm.OptionalgetLabel()
          metadataArbitrary key/value pairs.OptionalgetMetadata()
          +
          +
          +

          Create Alarm

          +
          $alarm->create(array(
          +    'check_id' => 'chAAAA',
          +    'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);',
          +    'notification_plan_id' => 'npAAAAA'
          +));
          +
          +
          +
          +
          +

          List Alarms

          +
          $alarms = $entity->getAlarms();
          +
          +foreach ($alarms as $alarm) {
          +    echo $alarm->getId();
          +}
          +
          +
          +
          +
          +

          Update and delete Alarm

          +
          // Update
          +$alarm->update(array(
          +    'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);'
          +));
          +
          +// Delete
          +$alarm->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Changelogs.md.html b/doc/_build/html/services/monitoring/Changelogs.md.html new file mode 100644 index 000000000..27d72515a --- /dev/null +++ b/doc/_build/html/services/monitoring/Changelogs.md.html @@ -0,0 +1,200 @@ + + + + + + + + + + Changelogs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Changelogs

          +
          +

          Info

          +

          The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information.

          +
          +
          +

          Working with Changelogs

          +
          $changelog = $service->getChangelog();
          +
          +foreach ($changelog as $item) {
          +   $entity = $item->getEntityId();
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Checks.md.html b/doc/_build/html/services/monitoring/Checks.md.html new file mode 100644 index 000000000..643149bf5 --- /dev/null +++ b/doc/_build/html/services/monitoring/Checks.md.html @@ -0,0 +1,456 @@ + + + + + + + + + + Checks — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Checks

          +
          +

          Info

          +

          A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information:

          +
            +
          • A name for the check
          • +
          • The check’s parent entity
          • +
          • The type of check you’re creating
          • +
          • Details of the check
          • +
          • The monitoring zones that will launch the check
          • +
          +

          The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate).

          +
          +
          +

          Setup

          +

          Checks are sub-resources of Entities:

          +
          $checkId = 'chAAAA';
          +$check = $entity->getCheck($checkId);
          +
          +
          +
          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data type
          typeThe type of check.RequiredValid check type. String (1..25 chars)
          detailsDetails specific to the check type.OptionalArray
          disabledDisables the check.OptionalBoolean
          labelA friendly label for a check.OptionalString (1..255 chars)
          metadataArbitrary key/value pairs.OptionalArray
          periodThe period in seconds for a check. The value must be greater than the minimum period set on your account.OptionalInteger (30..1800)
          timeoutThe timeout in seconds for a check. This has to be less than the period.OptionalInteger (2..1800)
          +
          +

          Attributes used for remote Checks

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data type
          monitoring_zones_pollList of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checksOptionalArray
          target_aliasA key in the entity’s ip_addresses hash used to resolve this check to an IP address. This parameter is mutually exclusive with target_hostname.OptionalString (1..64 chars)
          target_hostnameThe hostname this check should target. This parameter is mutually exclusive with target_alias.OptionalValid FQDN, IPv4 or IPv6 address. String (1..256 chars).
          target_resolverDetermines how to resolve the check target.OptionalIPv4 or IPv6
          +
          +
          +
          +

          Test parameters (before create)

          +
          $params = array(
          +    'type'   => 'remote.http',
          +    'details' => array(
          +        'url'    => 'http://example.com',
          +        'method' => 'GET'
          +    ),
          +    'monitoring_zones_poll' => array('mzlon'),
          +    'period' => '100',
          +    'timeout' => '30',
          +    'target_alias' => 'default',
          +    'label'  => 'Website check 1'
          +);
          +
          +// You can do a test to see what would happen
          +// if a Check is launched with these params
          +$response = $entity->testNewCheckParams($params);
          +
          +echo $response->timestamp; // When was it executed?
          +echo $response->available; // Was it available?
          +echo $response->status;    // Status code
          +
          +
          +
          +
          +

          Create a Check

          +
          $entity->createCheck($params);
          +
          +
          +
          +
          +

          Test existing Check

          +
          // Set arg to TRUE for debug information
          +$response = $check->test(true);
          +
          +echo $response->debug_info;
          +
          +
          +
          +
          +

          List Checks

          +
          $checks = $entity->getChecks();
          +
          +foreach ($checks as $check) {
          +    echo $check->getId();
          +}
          +
          +
          +
          +

          Update and delete Check

          +
          // Update
          +$check->update(array('period' => 500));
          +
          +// Delete
          +$check->delete();
          +
          +
          +
          +
          +
          +
          +

          Check types

          +
          +

          Info

          +

          Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. Note: Users cannot create, update or delete +check types.

          +

          Check types for commonly encountered web protocols, such as HTTP +(remote.http), IMAP (remote.imap-banner) , SMTP +(remote.stmp), and DNS (remote.dns) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(remote.mysql-banner) and PostgreSQL (remote.postgresql-banner) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the ‘extract’ attribute to define the format.

          +

          In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +official API +documentation.

          +

          Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check’s parameters. For example, +using the ‘extract’ attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided.

          +
          +
          +

          Setup

          +

          If you want to see the type for an existing Check:

          +
          /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */
          +$checkType = $check->getCheckType();
          +
          +
          +

          Alternatively, you can retrieve a specific type based on its ID:

          +
          $checkTypeId = 'remote.dns';
          +$checkType = $service->getCheckType($checkTypeId);
          +
          +
          +
          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          typeThe name of the supported check type.StringgetType()
          fieldsCheck type fields.ArraygetFields()
          supported_platformsPlatforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtimeArraygetSupportedPlatforms()
          +
          +
          +

          List all possible check types

          +
          $checkTypes = $service->getCheckTypes();
          +
          +foreach ($checkTypes as $checkType) {
          +   echo $checkType->getId();
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Entities.md.html b/doc/_build/html/services/monitoring/Entities.md.html new file mode 100644 index 000000000..ed19e1acc --- /dev/null +++ b/doc/_build/html/services/monitoring/Entities.md.html @@ -0,0 +1,283 @@ + + + + + + + + + +  Entities — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

           Entities

          +
          +

          Info

          +

          An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system – if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster.

          +

          An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check.

          +
          +
          +

          Setup

          +
          $entity = $service->getEntity();
          +
          +
          +

          For more information about setting up the $service object, please +see the userguide tutorial for services.

          +
          +
          +

          Attributes

          + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data typeMethod
          labelDefines a name for the entity.RequiredString (1..255 chars)getLabel()
          agent_idAgent to which this entity is bound to.OptionalString matching the regex: /^[-\.\w]{1,255}$/getAgentId()
          ip_addressesHash of IP addresses that can be referenced by checks on this entity.OptionalArraygetIpAddresses()
          metadataArbitrary key/value pairs that are passed during the alerting phase.OptionalOpenCloud\Common\MetadatagetMetadata()
          +
          +
          +

          Create Entity

          +
          $service->createEntity(array(
          +    'label' => 'Brand New Entity',
          +    'ip_addresses' => array(
          +        'default' => '127.0.0.4',
          +        'b'       => '127.0.0.5',
          +        'c'       => '127.0.0.6',
          +        'test'    => '127.0.0.7'
          +    ),
          +    'metadata' => array(
          +        'all'  => 'kinds',
          +        'of'   => 'stuff',
          +        'can'  => 'go',
          +        'here' => 'null is not a valid value'
          +    )
          +));
          +
          +
          +
          +
          +

          Update and delete Entity

          +
          // Update
          +$entity->update(array(
          +    'label' => 'New label for my entity'
          +));
          +
          +// Delete
          +$entity->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Metrics.md.html b/doc/_build/html/services/monitoring/Metrics.md.html new file mode 100644 index 000000000..365595ffd --- /dev/null +++ b/doc/_build/html/services/monitoring/Metrics.md.html @@ -0,0 +1,347 @@ + + + + + + + + + +  Metrics — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

           Metrics

          +
          +

          Info

          +

          When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points.

          +

          Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time.

          +
          +
          +

          Data Granularity

          +

          Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals.

          +

          When you fetch metrics data points, you specify several parameters to +control the granularity of data returned:

          +
            +
          • A time range for the points
          • +
          • Either the number of points you want returned OR the resolution of +the data you want returned
          • +
          +

          When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution.

          +

          Note: Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request.

          +

          Consider that you want to query data for a 48-hour time range between +the timestamps from=1354647221000 and to=1358794421000 ( +specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970 ). The following table shows the number +of points that the API returns for a given resolution.

          +
          +

          Specifying resolution to retrieve data in 48 hour period

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          You specify resolution...API returns points...
          FULL5760
          MIN5576
          MIN20144
          MIN6048
          MIN24012
          MIN14402
          +
          +
          +

          Specifying number of points to retrieve data in 48 hour period

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          You specify points in the range...API calculates resolution
          3168-∞FULL
          360-3167MIN5
          96-359MIN20
          30-95MIN60
          7-29MIN240
          0-6MIN1440
          +
          +
          +

          Data Point Expiration

          +

          Cloud Monitoring expires data points according to the following +schedule:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          ResolutionExpiration
          FULL2 days
          MIN57 days
          MIN2015 days
          MIN6030 days
          MIN24060 days
          MIN1440365 days
          +
          +
          +
          +

           Setup

          +

          Metrics are sub-resources of Checks. For more information about working +with Checks, please see the relevant documentation.

          +
          +
          +

          List all metrics

          +
          $metrics = $check->getMetrics();
          +
          +foreach ($metrics as $metric) {
          +    echo $metric->getName();
          +}
          +
          +
          +
          +
          +

          Fetch data points

          +
          $data = $check->fetchDataPoints('mzdfw.available', array(
          +    'resolution' => 'FULL',
          +    'from'       => 1369756378450,
          +    'to'         => 1369760279018
          +));
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Notifications.md.html b/doc/_build/html/services/monitoring/Notifications.md.html new file mode 100644 index 000000000..59a52dcb4 --- /dev/null +++ b/doc/_build/html/services/monitoring/Notifications.md.html @@ -0,0 +1,513 @@ + + + + + + + + + + Notifications — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Notifications

          +
          +

          Info

          +

          A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time.

          +

          For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from OK -> CRITICAL and so on).

          +
          +
          +

          Setup

          +
          $id = 'ntAAAA';
          +$notification = $service->getNotification($id);
          +
          +
          +
          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          detailsA hash of notification specific details based on the notification type.ArraygetDetails()
          labelFriendly name for the notification.String (1..255 chars)getLabel()
          typeThe notification type to send.String. Either webhook, email, or pagerdutygetType()
          +
          +
          +

          Test parameters

          +
          $params = array(
          +    'label' => 'My webhook #1',
          +    'type'  => 'webhook',
          +    'details' => array(
          +        'url' => 'http://example.com'
          +    )
          +);
          +
          +// Test it
          +$response = $notification->testParams($params);
          +
          +if ($response->status == 'Success') {
          +    echo $response->message;
          +}
          +
          +
          +
          +
          +

          Create Notification

          +
          $notification->create($params);
          +
          +
          +
          +
          +

          Test existing notification

          +
          $response = $notification->testExisting(true);
          +echo $response->debug_info;
          +
          +
          +
          +
          +

          List Notifications

          +
          $notifications = $service->getNotifications();
          +
          +foreach ($notifications as $notification) {
          +    echo $notification->getId();
          +}
          +
          +
          +
          +
          +

          Update and delete Notifications

          +
          // Update
          +$notification->update(array(
          +    'label' => 'New notification label'
          +));
          +
          +// Delete
          +$notification->delete();
          +
          +
          +
          +
          +
          +

          Notification types

          +
          +

          Info

          +

          Pretty self-explanatory. Rackspace Cloud Monitoring currently supports +the following notification types:

          +
          +

          Webhook

          +

          Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes:

          + +++++ + + + + + + + + + + + + +
          NameDescriptionData type
          addressEmail address to send notifications toValid email
          +
          +
          +

          Email

          +

          Email alerts where the message is delivered to a specified address. It +has these attributes:

          + +++++ + + + + + + + + + + + + +
          NameDescriptionData type
          urlAn HTTP or HTTPS URL to POST toValid URL
          +
          +
          +
          +

          Setup

          +

          If you’ve already set up a main Notification object, and want to access +functionality for this Notification’s particular Notification Type, you +can access its property:

          +
          $type = $notification->getNotificationType();
          +
          +
          +

          Alternatively, you can retrieve an independent resource using the ID:

          +
          $typeId = 'pagerduty';
          +$type = $service->getNotificationType($typeId);
          +
          +
          +
          +
          +

          List all possible notification types

          +
          $types = $service->getNotificationTypes();
          +
          +foreach ($types as $type) {
          +    echo sprintf('%s %s', $type->getName(), $type->getDescription());
          +}
          +
          +
          +
          +
          +
          +

          Notification plans

          +
          +

          Info

          +

          A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications.

          +

          Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states:

          +
            +
          • Critical
          • +
          • Warning
          • +
          • OK
          • +
          +

          A notification plan, npTechnicalContactsEmail, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change.

          +
          +
          +

          Setup

          +
          $planId = 'npAAAA';
          +$plan = $service->getNotificationPlan();
          +
          +
          +
          +
          +

          Attributes

          + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data typeMethod
          labelFriendly name for the notification plan.RequiredString (1..255 chars)getLabel()
          critical_stateThe notification list to send to when the state is CRITICAL.OptionalArraygetCriticalState()
          ok_stateThe notification list to send to when the state is OK.OptionalArraygetOkState()
          warning_stateThe notification list to send to when the state is WARNING.OptionalArraygetWarningState()
          +
          +
          +

          Create Notification Plan

          +
          $plan->create(array(
          +    'label'          => 'New Notification Plan',
          +    'critical_state' => array('ntAAAA'),
          +    'ok_state'       => array('ntBBBB'),
          +    'warning_state'  => array('ntCCCC')
          +));
          +
          +
          +
          +
          +

          Update and delete Notification Plan

          +
          // Update
          +$plan->update(array(
          +    'label' => 'New label for my plan'
          +));
          +
          +// Delete
          +$plan->delete();
          +
          +
          +
          +
          +
          +

          Alarm Notification History

          +
          +

          Info

          +

          The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm’s statuses.

          +

          Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information.

          +
          +
          +

           Setup

          +

          Notification History is a sub-resource of an Alarm. For more information +about working with Alarms, please consult the relevant +documentation.

          +
          +
          +

          Discover which Checks have a Notification History

          +

          This operation list checks for which alarm notification history is +available:

          +
          $checks = $alarm->getRecordedChecks();
          +
          +
          +
          +
          +

          List Alarm Notification History for a particular Check

          +
          $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA');
          +
          +
          +
          +
          +

          Get a particular Notification History item

          +
          $checkId  = 'chAAAA';
          +$itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26';
          +
          +$singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Service.md.html b/doc/_build/html/services/monitoring/Service.md.html new file mode 100644 index 000000000..930f44111 --- /dev/null +++ b/doc/_build/html/services/monitoring/Service.md.html @@ -0,0 +1,203 @@ + + + + + + + + + + Cloud Monitoring Service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Cloud Monitoring Service

          +

          Initializing the Cloud Monitoring is easy - and can be done in a similar +way to all other Rackspace services:

          +
            +
          1. Create client and pass in auth details. For more information about +creating clients, please consult the Client +documentation.
          2. +
          3. Use the factory method, specifying additional parameters where +necessary:
          4. +
          +
          $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL');
          +
          +
          +

          All three parameters are optional - if not specified, it will revert to +the service’s default values which are:

          +
            +
          • Name = cloudMonitoring
          • +
          • Region = DFW
          • +
          • URL type = publicURL
          • +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Views.md.html b/doc/_build/html/services/monitoring/Views.md.html new file mode 100644 index 000000000..bb2ebdfca --- /dev/null +++ b/doc/_build/html/services/monitoring/Views.md.html @@ -0,0 +1,203 @@ + + + + + + + + + + Views — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Views

          +
          +

          Info

          +

          Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint.

          +
          +
          +

          List all Views

          +

          ```php $views = $service->getViews();

          +

          foreach ($views as $view) { $entity = $view->getEntity();

          +
          echo $view->getTimestamp();
          +
          +
          +

          } ```

          +

          Please consult the iterator doc for +more information about iterators.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/Zones.md.html b/doc/_build/html/services/monitoring/Zones.md.html new file mode 100644 index 000000000..d6808c0d4 --- /dev/null +++ b/doc/_build/html/services/monitoring/Zones.md.html @@ -0,0 +1,263 @@ + + + + + + + + + + Zones — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Zones

          +
          +

          Info

          +

          A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are “US West”, “DFW1” or “ORD1”. +It is an abstraction for a general location from which data is +collected.

          +

          An “endpoint,” also known as a “collector,” collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range.

          +

          A check references a list of monitoring zones it should be run from.

          +
          +
          +

           Setup

          +
          $zoneId = 'mzAAAAA';
          +$zone = $monitoringService->getMonitoringZone($zoneId);
          +
          +
          +
          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          country_codeCountry CodeString longer than 2 charactersgetCountryCode()
          labelLabelStringgetLabel()
          source_ipsSource IP listArraygetSourceIps()
          +
          +
          +

           List all zones

          +
          $zones = $service->getMonitoringZones();
          +
          +
          +

          Please consult the iterator doc for +more information about iterators.

          +
          +
          +

          Perform a traceroute

          +
          $traceroute = $zone->traceroute(array(
          +    'target' => 'http://test.com',
          +    'target_resolver' => 'IPv4'
          +));
          +
          +// How many hops?
          +echo count($traceroute);
          +
          +// What was the first hop's IP?
          +echo $traceroute[0]->ip;
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/agents.html b/doc/_build/html/services/monitoring/agents.html new file mode 100644 index 000000000..20c723912 --- /dev/null +++ b/doc/_build/html/services/monitoring/agents.html @@ -0,0 +1,470 @@ + + + + + + + + + + Agents — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Agents

          +

          The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them.

          +

          For more information about this feature, including a brief overview of +its core design principles and security layers, see the official API +documentation.

          +
          +

          Retrieve details about an agent

          +
          $agent = $service->getAgent('{agentId}');
          +
          +
          +
          +
          +

          List agents

          +
          $agents = $service->getAgents();
          +
          +foreach ($agents as $agent) {
          +   echo $agent->getLastConnected();
          +}
          +
          +
          +
          +
          +

          List connections

          +
          $connections = $agent->getConnections();
          +
          +
          +
          +
          +

          Get connection

          +
          /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */
          +$connection = $agent->getConnection('{connectionId}');
          +
          +
          +

          Once you have access to an agent’s OpenCloud\CloudMonitoring\Resource\AgentConnection +object, these are the attributes you can access:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameMethod
          idgetId()
          guidgetGuid()
          agent_idgetAgentId()
          endpointgetEndpoint()
          process_versiongetProcessVersion()
          bundle_versiongetBundleVersion()
          agent_ipgetAgentIp()
          +
          +
          +
          +

          Agent tokens

          +

          Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token.

          +
          +

          Retrieve an agent token

          +
          $agentToken = $service->getAgentToken('{tokenId}');
          +
          +
          +
          +
          +

          Create agent token

          +
          $newToken = $service->getAgentToken();
          +$newToken->create(array('label' => 'Foobar'));
          +
          +
          +
          +
          +

          List agent tokens

          +
          $agentTokens = $service->getAgentTokens();
          +
          +foreach ($agentTokens as $token) {
          +    echo $token->getLabel();
          +}
          +
          +
          +
          +
          +

          Update agent token

          +
          $token->update(array(
          +    'label' => 'New label'
          +));
          +
          +
          +
          +
          +

          Update agent token

          +
          $token->delete();
          +
          +
          +
          +
          +
          +

          Agent Host Information

          +

          An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities.

          +
          +

          Setup

          +
          $host = $service->getAgentHost();
          +
          +
          +
          +
          +

          Get some metrics

          +
          $cpuInfo        = $host->info('cpus');
          +$diskInfo       = $host->info('disks');
          +$filesystemInfo = $host->info('filesystems');
          +$memoryInfo     = $host->info('memory');
          +$networkIntInfo = $host->info('network_interfaces');
          +$processesInfo  = $host->info('processes');
          +$systemInfo     = $host->info('system');
          +$userInfo       = $host->info('who');
          +
          +// What CPU models do we have?
          +foreach ($cpuInfo as $cpuMetric) {
          +    echo $cpuMetric->model, PHP_EOL;
          +}
          +
          +// How many disks do we have?
          +echo $diskInfo->count();
          +
          +// What's the available space on our ext4 filesystem?
          +foreach ($filesystemInfo as $filesystemMetric) {
          +    if ($filesystemMetric->sys_type_name == 'ext4') {
          +        echo $filesystemMetric->avail;
          +    }
          +}
          +
          +
          +
          +
          +
          +

          Agent targets

          +

          Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +agent.network gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers.

          +
          +

          List agent targets

          +
          $targets = $service->getAgentTargets();
          +
          +foreach ($targets as $target) {
          +    echo $target->getType();
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/alarms.html b/doc/_build/html/services/monitoring/alarms.html new file mode 100644 index 000000000..2d77a16a8 --- /dev/null +++ b/doc/_build/html/services/monitoring/alarms.html @@ -0,0 +1,407 @@ + + + + + + + + + + Alarms — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Alarms

          +

          Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (OK, +WARNING or CRITICAL) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the reference +documentation.

          +
          +

          Setup

          +

          In order to interact with this feature, you must first retrieve an entity by +its ID:

          +
          $entity = $service->getEntity('{entityId}');
          +
          +
          +

          and then a particular check, about which you can configure alarms:

          +
          $check = $entity->getCheck('{checkId}');
          +
          +
          +

          For more information about these resource types, please consult the documentation +about entities and checks.

          +
          +
          +

          Retrieve alarm

          +
          $alarm = $check->getAlarm('{alarmId}');
          +
          +
          +

          Once you have access to a OpenCloud\Monitoring\Resource\Alarm object, these +are the attributes you can access:

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Method
          check_idThe ID of the check to alert on.RequiredgetCheckId()
          notification_plan_idThe ID of the notification plan to execute when the state changes.OptionalgetNotificationPlanId()
          criteriaThe alarm DSL for describing alerting conditions and their output states.OptionalgetCriteria()
          disabledDisable processing and alerts on this alarmOptionalisDisabled() <bool>
          labelA friendly label for an alarm.OptionalgetLabel()
          metadataArbitrary key/value pairs.OptionalgetMetadata()
          +
          +
          +

          Create Alarm

          +
          $alarm = $check->getAlarm();
          +$alarm->create(array(
          +    'check_id' => 'chAAAA',
          +    'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);',
          +    'notification_plan_id' => 'npAAAAA'
          +));
          +
          +
          +
          +
          +

          List Alarms

          +
          $alarms = $entity->getAlarms();
          +
          +foreach ($alarms as $alarm) {
          +    echo $alarm->getId();
          +}
          +
          +
          +
          +
          +

          Update Alarm

          +
          $alarm->update(array(
          +    'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);'
          +));
          +
          +
          +
          +
          +

          Delete alarm

          +
          $alarm->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/changelogs.html b/doc/_build/html/services/monitoring/changelogs.html new file mode 100644 index 000000000..524d11376 --- /dev/null +++ b/doc/_build/html/services/monitoring/changelogs.html @@ -0,0 +1,309 @@ + + + + + + + + + + Changelogs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Changelogs

          +

          The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information.

          +
          +

          View Changelog

          +
          $changelog = $service->getChangelog();
          +
          +foreach ($changelog as $item) {
          +   $entity = $item->getEntityId();
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/checks.html b/doc/_build/html/services/monitoring/checks.html new file mode 100644 index 000000000..097026d98 --- /dev/null +++ b/doc/_build/html/services/monitoring/checks.html @@ -0,0 +1,570 @@ + + + + + + + + + + Checks — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Checks

          +

          A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information:

          +
            +
          • A name for the check
          • +
          • The check’s parent entity
          • +
          • The type of check you’re creating
          • +
          • Details of the check
          • +
          • The monitoring zones that will launch the check
          • +
          +

          The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate).

          +
          +

          Create a check

          +

          There are various attributes available to you when creating a new monitoring +check:

          +
          $params = array(
          +    'type'   => 'remote.http',
          +    'details' => array(
          +        'url'    => 'http://example.com',
          +        'method' => 'GET'
          +    ),
          +    'monitoring_zones_poll' => array('mzlon'),
          +    'period' => '100',
          +    'timeout' => '30',
          +    'target_alias' => 'default',
          +    'label'  => 'Website check 1'
          +);
          +
          +
          +

          For a full list of available attributes, consult the list below.

          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data type
          typeThe type of check.RequiredValid check type. String (1..25 chars)
          detailsDetails specific to the check type.OptionalArray
          disabledDisables the check.OptionalBoolean
          labelA friendly label for a check.OptionalString (1..255 chars)
          metadataArbitrary key/value pairs.OptionalArray
          periodThe period in seconds for a check. The value must be greater than the minimum period set on your account.OptionalInteger (30..1800)
          timeoutThe timeout in seconds for a check. This has to be less than the period.OptionalInteger (2..1800)
          +
          +
          +

          Optional attributes to be used with remote checks

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data type
          monitoring_zones_pollList of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checksOptionalArray
          target_aliasA key in the entity’s ip_addresses hash used to resolve this check to an IP address. This parameter is mutually exclusive with target_hostname.OptionalString (1..64 chars)
          target_hostnameThe hostname this check should target. This parameter is mutually exclusive with target_alias.OptionalValid FQDN, IPv4 or IPv6 address. String (1..256 chars).
          target_resolverDetermines how to resolve the check target.OptionalIPv4 or IPv6
          +
          +
          +
          +

          Test parameters

          +

          Sometimes it can be useful to test out the parameters before sending them as a +create call. To do this, pass in the $params like so:

          +
          $response = $entity->testNewCheckParams($params);
          +
          +echo $response->timestamp; // When was it executed?
          +echo $response->available; // Was it available?
          +echo $response->status;    // Status code
          +
          +
          +
          +

          Send parameters

          +

          Once you are satisfied with your configuration parameters, you can complete the +operation and send it to the API like so:

          +
          $entity->createCheck($params);
          +
          +
          +
          +
          +
          +

          Test existing Check

          +
          // Set arg to TRUE for debug information
          +$response = $check->test(true);
          +
          +echo $response->debug_info;
          +
          +
          +
          +
          +

          List Checks

          +
          $checks = $entity->getChecks();
          +
          +foreach ($checks as $check) {
          +    echo $check->getId();
          +}
          +
          +
          +
          +
          +

          Update Check

          +
          $check->update(array('period' => 500));
          +
          +
          +
          +
          +

          Delete check

          +
          $check->delete();
          +
          +
          +
          +
          +
          +

          Check types

          +

          Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. Note: Users cannot create, update or delete +check types.

          +

          Check types for commonly encountered web protocols, such as HTTP +(remote.http), IMAP (remote.imap-banner) , SMTP +(remote.stmp), and DNS (remote.dns) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(remote.mysql-banner) and PostgreSQL (remote.postgresql-banner) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the ‘extract’ attribute to define the format.

          +

          In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +official API +documentation.

          +

          Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check’s parameters. For example, +using the ‘extract’ attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided.

          +
          +

          Find an existing check’s type

          +

          If you want to see the type for an existing Check resource:

          +
          /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */
          +$checkType = $check->getCheckType();
          +
          +
          +
          +
          +

          List all possible check types

          +
          $checkTypes = $service->getCheckTypes();
          +
          +foreach ($checkTypes as $checkType) {
          +   echo $checkType->getId();
          +}
          +
          +
          +
          +
          +

          Retrieve details about a Type by its ID

          +

          Alternatively, you can retrieve a specific type based on its ID:

          +
          $checkTypeId = 'remote.dns';
          +$checkType = $service->getCheckType($checkTypeId);
          +
          +
          +
          +
          +

          Attributes

          +

          Once you have access to a OpenCloud\CloudMonitoring\Resource\CheckType object, +you can query these attributes:

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          typeThe name of the supported check type.StringgetType()
          fieldsCheck type fields.ArraygetFields()
          supported_platformsPlatforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtimeArraygetSupportedPlatforms()
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/entities.html b/doc/_build/html/services/monitoring/entities.html new file mode 100644 index 000000000..6b26160af --- /dev/null +++ b/doc/_build/html/services/monitoring/entities.html @@ -0,0 +1,386 @@ + + + + + + + + + +  Entities — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

           Entities

          +

          An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system – if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster.

          +

          An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check.

          +
          +

          Create Entity

          +
          $service->createEntity(array(
          +    'label' => 'Brand New Entity',
          +    'ip_addresses' => array(
          +        'default' => '127.0.0.4',
          +        'b'       => '127.0.0.5',
          +        'c'       => '127.0.0.6',
          +        'test'    => '127.0.0.7'
          +    ),
          +    'metadata' => array(
          +        'all'  => 'kinds',
          +        'of'   => 'stuff',
          +        'can'  => 'go',
          +        'here' => 'null is not a valid value'
          +    )
          +));
          +
          +
          +
          +
          +

          Retrive an entity

          +
          $entity = $service->getEntity('{entityId}');
          +
          +
          +
          +

          Attributes

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          labelDefines a name for the entity.String (1..255 chars)getLabel()
          agent_idAgent to which this entity is bound to.String matching the regex: /^[-\.\w]{1,255}$/getAgentId()
          ip_addressesHash of IP addresses that can be referenced by checks on this entity.ArraygetIpAddresses()
          metadataArbitrary key/value pairs that are passed during the alerting phase.OpenCloud\Common\MetadatagetMetadata()
          +
          +
          +
          +

          Update an entity

          +
          $entity->update(array(
          +    'label' => 'New label for my entity'
          +));
          +
          +
          +
          +
          +

          Delete entity

          +
          $entity->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/index.html b/doc/_build/html/services/monitoring/index.html new file mode 100644 index 000000000..1eec07d17 --- /dev/null +++ b/doc/_build/html/services/monitoring/index.html @@ -0,0 +1,517 @@ + + + + + + + + + + Monitoring v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Monitoring v1

          +
          +

          Note

          +

          This service is only available for Rackspace users.

          +
          +
          +

          Setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +

          Monitoring service

          +

          Now to instantiate the Monitoring service:

          +
          $service = $client->monitoringService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          +
          +

          Operations

          +
          + +
          +
          +
          +

          Glossary

          +
          +
          agent
          +
          A monitoring daemon that resides on the server being monitored. The agent +gathers metrics based on agent checks and pushes them to Cloud Monitoring. +The agent provides insight into your servers with checks for information +such as load average and network usage. The agent acts as a single small +service that runs scheduled checks and pushes metrics to the rest of Cloud +Monitoring so the metrics can be analyzed, trigger alerts, and be archived. +These metrics are gathered via checks using agent check types, and can be +used with the other Cloud Monitoring primitives such as alarms.
          +
          agent token
          +
          An authentication token used to identify the agent when it communicates +with Cloud Monitoring.
          +
          alarm
          +
          An alarm contains a set of rules that determine when the monitoring system +sends a notification. You can create multiple alarms for the different +checks types associated with an entity. For example, if your entity is a +web server that hosts your company’s website, you can create one alarm to +monitor the server itself, and another alarm to monitor the website.
          +
          check
          +
          Checks explicitly specify how you want to monitor an entity. Once you’ve +created an entity, you can configure one or more checks for it. A check is +the foundational building block of the monitoring system, and is always +associated with an entity. The check specifies the parts or pieces of the +entity that you want to monitor, the monitoring frequency, how many +monitoring zones are launching the check, and so on. It contains the +specific details of how you are monitoring the entity.
          +
          entity
          +
          The object or resource that you want to monitor. It can be any object or +device that you want to monitor. It’s commonly a web server, but it might +also be a website, a web page or a web service.
          +
          monitoring zone
          +
          A monitoring zone is the “launch point” of a check. When you create a +check, you specify which monitoring zone(s) you want to launch the check +from. This concept of a monitoring zone is similar to that of a datacenter, +however in the monitoring system, you can think of it more as a geographical +region.
          +
          notification
          +
          A notification is an informational message sent to one or more addresses +by the monitoring system when an alarm is triggered. You can set up +notifications to alert a single individual or an entire team. Rackspace +Cloud Monitoring currently supports webhooks and email for sending +notifications.
          +
          notification plan
          +
          A notification plan contains a set of notification rules to execute when an +alarm is triggered. A notification plan can contain multiple notifications +for each of the following states:
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/metrics.html b/doc/_build/html/services/monitoring/metrics.html new file mode 100644 index 000000000..1b0bfd6f2 --- /dev/null +++ b/doc/_build/html/services/monitoring/metrics.html @@ -0,0 +1,465 @@ + + + + + + + + + +  Metrics — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

           Metrics

          +

          When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points.

          +

          Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time.

          +
          +

          Data Granularity

          +

          Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals.

          +

          When you fetch metrics data points, you specify several parameters to +control the granularity of data returned:

          +
            +
          • A time range for the points
          • +
          • Either the number of points you want returned OR the resolution of +the data you want returned
          • +
          +

          When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution.

          +

          Note: Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request.

          +

          Consider that you want to query data for a 48-hour time range between +the timestamps from=1354647221000 and to=1358794421000 ( +specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970 ). The following table shows the number +of points that the API returns for a given resolution.

          +
          +

          Specifying resolution to retrieve data in 48 hour period

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          You specify resolution...API returns points...
          FULL5760
          MIN5576
          MIN20144
          MIN6048
          MIN24012
          MIN14402
          +
          +
          +

          Specifying number of points to retrieve data in 48 hour period

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          You specify points in the range...API calculates resolution
          3168-∞FULL
          360-3167MIN5
          96-359MIN20
          30-95MIN60
          7-29MIN240
          0-6MIN1440
          +
          +
          +

          Data Point Expiration

          +

          Cloud Monitoring expires data points according to the following +schedule:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          ResolutionExpiration
          FULL2 days
          MIN57 days
          MIN2015 days
          MIN6030 days
          MIN24060 days
          MIN1440365 days
          +
          +
          +
          +

          Setup

          +

          In order to interact with this feature, you must first retrieve an entity by +its ID:

          +
          $entity = $service->getEntity('{entityId}');
          +
          +
          +

          and then a particular check, about which you can configure alarms:

          +
          $check = $entity->getCheck('{checkId}');
          +
          +
          +

          For more information about these resource types, please consult the documentation +about entities and checks.

          +
          +
          +

          List all metrics

          +
          $metrics = $check->getMetrics();
          +
          +foreach ($metrics as $metric) {
          +    echo $metric->getName();
          +}
          +
          +
          +
          +
          +

          Fetch data points

          +
          $data = $check->fetchDataPoints('mzdfw.available', array(
          +    'resolution' => 'FULL',
          +    'from'       => 1369756378450,
          +    'to'         => 1369760279018
          +));
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/notifications.html b/doc/_build/html/services/monitoring/notifications.html new file mode 100644 index 000000000..4f1bd357f --- /dev/null +++ b/doc/_build/html/services/monitoring/notifications.html @@ -0,0 +1,628 @@ + + + + + + + + + + Notifications — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Notifications

          +

          A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time.

          +

          For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from OK -> CRITICAL and so on).

          +
          +

          Get notification

          +
          $notification = $service->getNotification('{id}');
          +
          +
          +

          Once you have access to a OpenCloud\Monitoring\Resource\Notification object, +these are the attributes available for use:

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          detailsA hash of notification specific details based on the notification type.ArraygetDetails()
          labelFriendly name for the notification.String (1..255 chars)getLabel()
          typeThe notification type to send.String. Either webhook, email, or pagerdutygetType()
          +
          +
          +

          Creating notifications

          +

          The first thing to do when creating a new notification is configure the +parameters which will define the behaviour of your resource:

          +
          $params = array(
          +    'label' => 'My webhook #1',
          +    'type'  => 'webhook',
          +    'details' => array(
          +        'url' => 'http://example.com'
          +    )
          +);
          +
          +
          +
          +

          Test parameters

          +

          Once this is done, it is often useful to test them out to check whether they +will result in a successful creation:

          +
          // Test it
          +$response = $notification->testParams($params);
          +
          +if ($response->status == 'Success') {
          +    echo $response->message;
          +}
          +
          +
          +
          +
          +

          Send parameters

          +

          When you’re happy with the parameters you’ve defined, you can complete the +operation by sending them to the API like so:

          +
          $notification->create($params);
          +
          +
          +
          +
          +
          +

          Test existing notification

          +
          $response = $notification->testExisting(true);
          +echo $response->debug_info;
          +
          +
          +
          +
          +

          List Notifications

          +
          $notifications = $service->getNotifications();
          +
          +foreach ($notifications as $notification) {
          +    echo $notification->getId();
          +}
          +
          +
          +
          +
          +

          Update a Notification

          +
          $notification->update(array(
          +    'label' => 'New notification label'
          +));
          +
          +
          +
          +
          +

          Delete a Notification

          +
          $notification->delete();
          +
          +
          +
          +
          +
          +

          Notification types

          +

          Rackspace Cloud Monitoring currently supports the following notification types:

          +

          Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes:

          + +++++ + + + + + + + + + + + + +
          NameDescriptionData type
          addressEmail address to send notifications toValid email
          +

          Email alerts where the message is delivered to a specified address. It +has these attributes:

          + +++++ + + + + + + + + + + + + +
          NameDescriptionData type
          urlAn HTTP or HTTPS URL to POST toValid URL
          +
          +

          Setup

          +

          If you’ve already set up a main Notification object, and want to access +functionality for this Notification’s particular Notification Type, you +can access its property:

          +
          $type = $notification->getNotificationType();
          +
          +
          +

          Alternatively, you can retrieve an independent resource using the ID:

          +
          $typeId = 'pagerduty';
          +$type = $service->getNotificationType($typeId);
          +
          +
          +
          +
          +

          List all possible notification types

          +
          $types = $service->getNotificationTypes();
          +
          +foreach ($types as $type) {
          +    echo sprintf('%s %s', $type->getName(), $type->getDescription());
          +}
          +
          +
          +
          +
          +
          +

          Notification plans

          +

          A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications.

          +

          Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states:

          +
            +
          • Critical
          • +
          • Warning
          • +
          • OK
          • +
          +

          A notification plan, npTechnicalContactsEmail, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change.

          +
          +

          Get a notification plan

          +
          $plan = $service->getNotificationPlan('{planId}');
          +
          +
          +

          Once you have access to a OpenCloud\\Monitoring\\Resource\\NotificationPlan +object, you can access these resources:

          + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionRequired?Data typeMethod
          labelFriendly name for the notification plan.RequiredString (1..255 chars)getLabel()
          critical_stateThe notification list to send to when the state is CRITICAL.OptionalArraygetCriticalState()
          ok_stateThe notification list to send to when the state is OK.OptionalArraygetOkState()
          warning_stateThe notification list to send to when the state is WARNING.OptionalArraygetWarningState()
          +
          +
          +

          Create Notification Plan

          +
          $plan->create(array(
          +    'label'          => 'New Notification Plan',
          +    'critical_state' => array('ntAAAA'),
          +    'ok_state'       => array('ntBBBB'),
          +    'warning_state'  => array('ntCCCC')
          +));
          +
          +
          +
          +
          +

          Update notification plan

          +
          $plan->update(array(
          +    'label' => 'New label for my plan'
          +));
          +
          +
          +
          +
          +

          Delete notification plan

          +
          $plan->delete();
          +
          +
          +
          +
          +
          +

          Alarm Notification History

          +

          The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm’s statuses.

          +

          Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information.

          +
          +

          Setup

          +

          In order to interact with this feature, you must first retrieve an entity by +its ID:

          +
          $entity = $service->getEntity('{entityId}');
          +
          +
          +

          and then a particular check, about which you can configure alarms:

          +
          $check = $entity->getCheck('{checkId}');
          +
          +
          +

          and finally, retrieve the alarm:

          +
          $alarm = $check->getAlarm('{alarmId}');
          +
          +
          +

          For more information about these resource types, please consult the documentation +about entities and checks.

          +
          +
          +

          Discover which Checks have a Notification History

          +

          This operation list checks for which alarm notification history is +available:

          +
          $checks = $alarm->getRecordedChecks();
          +
          +
          +
          +
          +

          List Alarm Notification History for a particular Check

          +
          $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA');
          +
          +
          +
          +
          +

          Get a particular Notification History item

          +
          $checkId  = 'chAAAA';
          +$itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26';
          +
          +$singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/views.html b/doc/_build/html/services/monitoring/views.html new file mode 100644 index 000000000..7c018f6c5 --- /dev/null +++ b/doc/_build/html/services/monitoring/views.html @@ -0,0 +1,312 @@ + + + + + + + + + + Views — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Views

          +

          Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint.

          +
          +

          List all Views

          +
          $views = $service->getViews();
          +
          +foreach ($views as $view) {
          +    $entity = $view->getEntity();
          +    echo $view->getTimestamp();
          +}
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/monitoring/zones.html b/doc/_build/html/services/monitoring/zones.html new file mode 100644 index 000000000..0a3f60040 --- /dev/null +++ b/doc/_build/html/services/monitoring/zones.html @@ -0,0 +1,366 @@ + + + + + + + + + + Zones — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Zones

          +

          A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are “US West”, “DFW1” or “ORD1”. +It is an abstraction for a general location from which data is +collected.

          +

          An “endpoint,” also known as a “collector,” collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range.

          +

          A check references a list of monitoring zones it should be run from.

          +
          +

          Get details about a zone

          +
          $zone = $monitoringService->getMonitoringZone('{zoneId}');
          +
          +
          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeMethod
          country_codeCountry CodeString longer than 2 charactersgetCountryCode()
          labelLabelStringgetLabel()
          source_ipsSource IP listArraygetSourceIps()
          +
          +
          +

           List all zones

          +
          $zones = $service->getMonitoringZones();
          +
          +
          +
          +
          +

          Perform a traceroute

          +
          $traceroute = $zone->traceroute(array(
          +    'target' => 'http://test.com',
          +    'target_resolver' => 'IPv4'
          +));
          +
          +// How many hops?
          +echo count($traceroute);
          +
          +// What was the first hop's IP?
          +echo $traceroute[0]->ip;
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/README.md.html b/doc/_build/html/services/networking/README.md.html new file mode 100644 index 000000000..7840bde62 --- /dev/null +++ b/doc/_build/html/services/networking/README.md.html @@ -0,0 +1,310 @@ + + + + + + + + + + Networking — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Networking

          +

          Networking is a service that you can use to create virtual networks +and attach cloud devices such as servers to these networks.

          +
          +

          Concepts

          +
          +
          +

          Concepts

          +

          To use the Networking service effectively, you should understand the +following key concepts:

          +
            +
          • Network: A network is an isolated virtual layer-2 broadcast +domain that is typically reserved for the tenant who created it +unless you configure the network to be shared. The network is the +main entity in the Networking service. Ports and subnets are always +associated with a network.
          • +
          • Subnet: A subnet represents an IP address block that can be used +to assign IP addresses to virtual instances (such as servers created +using the Compute service). Each subnet must have a CIDR and must be +associated with a network.
          • +
          • Port: A port represents a virtual switch port on a logical +network switch. Virtual instances (such as servers created using the +Compute service) attach their interfaces into ports. The port also +defines the MAC address and the IP address(es) to be assigned to the +interfaces plugged into them. When IP addresses are associated to a +port, this also implies the port is associated with a subet, as the +IP address is taken from the allocation pool for a specific subnet.
          • +
          +
          +
          +

          Getting started

          +
          +

          1. Instantiate an OpenStack or Rackspace client.

          +

          To use the Networking service, you must first instantiate a +OpenStack or Rackspace client object.

          +
            +
          • If you are working with an OpenStack cloud, instantiate an +OpenCloud\OpenStack client as follows:

            +
            use OpenCloud\OpenStack;
            +
            +$client = new OpenStack('<OPENSTACK CLOUD IDENTITY ENDPOINT URL>', array(
            +    'username' => '<YOUR OPENSTACK CLOUD ACCOUNT USERNAME>',
            +    'password' => '<YOUR OPENSTACK CLOUD ACCOUNT PASSWORD>'
            +));
            +
            +
            +
          • +
          • If you are working with the Rackspace cloud, instantiate a +OpenCloud\Rackspace client as follows:

            +
            use OpenCloud\Rackspace;
            +
            +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            + ));
            +
            +
            +
          • +
          +
          +
          +

          2. Obtain an Networking service object from the client.

          +

          All Networking operations are done via an networking service object. +To instantiate this object, call the networkingService method on the +$client object. This method takes two arguments:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          PositionDescriptionData typeRequired?Default valueExample value
          1Name of the service, as it appears in the service catalogStringNonull; automatically determined when possiblecloudNetworks
          2Cloud regionStringYes
            +
          • +
          +
          DFW
          +
          $region = '<CLOUD REGION NAME>';
          +$networkingService = $client->networkingService(null, $region);
          +
          +
          +

          Any networks, subnets, and ports created with this +$networkingService instance will be stored in the cloud region +specified by $region.

          +
          +
          +

          3. Create a network.

          +
          $network = $networkingService->createNetwork(array(
          +    'name' => 'My private backend network'
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Next steps

          +

          Once you have created a network, there is more you can do with it. See +complete user guide for networking.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/USERGUIDE.md.html b/doc/_build/html/services/networking/USERGUIDE.md.html new file mode 100644 index 000000000..0c1c57f60 --- /dev/null +++ b/doc/_build/html/services/networking/USERGUIDE.md.html @@ -0,0 +1,1021 @@ + + + + + + + + + + Complete User Guide for the Networking Service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Complete User Guide for the Networking Service

          +

          Networking is a service that you can use to create virtual networks and +attach cloud devices such as servers to these networks.

          +

          This user guide introduces you the entities in the Networking service — +networks, subnets, and ports — and shows you how to create and manage +these entities.

          + +
          +

          Concepts

          +

          To use the Networking service effectively, you should understand the +following key concepts:

          +
            +
          • Network: An isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it unless it is +configured to be shared. The network is the main entity in the +Networking service. Ports and subnets are always associated with a +network.
          • +
          • Subnet: An IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the +Compute service). Each subnet must have a CIDR and must be associated +with a network.
          • +
          • Port: A virtual switch port on a logical network switch. Virtual +instances (such as servers created using the Compute service) attach +their interfaces into ports. The port also defines the MAC address +and the IP address or addresses to be assigned to the interfaces +plugged into them. When IP addresses are associated with a port, this +also implies the port is associated with a subnet because the IP +address is taken from the allocation pool for a specific subnet.
          • +
          +
          +
          +

          Prerequisites

          +
          +

          Client

          +

          To use the Networking service, you must first instantiate a +OpenStack or Rackspace client object.

          +
            +
          • If you are working with an OpenStack cloud, instantiate an +OpenCloud\OpenStack client as follows:

            +
            use OpenCloud\OpenStack;
            +
            +$client = new OpenStack('<OPENSTACK CLOUD IDENTITY ENDPOINT URL>', array(
            +    'username' => '<YOUR OPENSTACK CLOUD ACCOUNT USERNAME>',
            +    'password' => '<YOUR OPENSTACK CLOUD ACCOUNT PASSWORD>'
            +));
            +
            +
            +
          • +
          • If you are working with the Rackspace cloud, instantiate an +OpenCloud\Rackspace client as follows:

            +
            use OpenCloud\Rackspace;
            +
            +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            +));
            +
            +
            +
          • +
          +
          +
          +

          Networking service

          +

          All Networking operations are done via a networking service object. To +instantiate this object, call the networkingService method on the +$client object. This method takes the following arguments:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          PositionDescriptionData typeRequired?Default valueExample value
          1Name of the service, as it appears in the service catalogStringNonull; automatically determined when possiblecloudNetworks
          2Cloud regionStringYes
            +
          • +
          +
          DFW
          +
          $region = '<CLOUD REGION NAME>';
          +$networkingService = $client->networkingService(null, $region);
          +
          +
          +

          Any networks, subnets, and ports created with this +$networkingService instance are stored in the cloud region specified +by $region.

          +
          +
          +
          +

          Networks

          +

          A network is an isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it unless it is configured +to be shared. The network is the main entity in the Networking service. +Ports and subnets are always associated with a network.

          +
          +

          Create a network

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the network. This name might not be unique.StringNonullMy private backend network
          adminStateUpThe administrative state of network. If false (down), the network does not forward packets.BooleanNotruetrue
          sharedSpecifies whether the network resource can be accessed by any tenant.BooleanNofalsefalse
          tenantIdOwner of network. Only admin users can specify a tenant ID other than their own.StringNoSame as tenant creating the network123456
          +

          You can create a network as shown in the following example:

          +
          $network = $networkingService->createNetwork(array(
          +    'name' => 'My private backend network'
          +));
          +/** @var $network OpenCloud\Networking\Resource\Network **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Create multiple networks

          +

          This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in the +preceding table.

          +

          You can create multiple networks as shown in the following example:

          +
          $networks = $networkingService->createNetworks(array(
          +    array(
          +        'name' => 'My private backend network #1'
          +    ),
          +    array(
          +        'name' => 'My private backend network #2'
          +    )
          +));
          +
          +foreach ($networks as $network) {
          +    /** @var $network OpenCloud\Networking\Resource\Network **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          List networks

          +

          You can list all the networks to which you have access as shown in the +following example:

          +
          $networks = $networkingService->listNetworks();
          +foreach ($networks as $network) {
          +    /** @var $network OpenCloud\Networking\Resource\Network **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get a network

          +

          You can retrieve a specific network by using that network’s ID, as shown +in the following example:

          +
          $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c');
          +/** @var $network OpenCloud\Networking\Resource\Network **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Update a network

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the network. This name might not be unique.StringNonullMy updated private backend network
          adminStateUpThe administrative state of network. If false (down), the network does not forward packets.BooleanNotruetrue
          sharedSpecifies whether the network resource can be accessed by any tenant.BooleanNofalsefalse
          +

          You can update a network as shown in the following example:

          +
          $network->update(array(
          +    'name' => 'My updated private backend network'
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Delete a network

          +

          You can delete a network as shown in the following example:

          +
          $network->delete();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Subnets

          +

          A subnet represents an IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the +Compute service). Each subnet must have a CIDR and must be associated +with a network.

          +
          +

          Create a subnet

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          networkIdNetwork this subnet is associated withStringYes
            +
          • +
          +
          eb60583c-57ea-41b9-8d5c-8fab2d22224c
          ipVersionIP versionInteger (4 or 6)Yes
            +
          • +
          +
          4
          cidrCIDR representing the IP address range for this subnetString (CIDR)Yes
            +
          • +
          +
          192.168.199.0/25
          nameA human-readable name for the subnet. This name might not be unique.StringNonullMy subnet
          gatewayIpIP address of the default gateway used by devices on this subnetString (IP address)NoFirst IP address in CIDR192.168.199.128
          dnsNameserversDNS nameservers used by hosts in this subnetIndexed array of stringsNoEmpty arrayarray('4.4.4.4', '8.8.8.8')
          allocationPoolsSubranges of the CIDR available for dynamic allocation to portsIndexed array of associative arraysNoEvery IP address in CIDR, excluding gateway IP address if configuredarray(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))
          hostRoutesRoutes that should be used by devices with IP addresses from this subnet (not including the local subnet route)Indexed array of associative arraysNoEmpty arrayarray(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))
          enableDhcpSpecifies whether DHCP is enabled for this subnetBooleanNotruefalse
          tenantIdOwner of the subnet. Only admin users can specify a tenant ID other than their own.StringNoSame as tenant creating the subnet123456
          +

          You can create a subnet as shown in the following example:

          +
          $subnet = $networkingService->createSubnet(array(
          +    'name' => 'My subnet',
          +    'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c',
          +    'ipVersion' => 4,
          +    'cidr' => '192.168.199.0/25'
          +));
          +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Create multiple subnets

          +

          This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in the +preceding table.

          +

          You can create multiple subnets as shown in the following example:

          +
          $subnets = $networkingService->createSubnets(array(
          +    array(
          +        'name' => 'My subnet #1'
          +    ),
          +    array(
          +        'name' => 'My subnet #2'
          +    )
          +));
          +
          +foreach ($subnets as $subnet) {
          +    /** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          List subnets

          +

          You can list all the subnets to which you have access as shown in the +following example:

          +
          $subnets = $networkingService->listSubnets();
          +foreach ($subnets as $subnet) {
          +    /** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get a subnet

          +

          You can retrieve a specific subnet by using that subnet’s ID, as shown +in the following example:

          +
          $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e');
          +/** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Update a subnet

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the subnet. This name might not be unique.StringNonullMy updated subnet
          gatewayIpIP address of the default gateway used by devices on this subnetString (IP address)NoFirst IP address in CIDR192.168.62.155
          dnsNameserversDNS nameservers used by hosts in this subnetIndexed array of stringsNoEmpty arrayarray('4.4.4.4', '8.8.8.8')
          hostRoutesRoutes that should be used by devices with IP adresses from this subnet (not including the local subnet route)Indexed array of associative arraysNoEmpty arrayarray(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))
          enableDhcpSpecifies whether DHCP is enabled for this subnetBooleanNotruefalse
          +

          You can update a subnet as shown in the following example:

          +
          $subnet->update(array(
          +    'name' => 'My updated subnet',
          +    'hostRoutes' => array(
          +        array(
          +            'destination' => '1.1.1.0/24',
          +            'nexthop'     => '192.168.17.19'
          +        )
          +    ),
          +    'gatewayIp' => '192.168.62.155'
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Delete a subnet

          +

          You can delete a subnet as shown in the following example:

          +
          $subnet->delete();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Ports

          +

          A port represents a virtual switch port on a logical network switch. +Virtual instances (such as servers created using the Compute service) +attach their interfaces into ports. The port also defines the MAC +address and the IP address or addresses to be assigned to the interfaces +plugged into them. When IP addresses are associated with a port, this +also implies the port is associated with a subnet because the IP address +is taken from the allocation pool for a specific subnet.

          +
          +

          Create a port

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          networkIdNetwork this port is associated withStringYes
            +
          • +
          +
          eb60583c-57ea-41b9-8d5c-8fab2d22224c
          nameA human-readable name for the port. This name might not be unique.StringNonullMy port
          adminStateUpThe administrative state of port. If false (down), the port does not forward packets.BooleanNotruetrue
          macAddressMAC address to use on this portString (MAC address in 6-octet form separated by colons)NoGenerated0F:5A:6F:70:E9:5C
          fixedIpsIP addresses for this portIndexed array of associative arraysNoAutomatically allocated from the poolarray(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))
          deviceIdIdentifies the device (for example, virtual server) using this portStringNonull5e3898d7-11be-483e-9732-b2f5eccd2b2e
          deviceOwnerIdentifies the entity (for example, DHCP agent) using this portStringNonullnetwork:router_interface
          securityGroupsSpecifies the IDs of any security groups associated with this portIndexed array of stringsNoEmpty arrayarray('f0ac4394-7e4a-4409-9701-ba8be283dbc3')
          tenantIdOwner of the port. Only admin users can specify a tenant ID other than their own.StringNoSame as the tenant creating the port123456
          +

          You can create a port as shown in the following example:

          +
          $port = $networkingService->createPort(array(
          +    'name' => 'My port',
          +    'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c'
          +));
          +/** @var $port OpenCloud\Networking\Resource\Port **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Create multiple ports

          +

          This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in the +preceding table.

          +

          You can create multiple ports as shown in the following example:

          +
          $ports = $networkingService->createPorts(array(
          +    array(
          +        'name' => 'My port #1',
          +        'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c'
          +    ),
          +    array(
          +        'name' => 'My port #2',
          +        'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c'
          +    )
          +));
          +
          +foreach ($ports as $port) {
          +    /** @var $port OpenCloud\Networking\Resource\Port **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          List ports

          +

          You can list all the ports to which you have access as shown in the +following example:

          +
          $ports = $networkingService->listPorts();
          +foreach ($ports as $port) {
          +    /** @var $port OpenCloud\Networking\Resource\Port **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get a port

          +

          You can retrieve a specific port by using that port’s ID, as shown in +the following example:

          +
          $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66');
          +/** @var $port OpenCloud\Networking\Resource\Port **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Update a port

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the port. This name might not be unique.StringNonullMy port
          adminStateUpThe administrative state of port. If false (down), the port does not forward packets.BooleanNotruetrue
          fixedIpsIP addresses for this portIndexed array of associative arraysNoAutomatically allocated from the poolarray(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))
          deviceIdIdentifies the device (for example, virtual server) using this portStringNonull5e3898d7-11be-483e-9732-b2f5eccd2b2e
          deviceOwnerIdentifies the entity (for example, DHCP agent) using this portStringNonullnetwork:router_interface
          securityGroupsSpecifies the IDs of any security groups associated with this portIndexed array of stringsNoEmpty arrayarray('f0ac4394-7e4a-4409-9701-ba8be283dbc3')
          +

          You can update a port as shown in the following example:

          +
          $port->update(array(
          +    'fixedIps' => array(
          +        array(
          +            'subnetId'  => '75906d20-6625-11e4-9803-0800200c9a66',
          +            'ipAddress' => '192.168.199.59'
          +        )
          +    )
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Delete a port

          +

          You can delete a port as shown in the following example:

          +
          $port->delete();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/index.html b/doc/_build/html/services/networking/index.html new file mode 100644 index 000000000..2f6c08518 --- /dev/null +++ b/doc/_build/html/services/networking/index.html @@ -0,0 +1,422 @@ + + + + + + + + + + Networking v2 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Networking v2

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Networking service

          +

          Now to instantiate the Networking service:

          +
          $service = $client->networkingService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          network
          +
          A network is an isolated virtual layer-2 broadcast domain that is typically +reserved for the tenant who created it unless you configure the network to +be shared. The network is the main entity in the Networking service. Ports +and subnets are always associated with a network.
          +
          subnet
          +
          A subnet represents an IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the Compute +service). Each subnet must have a CIDR and must be associated with a network.
          +
          port
          +
          A port represents a virtual switch port on a logical network switch. +Virtual instances (such as servers created using the Compute service) +attach their interfaces into ports. The port also defines the MAC address +and the IP address(es) to be assigned to the interfaces plugged into them. +When IP addresses are associated to a port, this also implies the port is +associated with a subet, as the IP address is taken from the allocation +pool for a specific subnet.
          +
          security group
          +
          A security group is a named container for security group rules.
          +
          security group rule
          +
          A security group rule provides users the ability to specify the types of +traffic that are allowed to pass through to and from ports on a virtual +server instance.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/networks.html b/doc/_build/html/services/networking/networks.html new file mode 100644 index 000000000..cd173a910 --- /dev/null +++ b/doc/_build/html/services/networking/networks.html @@ -0,0 +1,465 @@ + + + + + + + + + + Networks — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Networks

          +
          +

          Create a network

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the network. This name might not be unique.StringNonullMy private backend network
          adminStateUpThe administrative state of network. If false (down), the network does not forward packets.BooleanNotruetrue
          sharedSpecifies whether the network resource can be accessed by any tenant.BooleanNofalsefalse
          tenantIdOwner of network. Only admin users can specify a tenant ID other than their own.StringNoSame as tenant creating the network123456
          +

          You can create a network as shown in the following example:

          +
          /** @var $network OpenCloud\Networking\Resource\Network **/
          +$network = $networkingService->createNetwork(array(
          +    'name' => 'My private backend network'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Create multiple networks

          +

          This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in the +preceding table.

          +

          You can create multiple networks as shown in the following example:

          +
          $networks = $networkingService->createNetworks(array(
          +    array(
          +        'name' => 'My private backend network #1'
          +    ),
          +    array(
          +        'name' => 'My private backend network #2'
          +    )
          +));
          +
          +foreach ($networks as $network) {
          +    /** @var $network OpenCloud\Networking\Resource\Network **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List networks

          +

          You can list all the networks to which you have access as shown in the +following example:

          +
          $networks = $networkingService->listNetworks();
          +
          +foreach ($networks as $network) {
          +    /** @var $network OpenCloud\Networking\Resource\Network **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get a network

          +

          You can retrieve a specific network by using that network’s ID, as shown +in the following example:

          +
          /** @var $network OpenCloud\Networking\Resource\Network **/
          +$network = $networkingService->getNetwork('{networkId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update a network

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the network. This name might not be unique.StringNonullMy updated private backend network
          adminStateUpThe administrative state of network. If false (down), the network does not forward packets.BooleanNotruetrue
          sharedSpecifies whether the network resource can be accessed by any tenant.BooleanNofalsefalse
          +

          You can update a network as shown in the following example:

          +
          $network->update(array(
          +    'name' => 'My updated private backend network'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +

          Delete a network

          +

          You can delete a network as shown in the following example:

          +
          $network->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/ports.html b/doc/_build/html/services/networking/ports.html new file mode 100644 index 000000000..f80f748a5 --- /dev/null +++ b/doc/_build/html/services/networking/ports.html @@ -0,0 +1,529 @@ + + + + + + + + + + Ports — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Ports

          +
          +

          Create a port

          +

          This operation takes one parameter, an associative array, with the following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          networkIdNetwork this port is associated withStringYes
            +
          • +
          +
          eb60583c-57ea-41b9-8d5c-8fab2d22224c
          nameA human-readable name for the port. This name might not be unique.StringNonullMy port
          adminStateUpThe administrative state of port. If false (down), the port does not forward packets.BooleanNotruetrue
          macAddressMAC address to use on this portString (MAC address in 6-octet form separated by colons)NoGenerated0F:5A:6F:70:E9:5C
          fixedIpsIP addresses for this portIndexed array of associative arraysNoAutomatically allocated from the poolarray(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))
          deviceIdIdentifies the device (for example, virtual server) using this portStringNonull5e3898d7-11be-483e-9732-b2f5eccd2b2e
          deviceOwnerIdentifies the entity (for example, DHCP agent) using this portStringNonullnetwork:router_interface
          securityGroupsSpecifies the IDs of any security groups associated with this portIndexed array of stringsNoEmpty arrayarray('f0ac4394-7e4a-4409-9701-ba8be283dbc3')
          tenantIdOwner of the port. Only admin users can specify a tenant ID other than their own.StringNoSame as the tenant creating the port123456
          +

          You can create a port as shown in the following example:

          +
          /** @var $port OpenCloud\Networking\Resource\Port **/
          +$port = $networkingService->createPort(array(
          +    'name' => 'My port',
          +    'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Create multiple ports

          +

          This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in the +preceding table.

          +

          You can create multiple ports as shown in the following example:

          +
          $ports = $networkingService->createPorts(array(
          +    array(
          +        'name' => 'My port #1',
          +        'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c'
          +    ),
          +    array(
          +        'name' => 'My port #2',
          +        'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c'
          +    )
          +));
          +
          +foreach ($ports as $port) {
          +    /** @var $port OpenCloud\Networking\Resource\Port **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List ports

          +

          You can list all the ports to which you have access as shown in the following example:

          +
          $ports = $networkingService->listPorts();
          +
          +foreach ($ports as $port) {
          +    /** @var $port OpenCloud\Networking\Resource\Port **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get a port

          +

          You can retrieve a specific port by using that port’s ID, as shown in +the following example:

          +
          /** @var $port OpenCloud\Networking\Resource\Port **/
          +$port = $networkingService->getPort('{portId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update a port

          +

          This operation takes one parameter, an associative array, with the following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the port. This name might not be unique.StringNonullMy port
          adminStateUpThe administrative state of port. If false (down), the port does not forward packets.BooleanNotruetrue
          fixedIpsIP addresses for this portIndexed array of associative arraysNoAutomatically allocated from the poolarray(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))
          deviceIdIdentifies the device (for example, virtual server) using this portStringNonull5e3898d7-11be-483e-9732-b2f5eccd2b2e
          deviceOwnerIdentifies the entity (for example, DHCP agent) using this portStringNonullnetwork:router_interface
          securityGroupsSpecifies the IDs of any security groups associated with this portIndexed array of stringsNoEmpty arrayarray('f0ac4394-7e4a-4409-9701-ba8be283dbc3')
          +

          You can update a port as shown in the following example:

          +
          $port->update(array(
          +    'fixedIps' => array(
          +        array(
          +            'subnetId'  => '75906d20-6625-11e4-9803-0800200c9a66',
          +            'ipAddress' => '192.168.199.59'
          +        )
          +    )
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Delete a port

          +

          You can delete a port as shown in the following example:

          +
          $port->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/security-group-rules.html b/doc/_build/html/services/networking/security-group-rules.html new file mode 100644 index 000000000..3b6436115 --- /dev/null +++ b/doc/_build/html/services/networking/security-group-rules.html @@ -0,0 +1,410 @@ + + + + + + + + + + Security Group Rules — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Security Group Rules

          +
          +

          Create a security group rule

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          securityGroupIdThe security group ID to associate with this security group rule.StringYes
            +
          • +
          +
          2076db17-a522-4506-91de-c6dd8e837028
          directionThe direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance.String (ingress or egress)Yes
            +
          • +
          +
          ingress
          ethertypeMust be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules.String (IPv4 or IPv6)NoIPv4IPv6
          portRangeMinThe minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the portRangeMax attribute. If the protocol is ICMP, this value must be an ICMP type.IntegerNonull80
          portRangeMaxThe maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type.IntegerNonull80
          protocolThe protocol that is matched by the security group rule.String (tcp, udp, icmp)Nonulltcp
          remoteGroupIdThe remote group ID to be associated with this security group rule. You can specify either remoteGroupId or remoteGroupPrefix.StringOptionalnull85cc3048-abc3-43cc-89b3-377341426ac5
          remoteIpPrefixThe remote IP prefix to be associated with this security group rule. You can specify either remoteGroupId or remoteGroupPrefix.StringOptionalnull192.168.5.0
          +

          You can create a security group rule as shown in the following example:

          +
          /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/
          +$securityGroupRule = $networkingService->createSecurityGroupRule(array(
          +    'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028',
          +    'direction'       => 'egress',
          +    'ethertype'       => 'IPv4',
          +    'portRangeMin'    => 80,
          +    'portRangeMax'    => 80,
          +    'protocol'        => 'tcp',
          +    'remoteGroupId'   => '85cc3048-abc3-43cc-89b3-377341426ac5'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List security group rules

          +

          You can list all the security group rules to which you have access as +shown in the following example:

          +
          $securityGroupRules = $networkingService->listSecurityGroupRules();
          +foreach ($securityGroupRules as $securityGroupRule) {
          +    /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/security-groups.html b/doc/_build/html/services/networking/security-groups.html new file mode 100644 index 000000000..97e294709 --- /dev/null +++ b/doc/_build/html/services/networking/security-groups.html @@ -0,0 +1,378 @@ + + + + + + + + + + Security Groups — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Security Groups

          +
          +

          Create a security group

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the security group. This name might not be unique.StringYes
            +
          • +
          +
          new-webservers
          descriptionDescription of the security group.StringNonullsecurity group for webservers
          +

          You can create a security group as shown in the following example:

          +
          /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/
          +$securityGroup = $networkingService->createSecurityGroup(array(
          +    'name' => 'new-webservers',
          +    'description' => 'security group for webservers'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List security groups

          +

          You can list all the security groups to which you have access as shown +in the following example:

          +
          $securityGroups = $networkingService->listSecurityGroups();
          +foreach ($securityGroups as $securityGroup) {
          +    /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get a security group

          +

          You can retrieve a specific security group by using that security +group’s ID, as shown in the following example:

          +
          /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/
          +$securityGroup = $networkingService->getSecurityGroup('{secGroupId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Delete a security group

          +

          You can delete a security group as shown in the following example:

          +
          $securityGroup->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/networking/subnets.html b/doc/_build/html/services/networking/subnets.html new file mode 100644 index 000000000..980c048ee --- /dev/null +++ b/doc/_build/html/services/networking/subnets.html @@ -0,0 +1,538 @@ + + + + + + + + + + Subnets — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Subnets

          +
          +

          Create a subnet

          +

          This operation takes one parameter, an associative array, with the following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          networkIdNetwork this subnet is associated withStringYes
            +
          • +
          +
          eb60583c-57ea-41b9-8d5c-8fab2d22224c
          ipVersionIP versionInteger (4 or 6)Yes
            +
          • +
          +
          4
          cidrCIDR representing the IP address range for this subnetString (CIDR)Yes
            +
          • +
          +
          192.168.199.0/25
          nameA human-readable name for the subnet. This name might not be unique.StringNonullMy subnet
          gatewayIpIP address of the default gateway used by devices on this subnetString (IP address)NoFirst IP address in CIDR192.168.199.128
          dnsNameserversDNS nameservers used by hosts in this subnetIndexed array of stringsNoEmpty arrayarray('4.4.4.4', '8.8.8.8')
          allocationPoolsSubranges of the CIDR available for dynamic allocation to portsIndexed array of associative arraysNoEvery IP address in CIDR, excluding gateway IP address if configuredarray(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))
          hostRoutesRoutes that should be used by devices with IP addresses from this subnet (not including the local subnet route)Indexed array of associative arraysNoEmpty arrayarray(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))
          enableDhcpSpecifies whether DHCP is enabled for this subnetBooleanNotruefalse
          tenantIdOwner of the subnet. Only admin users can specify a tenant ID other than their own.StringNoSame as tenant creating the subnet123456
          +

          You can create a subnet as shown in the following example:

          +
          /** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +$subnet = $networkingService->createSubnet(array(
          +    'name' => 'My subnet',
          +    'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c',
          +    'ipVersion' => 4,
          +    'cidr' => '192.168.199.0/25'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Create multiple subnets

          +

          This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in the +preceding table.

          +

          You can create multiple subnets as shown in the following example:

          +
          $subnets = $networkingService->createSubnets(array(
          +    array(
          +        'name' => 'My subnet #1'
          +    ),
          +    array(
          +        'name' => 'My subnet #2'
          +    )
          +));
          +
          +foreach ($subnets as $subnet) {
          +    /** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List subnets

          +

          You can list all the subnets to which you have access as shown in the +following example:

          +
          $subnets = $networkingService->listSubnets();
          +foreach ($subnets as $subnet) {
          +    /** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get a subnet

          +

          You can retrieve a specific subnet by using that subnet’s ID, as shown +in the following example:

          +
          /** @var $subnet OpenCloud\Networking\Resource\Subnet **/
          +$subnet = $networkingService->getSubnet('{subnetId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update a subnet

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameA human-readable name for the subnet. This name might not be unique.StringNonullMy updated subnet
          gatewayIpIP address of the default gateway used by devices on this subnetString (IP address)NoFirst IP address in CIDR192.168.62.155
          dnsNameserversDNS nameservers used by hosts in this subnetIndexed array of stringsNoEmpty arrayarray('4.4.4.4', '8.8.8.8')
          hostRoutesRoutes that should be used by devices with IP adresses from this subnet (not including the local subnet route)Indexed array of associative arraysNoEmpty arrayarray(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))
          enableDhcpSpecifies whether DHCP is enabled for this subnetBooleanNotruefalse
          +

          You can update a subnet as shown in the following example:

          +
          $subnet->update(array(
          +    'name' => 'My updated subnet',
          +    'hostRoutes' => array(
          +        array(
          +            'destination' => '1.1.1.0/24',
          +            'nexthop'     => '192.168.17.19'
          +        )
          +    ),
          +    'gatewayIp' => '192.168.62.155'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Delete a subnet

          +

          You can delete a subnet as shown in the following example:

          +
          $subnet->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Access.md.html b/doc/_build/html/services/object-store/Access.md.html new file mode 100644 index 000000000..11ddcb6d3 --- /dev/null +++ b/doc/_build/html/services/object-store/Access.md.html @@ -0,0 +1,246 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(RACKSPACE_US, array(
          +
          +));
          +
          +$service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want
          +
          +
          +
          +
          +

          Temporary URLs

          +

          Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they’re CDN-enabled.

          +
          +

          Set “temporary URL” metadata key

          +

          You must set this “secret” value on your account, where it can be used +in a global state:

          +
          $account = $service->getAccount();
          +$account->setTempUrlSecret('my_secret');
          +
          +echo $account->getTempUrlSecret();
          +
          +
          +

          The string argument of setTempUrlSecret() is optional - if left out, +the SDK will generate a random hashed secret for you.

          +
          +
          +

          Create a temporary URL

          +

          Once you’ve set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute:

          +
          $object->getTemporaryUrl(60, 'GET');
          +
          +
          +

          To allow PUT access for 1 hour:

          +
          $object->getTemporaryUrl(360, 'PUT');
          +
          +
          +
          +
          +
          +

          Hosting websites on CloudFiles

          +

          To host a static (i.e. HTML) website on CloudFiles, you must follow +these steps:

          +
            +
          1. CDN-enable a container
          2. +
          3. Upload all HTML content. You can use nested directory structures.
          4. +
          5. Tell CloudFiles what to use for your default index page like this:
          6. +
          +
          $container->setStaticIndexPage('index.html');
          +
          +
          +
            +
          1. (Optional) Tell CloudFiles which error page to use by default:
          2. +
          +
          $container->setStaticErrorPage('error.html');
          +
          +
          +

          Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object.

          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Account.md.html b/doc/_build/html/services/object-store/Account.md.html new file mode 100644 index 000000000..3e2c8a35b --- /dev/null +++ b/doc/_build/html/services/object-store/Account.md.html @@ -0,0 +1,211 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(RACKSPACE_US, array(
          +
          +));
          +
          +$service = $client->objectStoreService('cloudFiles');
          +
          +
          +
          +
          +

          View Account Details

          +

          To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used):

          +
          $account = $service->getAccount();
          +
          +// Either return the full Metadata object
          +$details = $account->getDetails();
          +
          +// or individual values
          +$account->getContainerCount();
          +$account->getObjectCount();
          +$account->getBytesUsed();
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Container.md.cdn.html b/doc/_build/html/services/object-store/Container.md.cdn.html new file mode 100644 index 000000000..ea070d286 --- /dev/null +++ b/doc/_build/html/services/object-store/Container.md.cdn.html @@ -0,0 +1,257 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(RACKSPACE_US, array(
          +
          +));
          +
          +$service = $client->objectStoreService('cloudFiles');
          +
          +
          +

          To access the CDN functionality of a particular container:

          +
          $container = $service->getContainer('foo_bar');
          +
          +$cdn = $container->getCdn();
          +
          +
          +
          +
          +

          List CDN-enabled container

          +

          To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on:

          +
          $cdnService = $service->getCdnService();
          +$cdnContainers = $cdnService->listContainers();
          +
          +foreach ($cdnContainers as $cdnContainer) {
          +
          +}
          +
          +
          +
          +
          +

          CDN-enable and -disable a container

          +

          Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container’s CDN URL with the object name.

          +

          Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period.

          +
          $container->enableCdn();
          +$container->disableCdn();
          +
          +
          +
          +
          +

          Serving containers through SSL

          +
          $cdn->getCdnSslUri();
          +
          +
          +
          +
          +

          Streaming CDN-enabled containers

          +
          $cdn->getCdnStreamingUri();
          +
          +
          +
          +
          +

          iOS streaming

          +

          The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices.

          +
          $cdn->getIosStreamingUri();
          +
          +
          +
          +
          +

          CDN logging

          +

          To enable and disable logging for your CDN:

          +
          $cdn->enableCdnLogging();
          +$cdn->disableCdnLogging();
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Container.md.storage.html b/doc/_build/html/services/object-store/Container.md.storage.html new file mode 100644 index 000000000..34b83dc68 --- /dev/null +++ b/doc/_build/html/services/object-store/Container.md.storage.html @@ -0,0 +1,393 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +
          use OpenCloud\Rackspace;
          +
          +// Create a client object to communicate with various Rackspace Cloud services.
          +$client = new Rackspace(RACKSPACE_US, array(
          +    'username' => 'Replace this with your Rackspace Cloud user name',
          +    'apiKey' => 'Replace this with your Rackspace Cloud API key'
          +));
          +
          +// Create a service object to use the object store service. The sample code
          +// creates the object store in the 'DFW' region.
          +$service = $client->objectStoreService('cloudFiles', 'DFW');
          +
          +
          +
          +
          +

          Create container

          +

          To create a new container, you just need to define its name:

          +
          $container = $service->createContainer('my_amazing_container');
          +
          +
          +

          If the response returned is FALSE, there was an API error - most +likely due to the fact you have a naming collision.

          +

          Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted.

          +
          +
          Note: when working with names that contain non-standard +alphanumerical characters (such as spaces or non-English +characters), you must ensure they are encoded with +`urlencode <http://php.net/urlencode>`__ before passing them in
          +
          +
          +

          List containers

          +
          +

          Return a list of containers

          +
          $containerList = $service->listContainers();
          +
          +while ($container = $containerList->next()) {
          +    // Do stuff; some examples below
          +    printf("Container name: %s\n", $container->name);
          +    printf("Number of objects within container: %d\n", $container->getObjectCount());
          +}
          +
          +
          +

          Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite’s +memcmp() function, regardless of text encoding.

          +

          The list is limited to 10,000 containers at a time. See 1.3 for ways to +limit and navigate this list.

          +
          +
          +

          Return a formatted list of containers

          +

          Currently, the SDK only supports JSON-formatted responses.

          +
          +
          +

          Controlling a large list of containers

          +

          You may limit and control this list of results by using the marker +and end_marker parameters. The former parameter (marker) tells +the API where to begin the list, and the latter (end_marker) tells +it where to end the list. You may use either of them independently or +together. You may also use the limit parameter to fix the number of +containers returned.

          +

          To list a set of containers between two fixed points:

          +
          $someContainers = $service->listContainers(array(
          +    'marker'     => 'container_55',
          +    'end_marker' => 'container_2001'
          +));
          +
          +
          +

          Or to return a limited set:

          +
          $someContainers = $service->listContainers(array('limit' => 560));
          +
          +
          +
          +
          +
          +

          Get container

          +

          To retrieve a certain container, either to access its object or +metadata:

          +
          $container = $service->getContainer('container_name');
          +
          +echo $container->getObjectCount();
          +echo $container->getBytesUsed();
          +
          +
          +
          +
          +

          Delete container

          +

          Deleting a container is easy:

          +
          $container->delete();
          +
          +
          +

          Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +$deleteObjects parameter to TRUE like so:

          +
          $container->delete(TRUE);
          +
          +
          +

          You can also do it manually:

          +
          $container->deleteAllObjects();
          +$container->delete();
          +
          +
          +
          +
          +

          Create or update container metadata

          +
          $container->saveMetadata(array(
          +    'Author' => 'Virginia Woolf',
          +    'Published' => '1931'
          +));
          +
          +
          +

          Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To append values +to the current metadata:

          +
          $metadata = $container->appendToMetadata(array(
          +    'Publisher' => 'Hogarth'
          +));
          +
          +
          +

          If you only want to set the metadata to the local object, and not +immediately retain these values on the API, you can use a standard +setter method - which can contribute to eventual actions like an update:

          +
          $container->setMetadata(array('Foo' => 'Bar'));
          +
          +
          +
          +
          +

          Container quotas

          +

          The container_quotas middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check.

          +

          To set quotas for a container:

          +
          use OpenCloud\Common\Constants\Size;
          +
          +$container->setCountQuota(1000);
          +$container->setBytesQuota(2.5 * Size::GB);
          +
          +
          +

          And to retrieve them:

          +
          echo $container->getCountQuota();
          +echo $container->getBytesQuota();
          +
          +
          +
          +
          +

          Access log delivery

          +

          To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage).

          +
          $container->enableLogging();
          +$container->disableLogging();
          +
          +
          +
          +
          +

          Syncing containers

          +

          You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory:

          +
          $container->uploadDirectory('/home/Jamie/blog');
          +
          +
          +

          There are four scenarios you should be aware of:

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          LocalRemoteComparisonAction
          File existsFile existsIdentical checksumNo action
          File existsFile existsDifferent checksumLocal file overwrites remote
          File existsFile does not exist
            +
          • +
          +
          Local file created in Swift
          Files does not existFile exists
            +
          • +
          +
          Remote file deleted
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Migrating.md.storage.html b/doc/_build/html/services/object-store/Migrating.md.storage.html new file mode 100644 index 000000000..c831f7294 --- /dev/null +++ b/doc/_build/html/services/object-store/Migrating.md.storage.html @@ -0,0 +1,289 @@ + + + + + + + + + + Migrating containers (across regions) — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Migrating containers (across regions)

          +
          +

          Introduction

          +

          Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a COPY +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality.

          +

          You will be charged for bandwidth between regions, so it’s advisable +to use ServiceNet where possible (which is free).

          +
          +
          +

          Requirements

          +
            +
          • You must install the full Guzzle package, so that the process can +take advantage of Guzzle’s batching functionality (it allows parallel +requests to be batched for greater efficiency). You can do this by +running:
          • +
          +
          php composer.phar install --dev
          +
          +
          +
            +
          • Depending on the size and number of transfer items, you will need to +raise PHP’s memory limit:
          • +
          +
          ini_set('memory_limit', '512M');
          +
          +
          +
            +
          • You will need to enact some kind of backoff/retry strategy for rate +limits. Guzzle comes with a convenient feature that just needs to be +added as a normal subscriber:
          • +
          +
          use Guzzle\Plugin\Backoff\BackoffPlugin;
          +
          +$client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408)));
          +
          +
          +

          This tells the client to retry up to 10 times for failed requests +have resulted in these HTTP status codes: 500, 503 or 408.

          +
          +
          +

          Setup

          +

          You can access all this functionality by executing:

          +
          $ordService = $client->objectStoreService('cloudFiles', 'ORD');
          +$iadService = $client->objectStoreService('cloudFiles', 'IAD');
          +
          +$oldContainer = $ordService->getContainer('old_container');
          +$newContainer = $iadService->getContainer('new_container');
          +
          +$iadService->migrateContainer($oldContainer, $newContainer);
          +
          +
          +

          It’s advisable to do this process in a Cloud Server in one of the two +regions you’re migrating to/from. This allows you to use privateURL +as the third argument in the objectStoreService methods like this:

          +
          $client->objectStoreService('cloudFiles', 'IAD', 'privateURL');
          +
          +
          +

          This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free.

          +
          +
          +

          Options

          +

          You can pass in an array of arguments to the method:

          +
          $options = array(
          +    'read.batchLimit'  => 100,
          +    'read.pageLimit'   => 100,
          +    'write.batchLimit' => 50
          +);
          +
          +$iadService->migrateContainer($oldContainer, $newContainer, $options);
          +
          +
          +
          +

          Options explained

          + +++++ + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionDefault
          read.pageLimitWhen the process begins, it has to collect all the files that exist in the old container. It does this through a conventional objectList method, which calls the PaginatedIterator. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API)10,000
          read.batchLimitAfter the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together.1,000
          write.batchLimitOnce each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together.100
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Object.md.cdn.html b/doc/_build/html/services/object-store/Object.md.cdn.html new file mode 100644 index 000000000..8e408b951 --- /dev/null +++ b/doc/_build/html/services/object-store/Object.md.cdn.html @@ -0,0 +1,201 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +

          You will need to instantiate the container object and access its CDN +functionality as documented +here.

          +
          +
          +

          Purge CDN-enabled objects

          +

          To remove a CDN object from public access:

          +
          $object->purge();
          +
          +
          +

          You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged:

          +
          $object->purge('jamie.hannaford@rackspace.com');
          +$object->purge('hello@example.com,hallo@example.com');
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/Object.md.storage.html b/doc/_build/html/services/object-store/Object.md.storage.html new file mode 100644 index 000000000..0dd100f28 --- /dev/null +++ b/doc/_build/html/services/object-store/Object.md.storage.html @@ -0,0 +1,480 @@ + + + + + + + + + + Setup — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Setup

          +

          Conceptually, a container contains objects (also known as files). In +order to work with objects, you will need to instantiate a container +object first as documented +here.

          +
          +
          +

          Note on object properties

          +

          Please be aware that you cannot directly access the properties of +DataObject anymore, you must use appropriate getter/ setter methods:

          + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          PropertyMethod
          Parent containergetContainer
          NamegetName
          Body of filegetContent
          Size of filegetContentLength
          Type of filegetContentType
          ETag checksumgetEtag
          Last modified dategetLastModified
          +
          +
          +

          Create an object

          +

          There are three ways to upload a new file, each of which has different +business needs.

          +
          +

          Note: Unlike previous versions, you do not need to manually +specify your object’s content type. The API will do this for you.

          +

          Note: when working with names that contain non-standard +alphanumerical characters (such as spaces or non-English +characters), you must ensure they are encoded with +`urlencode <http://php.net/urlencode>`__ before passing them in

          +
          +
          +

          To upload a single/basic file:

          +
          use OpenCloud\ObjectStore\Resource\DataObject;
          +
          +$data = fopen('/path/to/sample.mp3', 'r+');
          +
          +// alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource)
          +
          +$meta = array(
          +    'Author' => 'Camera Obscura',
          +    'Origin' => 'Glasgow'
          +);
          +
          +$metaHeaders = DataObject::stockHeaders($meta);
          +$customHeaders = array();
          +$allHeaders = $metaHeaders + $customHeaders;
          +
          +$container->uploadObject('sample.mp3', $data, $allHeaders);
          +
          +
          +
          +
          +

          To upload multiple small-to-mid sized files:

          +
          $files = array(
          +    array(
          +        'name' => 'apache.log',
          +        'path' => '/etc/httpd/logs/error_log'
          +    ),
          +    array(
          +        'name' => 'mysql.log',
          +        'body' => fopen('/tmp/mysql.log', 'r+')
          +    ),
          +    array(
          +        'name' => 'to_do_list.txt',
          +        'body' => 'PHONE HOME'
          +    )
          +);
          +
          +$container->uploadObjects($files);
          +
          +
          +

          As you can see, the name key is required for every file. You must +also specify either a path key (to an existing file), or a body. +The body can either be a PHP resource or a string representation of +the content you want to upload.

          +
          +
          +

          To upload large files

          +

          For files over 5GB, you will need to use the +OpenCloud\ObjectStore\Upload\TransferBuilder factory to build your +transfer, upon which you can execute your upload functionality. For your +convenience, the Container resource object contains a simple method to +do this heavy lifting for you:

          +
          $transfer = $container->setupObjectTransfer(array(
          +    'name' => 'video.mov',
          +    'path' => '/home/jamie/video.mov',
          +    'metadata' => array(
          +        'Author' => 'Jamie'
          +    ),
          +    'concurrency' => 4,
          +    'partSize'    => 1.5 * Size::GB
          +));
          +
          +$transfer->upload();
          +
          +
          +

          You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it’s a segment rather than the full file). After all parts +are uploaded, a manifest is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download.

          +
          +
          +
          +

          List objects in a container

          +

          To return a list of objects:

          +
          $files = $container->objectList();
          +
          +foreach ($files as $file) {
          +    // ... do something
          +}
          +
          +
          +

          By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the official +docs.

          +
          $container->objectList(array('prefix' => 'logFile_'));
          +
          +
          +
          +
          +

          Get object

          +

          To retrieve a specific file from Cloud Files:

          +
          $file = $container->getObject('summer_vacation.mp4');
          +
          +
          +
          +

          Conditional requests

          +

          You can also perform conditional requests according to RFC 2616 +specification (§§ 14.24-26). +Supported headers are If-Match, If-None-Match, +If-Modified-Since and If-Unmodified-Since.

          +

          So, to retrieve a file’s contents only if it’s been recently changed

          +
          $file = $container->getObject('error_log.txt', array(
          +    'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT'
          +));
          +
          +if ($file->getContentLength()) {
          +    echo 'Has been changed since the above date';
          +} else {
          +    echo 'Has not been changed';
          +}
          +
          +
          +

          Retrieve a file only if it has NOT been modified (and expect a 412 on +failure):

          +
          use Guzzle\Http\Exception\ClientErrorResponseException;
          +
          +try {
          +    $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array(
          +        'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT'
          +    ));
          +} catch (ClientErrorResponseException $e) {
          +    echo 'This file has been modified...';
          +}
          +
          +
          +

          Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file:

          +
          $snippet = $container->getObject('output.log', array('range' => 'bytes=-20'));
          +
          +
          +
          +
          +
          +

          Update an existing object

          +

          Updating content is easy:

          +
          $file->setContent(fopen('/path/to/new/content', 'r+'));
          +$file->update();
          +
          +
          +

          Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file.

          +
          +
          +

          Copy object

          +

          To copy a file to another location, you need to specify a string-based +destination path:

          +
          $object->copy('/container_2/new_object_name');
          +
          +
          +
          +
          +

          Delete object

          +
          $object->delete();
          +
          +
          +
          +
          +

          Get object metadata

          +

          You can fetch just the object metadata without fetching the full +content:

          +
          $container->getPartialObject('summer_vacation.mp4');
          +
          +
          +

          In order to access the metadata on a partial or complete object, use:

          +
          $object->getMetadata();
          +
          +
          +

          You can turn a partial object into a full object to get the content +after looking at the metadata:

          +
          $object->refresh();
          +
          +
          +

          You can also update to get the latest metadata:

          +
          $object->retrieveMetadata();
          +
          +
          +
          +
          +

          Update object metadata

          +

          Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you’ll be executing one soon (an update operation for +example):

          +
          // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation
          +$object->setMetadata(array(
          +    'Author' => 'Hemingway'
          +));
          +
          +// ... code here
          +
          +$object->update();
          +
          +
          +

          Alternatively, you can update the API straight away - so that everything +is retained:

          +
          $object->saveMetadata(array(
          +    'Author' => 'Hemingway'
          +));
          +
          +
          +

          Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method:

          +
          $metadata = $object->appendToMetadata(array(
          +  'Author' => 'Hemingway'
          +));
          +
          +$object->saveMetadata($metadata);
          +
          +
          +
          +
          +

          Extract archive

          +

          CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container:

          +
          use OpenCloud\ObjectStore\Constants\UrlType;
          +
          +$service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ);
          +
          +
          +

          You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive.

          +
          +
          +

          Bulk delete

          +

          Bulk delete a set of paths:

          +
          $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log');
          +
          +$service->bulkDelete($pathsToBeDeleted);
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/README.md.html b/doc/_build/html/services/object-store/README.md.html new file mode 100644 index 000000000..c43489305 --- /dev/null +++ b/doc/_build/html/services/object-store/README.md.html @@ -0,0 +1,259 @@ + + + + + + + + + + Object Store — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Object Store

          +

          Object Store is an object-based storage system that stores content +and metadata as objects in a cloud.

          +

          Specifically, a cloud is made up of one or more regions. Each region can +have several containers, created by a user. Each container can +container several objects (sometimes referred to as files), uploaded +by the user.

          +
          +

          Getting started

          +
          +

          1. Instantiate an OpenStack or Rackspace client.

          +

          Choose one of the following two options:

          +
            +
          • If you are working with a vanilla OpenStack cloud, instantiate an +OpenCloud\OpenStack client as shown below.

            +
            use OpenCloud\OpenStack;
            +
            + $client = new OpenStack('<OPENSTACK CLOUD IDENTITY ENDPOINT URL>', array(
            +     'username' => '<YOUR OPENSTACK USERNAME>',
            +     'password' => '<YOUR OPENSTACK PASSWORD>'
            + ));
            +
            +
            +
          • +
          • If you are working with the Rackspace cloud, instantiate a +OpenCloud\Rackspace client as shown below.

            +
            use OpenCloud\Rackspace;
            +
            +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            + ));
            +
            +
            +
          • +
          +
          +
          +

          2. Obtain an Object Store service object from the client.

          +
          $region = 'DFW';
          +$objectStoreService = $client->objectStoreService(null, $region);
          +
          +
          +

          In the example above, you are connecting to the DFW region of the +cloud. Any containers and objects created with this +$objectStoreService instance will be stored in that cloud region.

          +
          +
          +

          3. Create a container for your objects (also referred to as files).

          +
          $container = $objectStoreService->createContainer('logos');
          +
          +**Note:** when working with names that contain non-standard
          +alphanumerical characters (such as spaces or non-English
          +characters), you must ensure they are encoded with
          +```urlencode`` <http://php.net/urlencode>`__ before passing them in
          +
          +
          +
          +
          +

          4. Upload an object to the container.

          +
          $localFileName  = '/path/to/local/php-elephant.jpg';
          +$remoteFileName = 'php-elephant.jpg';
          +
          +$fileData = fopen($localFileName, 'r');
          +$container->uploadObject($remoteFileName, $fileData);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Next steps

          +

          There is a lot more you can do with containers and objects. See the +complete user guide to the Object Store service.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/USERGUIDE.md.html b/doc/_build/html/services/object-store/USERGUIDE.md.html new file mode 100644 index 000000000..93b698869 --- /dev/null +++ b/doc/_build/html/services/object-store/USERGUIDE.md.html @@ -0,0 +1,1014 @@ + + + + + + + + + + The Complete User Guide to the Object Store Service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          The Complete User Guide to the Object Store Service

          +

          Object Store is an object-based storage system that stores content +and metadata as objects in a cloud.

          +
          +

          Prerequisites

          +
          +

          Client

          +

          To use the object store service, you must first instantiate a +OpenStack or Rackspace client object.

          +
            +
          • If you are working with a vanilla OpenStack cloud, instantiate an +OpenCloud\OpenStack client as shown below.

            +
            use OpenCloud\OpenStack;
            +
            +$client = new OpenStack('<OPENSTACK CLOUD IDENTITY ENDPOINT URL>', array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            +));
            +
            +
            +
          • +
          • If you are working with the Rackspace cloud, instantiate a +OpenCloud\Rackspace client as shown below.

            +
            use OpenCloud\Rackspace;
            +
            +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            +));
            +
            +
            +
          • +
          +
          +
          +

          Object Store Service

          +

          All operations on the object store are done via an object store service +object.

          +
          $region = 'DFW';
          +$objectStoreService = $client->objectStoreService(null, $region);
          +
          +
          +

          In the example above, you are connecting to the DFW region of the +cloud. Any containers and objects created with this +$objectStoreService instance will be stored in that cloud region.

          +
          +
          +
          +

          Containers

          +

          A container defines a namespace for objects. An object with the +same name in two different containers represents two different objects.

          +

          For example, you may create a container called logos to hold all the +image files for your blog.

          +

          A container may contain zero or more objects in it.

          +
          +
          Note: when working with names that contain non-standard +alphanumerical characters (such as spaces or non-English +characters), you must ensure they are encoded with +`urlencode <http://php.net/urlencode>`__ before passing them in
          +
          +

          Create Container

          +
          $container = $objectStoreService->createContainer('logos');
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get Container Details

          +

          You can retrieve a single container’s details by using its name. An +instance of OpenCloud\ObjectStore\Resource\Container is returned.

          +
          $container = $objectStoreService->getContainer('logos');
          +
          +/** @var $container OpenCloud\ObjectStore\Resource\Container **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          List Containers

          +

          You can retrieve a list of all your containers. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $containers = $objectStoreService->listContainers();
          +foreach ($containers as $container) {
          +    /** @var $container OpenCloud\ObjectStore\Resource\Container  **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Set or Update Container Metadata

          +

          You can set metadata on a container.

          +
          $container->saveMetadata(array(
          +    'author' => 'John Doe'
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get Container Metadata

          +

          You can retrieve the metadata for a container.

          +
          $containerMetadata = $container->getMetadata();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Delete Container

          +

          When you no longer have a need for the container, you can remove it.

          +

          If the container is empty (that is, it has no objects in it), you can +remove it as shown below:

          +
          $container->delete();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          If the container is not empty (that is, it has objects in it), you have +two choices in how to remove it:

          + +
          +
          +

          Get Object Count

          +

          You can quickly find out how many objects are in a container.

          +
          $containerObjectCount = $container->getObjectCount();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, $containerObjectCount will contain the number +of objects in the container represented by $container.

          +
          +
          +

          Get Bytes Used

          +

          You can quickly find out the space used by a container, in bytes.

          +
          $containerSizeInBytes = $container->getBytesUsed();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, $containerSizeInBytes will contain the space +used, in bytes, by the container represented by $container.

          +
          +
          +

          Container Quotas

          +
          +

          Set Quota for Number of Objects

          +

          You can set a quota for the maximum number of objects that may be stored +in a container.

          +
          $maximumNumberOfObjectsAllowedInContainer = 25;
          +$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Set Quota for Total Size of Objects

          +

          You can set a quota for the maximum total space (in bytes) used by +objects in a container.

          +
          use OpenCloud\Common\Constants\Size;
          +
          +$maximumTotalSizeOfObjectsInContainer = 5 * Size::GB;
          +$container->setBytesQuota($maximumTotalSizeOfObjectsInContainer);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get Quota for Number of Objects

          +

          You can retrieve the quota for the maximum number of objects that may be +stored in a container.

          +
          $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get Quota for Total Size of Objects

          +

          You can retrieve the quota for the maximum total space (in bytes) used +by objects in a container.

          +
          $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +
          +

          Objects

          +

          An object (sometimes referred to as a file) is the unit of storage +in an Object Store. An object is a combination of content (data) and +metadata.

          +

          For example, you may upload an object named php-elephant.jpg, a JPEG +image file, to the logos container. Further, you may assign metadata +to this object to indicate that the author of this object was someone +named Jane Doe.

          +
          +
          Note: when working with names that contain non-standard +alphanumerical characters (such as spaces or non-English +characters), you must ensure they are encoded with +`urlencode <http://php.net/urlencode>`__ before passing them in
          +
          +

          Upload Object

          +

          Once you have created a container, you can upload objects to it.

          +
          $localFileName  = '/path/to/local/php-elephant.jpg';
          +$remoteFileName = 'php-elephant.jpg';
          +
          +$fileData = fopen($localFileName, 'r');
          +$container->uploadObject($remoteFileName, $fileData);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, an image file from the local filesystem +(path/to/local/php-elephant.jpg) is uploaded to a container in the +Object Store.

          +

          Note that while we call fopen to open the file resource, we do not +call fclose at the end. The file resource is automatically closed +inside the uploadObject call.

          +

          It is also possible to upload an object and associate metadata with it.

          +
          use OpenCloud\ObjectStore\Resource\DataObject;
          +
          +$localFileName  = '/path/to/local/php-elephant.jpg';
          +$remoteFileName = 'php-elephant.jpg';
          +$metadata = array('author' => 'Jane Doe');
          +
          +$customHeaders = array();
          +$metadataHeaders = DataObject::stockHeaders($metadata);
          +$allHeaders = $customHeaders + $metadataHeaders;
          +
          +$fileData = fopen($localFileName, 'r');
          +$container->uploadObject($remoteFileName, $fileData, $allHeaders);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          Note that while we call fopen to open the file resource, we do not +call fclose at the end. The file resource is automatically closed +inside the uploadObject call.

          +
          +

          Pseudo-hierarchical Folders

          +

          Although you cannot nest directories in an Object Store, you can +simulate a hierarchical structure within a single container by adding +forward slash characters (/) in the object name.

          +
          $localFileName  = '/path/to/local/php-elephant.jpg';
          +$remoteFileName = 'languages/php/elephant.jpg';
          +
          +$fileData = fopen($localFileName, 'r');
          +$container->uploadObject($remoteFileName, $fileData);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, an image file from the local filesystem +(/path/to/local/php-elephant.jpg) is uploaded to a container in the +Object Store. Within that container, the filename is +languages/php/elephant.jpg, where languages/php/ is a +pseudo-hierarchical folder hierarchy.

          +

          Note that while we call fopen to open the file resource, we do not +call fclose at the end. The file resource is automatically closed +inside the uploadObject call.

          +
          +
          +

          Upload Multiple Objects

          +

          You can upload more than one object at a time to a container.

          +
          $objects = array(
          +    array(
          +        'name' => 'php-elephant.jpg',
          +        'path'   => '/path/to/local/php-elephant.jpg'
          +    ),
          +    array(
          +        'name' => 'python-snake.jpg',
          +        'path'   => '/path/to/local/python-snake.jpg'
          +    ),
          +    a
          +);
          +
          +$container->uploadObjects($objects);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the above example, the contents of two files present on the local +filesystem are uploaded as objects to the container referenced by +$container.

          +

          Instead of specifying the path key in an element of the $objects +array, you can specify a body key whose value is a string or a +stream representation.

          +

          Finally, you can pass headers as the second parameter to the +uploadObjects method. These headers will be applied to every object +that is uploaded.

          +
          $metadata = array('author' => 'Jane Doe');
          +
          +$customHeaders = array();
          +$metadataHeaders = DataObject::stockHeaders($metadata);
          +$allHeaders = $customHeaders + $metadataHeaders;
          +
          +$container->uploadObjects($objects, $allHeaders);
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +

          In the example above, every object referenced within the $objects +array will be uploaded with the same metadata.

          +
          +
          +
          +

          Large Objects

          +

          If you want to upload objects larger than 5GB in size, you must use a +different upload process.

          +
          $options = array(
          +    'name' => 'san_diego_vacation_video.mp4',
          +    'path'   => '/path/to/local/videos/san_diego_vacation.mp4'
          +);
          +$objectTransfer = $container->setupObjectTransfer($options);
          +$objectTransfer->upload();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          The process shown above will automatically partition your large object +into small chunks and upload them concurrently to the container +represented by $container.

          +

          You can tune the parameters of this process by specifying additional +options in the $options array. Here is a complete listing of keys +that can be specified in the $options array:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Key nameDescriptionData TypeRequired?Default ValueExample
          nameName of large object in containerStringYes
            +
          • +
          +
          san_diego_vacation_video.mp4
          pathPath to file containing object data on local filesystemStringOne of path or body must be specified
            +
          • +
          +
          /path/to/local/videos/san_diego_vacation.mp4
          bodyString or stream representation of object dataString | StreamOne of path or body must be specified
            +
          • +
          +
          ... lots of data ...
          metadataMetadata for the objectAssociative array of metadata key-value pairsNoarray()array( "Author" => "Jane Doe" )
          partSizeThe size, in bytes, of each chunk that the large object is partitioned into prior to uploadingIntegerNo1073741824 (1GB)52428800 (50MB)
          concurrencyThe number of concurrent transfers to execute as part of the uploadIntegerNo1 (no concurrency; upload chunks serially)10
          progressA callable function or method which is called to report progress of the the upload. See `CURLOPT_PROGRESSFUNCTION documentation <http://us2.php.net/curl_setopt>`__ for details on parameters passed to this callable function or method.String (callable function or method name)NoNonereportProgress
          +
          +
          +

          Auto-extract Archive Files

          +

          You can upload a tar archive file and have the Object Store service +automatically extract it into a container.

          +
          use OpenCloud\ObjectStore\Constants\UrlType;
          +
          +$localArchiveFileName  = '/path/to/local/image_files.tar.gz';
          +$remotePath = 'images/';
          +
          +$fileData = fopen($localArchiveFileName, 'r');
          +$objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the above example, a local archive file named image_files.tar.gz +is uploaded to an Object Store container named images (defined by +the $remotePath variable).

          +

          Note that while we call fopen to open a file resource, we do not +call fclose at the end. The file resource is automatically closed +inside the bulkExtract call.

          +

          The third parameter to bulkExtract is the type of the archive file +being uploaded. The acceptable values for this are:

          +
            +
          • UrlType::TAR for tar archive files, or,
          • +
          • UrlType:TAR_GZ for tar archive files that are compressed with +gzip, or
          • +
          • UrlType::TAR_BZ for tar archive file that are compressed with +bzip
          • +
          +

          Note that the value of $remotePath could have been a +(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as +images/blog as well.

          +
          +
          +

          List Objects in a Container

          +

          You can list all the objects stored in a container. An instance of +OpenCloud\Common\Collection\PaginatedIterator is returned.

          +
          $objects = $container->objectList();
          +foreach ($objects as $object) {
          +    /** @var $object OpenCloud\ObjectStore\Resource\DataObject  **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          You can list only those objects in the container whose names start with +a certain prefix.

          +
          $options = array(
          +    'prefix' => 'php'
          +);
          +
          +$objects = $container->objectList($options);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In general, the objectList() method described above takes an +optional parameter ($options in the example above). This parameter +is an associative array of various options. Here is a complete listing +of keys that can be specified in the $options array:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Key nameDescriptionData TypeRequired?Default ValueExample
          prefixGiven a string x, limits the results to object names beginning with x.StringNo php
          limitGiven an integer n, limits the number of results to at most n values.IntegerNo 10
          markerGiven a string x, returns object names greater than the specified marker.StringNo php-elephant.jpg
          end_markerGiven a string x, returns object names less than the specified marker.StringNo python-snakes.jpg
          +
          +
          +

          Retrieve Object

          +

          You can retrieve an object and its metadata, given the object’s +container and name.

          +
          $objectName = 'php-elephant.jpg';
          +$object = $container->getObject($objectName);
          +
          +/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/
          +
          +$objectContent = $object->getContent();
          +
          +/** @var $objectContent Guzzle\Http\EntityBody **/
          +
          +// Write object content to file on local filesystem.
          +$objectContent->rewind();
          +$stream = $objectContent->getStream();
          +$localFilename = tempnam("/tmp", 'php-opencloud-');
          +file_put_contents($localFilename, $stream);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, $object is the object named +php-elephant.jpg in the container represented by $container. +Further, $objectContent represents the contents of the object. It is +of type +`Guzzle\Http\EntityBody <http://api.guzzlephp.org/class-Guzzle.Http.EntityBody.html>`__.

          +
          +
          +

          Retrieve Object Metadata

          +

          You can retrieve just an object’s metadata without retrieving its +contents.

          +
          $objectName = 'php-elephant.jpg';
          +$object = $container->getPartialObject($objectName);
          +$objectMetadata = $object->getMetadata();
          +
          +/** @var $objectMetadata \OpenCloud\Common\Metadata **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, while $object is an instance of +OpenCloud\ObjectStore\Resource\DataObject, that instance is only +partially populated. Specifically, only properties of the instance +relating to object metadata are populated.

          +
          +
          +

          Temporary URLs

          +

          The Temporary URL feature allows you to create limited-time Internet +addresses that allow you to grant limited access to your Object Store +account. Using this feature, you can allow others to retrieve or place +objects in your Object Store account for a specified amount of time. +Access to the temporary URL is independent of whether or not your +account is CDN-enabled. Even if you do not +CDN-enable a container, you can still grant temporary public access +through a temporary URL.

          +

          First, you must set the temporary URL secret on your account. This is a +one-time operation; you only need to perform it the very first time you +wish to use the temporary URLs feature.

          +
          $account->setTempUrlSecret();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          Note that this operation is carried out on $account, which is an +instance of OpenCloud\ObjectStore\Resource\Account, a class +representing your object store account.

          +

          The above operation will generate a random secret and set it on your +account. Instead of a random secret, if you wish to provide a secret, +you can supply it as a parameter to the setTempUrlSecret method.

          +
          $account->setTempUrlSecret('<SOME SECRET STRING>');
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +

          Once a temporary URL secret has been set on your account, you can +generate a temporary URL for any object in your Object Store.

          +
          $expirationTimeInSeconds = 3600; // one hour from now
          +$httpMethodAllowed = 'GET';
          +$tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, a temporary URL for the object is generated. This +temporary URL will provide public access to the object for an hour (3600 +seconds), as specified by the $expirationTimeInSeconds variable. +Further, only GET HTTP methods will be allowed on this URL, as specified +by the $httpMethodAllowed variable. The other value allowed for the +$httpMethodAllowed variable would be PUT.

          +

          You can also retrieve the temporary URL secret that has been set on your +account.

          +
          $tempUrlSecret = $account->getTempUrlSecret();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Update Object

          +

          You can update an object’s contents (as opposed to updating its +metadata) by simply re-uploading the +object to its container using the same object name +as before.

          +
          +
          +

          Update Object Metadata

          +

          You can update an object’s metadata after it has been uploaded to a +container.

          +
          $object->saveMetadata(array(
          +    'author' => 'John Doe'
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Copy Object

          +

          You can copy an object from one container to another, provided the +destination container already exists.

          +
          $object->copy('logos_copy/php.jpg');
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, both the name of the destination container +(logos_copy)and the name of the destination object (php.jpg) +have to be specified, separated by a /.

          +
          +
          +

          Delete Object

          +

          When you no longer need an object, you can delete it.

          +
          $object->delete();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Bulk Delete

          +

          While you can delete individual objects as shown above, you can also +delete objects and empty containers in bulk.

          +
          $objectStoreService->bulkDelete(array(
          +    'logos/php-elephant.png',
          +    'logos/python-snakes.png',
          +    'some_empty_container'
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, two objects (some_container/object_a.png, +some_other_container/object_z.png) and one empty container +(some_empty_container) are all being deleted in bulk via a single +command.

          +
          +
          +
          +

          CDN Containers

          +

          Note: The functionality described in this section is available only on +the Rackspace cloud. It will not work as described when working with a +vanilla OpenStack cloud.

          +

          Any container can be converted to a CDN-enabled container. When this is +done, the objects within the container can be accessed from anywhere on +the Internet via a URL.

          +
          +

          Enable CDN Container

          +

          To take advantage of CDN capabilities for a container and its objects, +you must CDN-enable that container.

          +
          $container->enableCdn();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Public URLs

          +

          Once you have CDN-enabled a container, you can retrieve a +publicly-accessible URL for any of its objects. There are four types of +publicly-accessible URLs for each object. Each type of URL is meant for +a different purpose. The sections below describe each of these URL types +and how to retrieve them.

          +
          +

          HTTP URL

          +

          You can use this type of URL to access the object over HTTP.

          +
          $httpUrl = $object->getPublicUrl();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Secure HTTP URL

          +

          You can use this type of URL to access the object over HTTP + TLS/SSL.

          +
          use OpenCloud\ObjectStore\Constants\UrlType;
          +
          +$httpsUrl = $object->getPublicUrl(UrlType::SSL);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Streaming URL

          +

          You can use this type of URL to stream a video or audio object using +Adobe’s HTTP Dynamic Streaming.

          +
          use OpenCloud\ObjectStore\Constants\UrlType;
          +
          +$streamingUrl = $object->getPublicUrl(UrlType::STREAMING);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          IOS Streaming URL

          +

          You can use this type of URL to stream an audio or video object to an +iOS device.

          +
          use OpenCloud\ObjectStore\Constants\UrlType;
          +
          +$iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Update CDN Container TTL

          +

          You can update the TTL of a CDN-enabled container.

          +
          $cdnContainer = $container->getCdn();
          +$cdnContainer->setTtl(<NEW TTL, IN SECONDS>);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Disable CDN Container

          +

          If you no longer need CDN capabilities for a container, you can disable +them.

          +
          $container->disableCdn();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Accounts

          +

          An account defines a namespace for containers. An account can +have zero or more containers in it.

          +
          +

          Retrieve Account

          +

          You must retrieve the account before performing any operations on it.

          +
          $account = $objectStoreService->getAccount();
          +
          +
          +
          +
          +

          Get Container Count

          +

          You can quickly find out how many containers are in your account.

          +
          $accountContainerCount = $account->getContainerCount();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get Object Count

          +

          You can quickly find out how many objects are in your account.

          +
          $accountObjectCount = $account->getObjectCount();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, $accountObjectCount will contain the number of +objects in the account represented by $account.

          +
          +
          +

          Get Bytes Used

          +

          You can quickly find out the space used by your account, in bytes.

          +
          $accountSizeInBytes = $account->getBytesUsed();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          In the example above, $accountSizeInBytes will contain the space +used, in bytes, by the account represented by $account.

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/access.html b/doc/_build/html/services/object-store/access.html new file mode 100644 index 000000000..c3befc4c9 --- /dev/null +++ b/doc/_build/html/services/object-store/access.html @@ -0,0 +1,373 @@ + + + + + + + + + + Temporary URLs — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Temporary URLs

          +

          Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they’re CDN-enabled.

          +
          +

          Set “temporary URL” metadata key

          +

          You must set this “secret” value on your account, where it can be used +in a global state:

          +
          $account = $service->getAccount();
          +$account->setTempUrlSecret('my_secret');
          +
          +echo $account->getTempUrlSecret();
          +
          +
          +

          The string argument of setTempUrlSecret() is optional - if left out, +the SDK will generate a random hashed secret for you.

          +

          Get the executable PHP script for this example:

          + +
          +
          +

          Create a temporary URL

          +

          Once you’ve set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute:

          +
          $object->getTemporaryUrl(60, 'GET');
          +
          +
          +

          To allow PUT access for 1 hour:

          +
          $object->getTemporaryUrl(360, 'PUT');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Hosting HTML sites on CDN

          +
          +

          Note

          +

          This feature is only available to Rackspace users.

          +
          +

          To host a static (i.e. HTML) website on Cloud Files, you must follow +these steps:

          +
            +
          1. CDN-enable a container:
          2. +
          +
          $container = $service->getContainer('html_site');
          +$container->enableCdn();
          +
          +
          +
            +
          1. Upload all HTML content. You can use nested directory structures.
          2. +
          +
          $container->uploadObjects(array(
          +    array('name' => 'index.html', 'path' => 'index.html'),
          +    array('name' => 'contact.html', 'path' => 'contact.html'),
          +    array('name' => 'error.html', 'path' => 'error.html'),
          +    array('name' => 'styles.css', 'path' => 'styles.css'),
          +    array('name' => 'main.js', 'path' => 'main.js'),
          +));
          +
          +
          +
            +
          1. Tell Cloud Files what to use for your default index page like this:
          2. +
          +
          $container->setStaticIndexPage('index.html');
          +
          +
          +
            +
          1. (Optional) Tell Cloud Files which error page to use by default:
          2. +
          +
          $container->setStaticErrorPage('error.html');
          +
          +
          +

          Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object.

          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/account.html b/doc/_build/html/services/object-store/account.html new file mode 100644 index 000000000..e4ae6f1cc --- /dev/null +++ b/doc/_build/html/services/object-store/account.html @@ -0,0 +1,333 @@ + + + + + + + + + + Account Details — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Account Details

          +

          To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used):

          +
          +

          Setup

          +
          $account = $service->getAccount();
          +
          +
          +
          +
          +

          View all details

          +
          $details = $account->getDetails();
          +
          +
          +
          +
          +

          Retrieve total container count

          +
          $account->getContainerCount();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieve total object count

          +
          $account->getObjectCount();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieve total bytes used

          +
          $account->getBytesUsed();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/cdn.html b/doc/_build/html/services/object-store/cdn.html new file mode 100644 index 000000000..32d01b3a4 --- /dev/null +++ b/doc/_build/html/services/object-store/cdn.html @@ -0,0 +1,394 @@ + + + + + + + + + + CDN Containers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          CDN Containers

          +
          +

          Note

          +

          This feature is only available to Rackspace users.

          +
          +
          +

          Setup

          +

          In order to interact with CDN containers, you first need to instantiate a +CDN service object:

          +
          $cdnService = $service->getCdnService();
          +
          +
          +
          +
          +

          List CDN-enabled containers

          +

          To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on:

          +
          $cdnContainers = $cdnService->listContainers();
          +
          +foreach ($cdnContainers as $cdnContainer) {
          +  /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          CDN-enable a container

          +

          Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container’s CDN URL with the object name.

          +

          Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period.

          +
          $container->enableCdn();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          CDN-disable a container

          +
          $container->disableCdn();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Operations on CDN-enabled containers

          +

          Once a container has been CDN-enabled, you can retrieve it like so:

          +
          $cdnContainer = $cdnService->cdnContainer('{containerName}');
          +
          +
          +
          +

          Retrieve the SSL URL of a CDN container

          +
          $cdnContainer->getCdnSslUri();
          +
          +
          +
          +
          +

          Retrieve the streaming URL of a CDN container

          +
          $cdnContainer->getCdnStreamingUri();
          +
          +
          +
          +
          +

          Retrieve the iOS streaming URL of a CDN container

          +

          The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices.

          +
          $cdnContainer->getIosStreamingUri();
          +
          +
          +
          +
          +

          CDN logging

          +

          To enable and disable logging for your CDN-enabled container:

          +
          $cdnContainer->enableCdnLogging();
          +$cdnContainer->disableCdnLogging();
          +
          +
          +
          +
          +
          +

          Purge CDN-enabled objects

          +

          To remove a CDN object from public access:

          +
          $object->purge();
          +
          +
          +

          You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged:

          +
          $object->purge('jamie.hannaford@rackspace.com');
          +$object->purge('hello@example.com,hallo@example.com');
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/containers.html b/doc/_build/html/services/object-store/containers.html new file mode 100644 index 000000000..c92b6df18 --- /dev/null +++ b/doc/_build/html/services/object-store/containers.html @@ -0,0 +1,513 @@ + + + + + + + + + + Containers — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Containers

          +
          +

          Create container

          +

          To create a new container, you just need to define its name:

          +
          $container = $service->createContainer('my_amazing_container');
          +
          +
          +

          If the response returned is FALSE, there was an API error - most +likely due to the fact you have a naming collision.

          +

          Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted.

          +
          +

          Note

          +

          When working with names that contain non-standard alphanumerical characters +(such as spaces or non-English characters), you must ensure they are encoded +with urlencode before passing them in

          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List containers

          +
          $containers = $service->listContainers();
          +
          +foreach ($containers as $container) {
          +    /** @param $container OpenCloud\ObjectStore\Resource\Container */
          +    printf("Container name: %s\n", $container->name);
          +    printf("Number of objects within container: %d\n", $container->getObjectCount());
          +}
          +
          +
          +

          Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite’s +memcmp() function, regardless of text encoding.

          +

          The list is limited to 10,000 containers at a time. To work with larger +collections, please read the next section.

          +

          Get the executable PHP script for this example

          +
          +

          Filtering large collections

          +

          When you need more control over collections of containers, you can filter the +results and return back a subset of the total collection by using the marker +and end_marker parameters. The former parameter (marker) tells +the API where to begin the list, and the latter (end_marker) tells +it where to end the list. You may use either of them independently or +together.

          +

          You may also use the limit parameter to fix the number of +containers returned.

          +

          To list a set of containers between two fixed points:

          +
          $someContainers = $service->listContainers(array(
          +    'marker'     => 'container_55',
          +    'end_marker' => 'container_2001'
          +));
          +
          +
          +

          Or to return a limited set:

          +
          $someContainers = $service->listContainers(array('limit' => 560));
          +
          +
          +
          +
          +
          +

          Get container

          +

          To retrieve a certain container:

          +
          /** @param $container OpenCloud\ObjectStore\Resource\Container */
          +$container = $service->getContainer('{containerName}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +

          Retrieve a container’s name

          +
          $name = $container->name;
          +
          +
          +
          +
          +

          Retrieve a container’s object count

          +
          $count = $container->getObjectCount();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Retrieve a container’s total bytes used

          +
          $bytes = $container->getBytesUsed();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Delete container

          +

          Deleting an empty container is easy:

          +
          $container->delete();
          +
          +
          +

          Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +$deleteObjects parameter to TRUE like so:

          +
          $container->delete(true);
          +
          +
          +

          You can also delete all objects +first, and then call delete.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Deleting all objects inside a container

          +
          $container->deleteAllObjects();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Create or update container metadata

          +
          $container->saveMetadata(array(
          +    'Author' => 'Virginia Woolf',
          +    'Published' => '1931'
          +));
          +
          +
          +

          Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To append values +to the current metadata:

          +
          $metadata = $container->appendToMetadata(array(
          +    'Publisher' => 'Hogarth'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Container quotas

          +

          The container_quotas middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check.

          +

          To set quotas for a container:

          +
          use OpenCloud\Common\Constants\Size;
          +
          +$container->setCountQuota(1000);
          +$container->setBytesQuota(2.5 * Size::GB);
          +
          +
          +

          And to retrieve them:

          +
          echo $container->getCountQuota();
          +echo $container->getBytesQuota();
          +
          +
          +

          Get the executable PHP scripts for this example:

          + +
          +
          +

          Access log delivery

          +

          To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage).

          +
          $container->enableLogging();
          +$container->disableLogging();
          +
          +
          +
          +
          +

          Syncing containers

          +

          You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory:

          +
          $container->uploadDirectory('/home/user/my-blog');
          +
          +
          +

          There are four scenarios you should be aware of:

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          LocalRemoteComparisonAction
          File existsFile existsIdentical checksumNo action
          File existsFile existsDifferent checksumLocal file overwrites remote
          File existsFile does not exist
            +
          • +
          +
          Local file created in Swift
          Files does not existFile exists
            +
          • +
          +
          Remote file deleted
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/index.html b/doc/_build/html/services/object-store/index.html new file mode 100644 index 000000000..89c7412af --- /dev/null +++ b/doc/_build/html/services/object-store/index.html @@ -0,0 +1,465 @@ + + + + + + + + + + Object Store v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Object Store v1

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Object Store service

          +

          Now to instantiate the Object Store service:

          +
          $service = $client->objectStoreService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          +
          +

          Operations

          +
          + +
          +
          +
          +

          Glossary

          +
          +
          account
          +
          The portion of the system designated for your use. An Object Store system is +typically designed to be used by many different customers, and your user +account is your portion of it.
          +
          container
          +
          A storage compartment that provides a way for you to organize data. A +container is similar to a folder in Windows or a directory in UNIX. The +primary difference between a container and these other file system concepts +is that containers cannot be nested.
          +
          cdn
          +
          A system of distributed servers (network) that delivers web pages and other +web content to a user based on the geographic locations of the user, the +origin of the web page, and a content delivery server.
          +
          metadata
          +
          Optional information that you can assign to Cloud Files accounts, +containers, and objects through the use of a metadata header.
          +
          object
          +
          An object (sometimes referred to as a file) is the unit of storage in an +Object Store. An object is a combination of content (data) and metadata.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/migrating-containers.html b/doc/_build/html/services/object-store/migrating-containers.html new file mode 100644 index 000000000..511cae3bb --- /dev/null +++ b/doc/_build/html/services/object-store/migrating-containers.html @@ -0,0 +1,405 @@ + + + + + + + + + + Migrating containers across regions — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Migrating containers across regions

          +

          Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a COPY +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality.

          +

          You will be charged for bandwidth between regions, so it’s advisable +to use ServiceNet where possible (which is free).

          +
          +

          Requirements

          +
            +
          • You must install the full Guzzle package, so that the process can +take advantage of Guzzle’s batching functionality (it allows parallel +requests to be batched for greater efficiency). You can do this by +running:
          • +
          +
          composer require guzzle/guzzle
          +
          +
          +
            +
          • Depending on the size and number of transfer items, you will need to +raise PHP’s memory limit:
          • +
          +
          ini_set('memory_limit', '512M');
          +
          +
          +
            +
          • You will need to enact some kind of backoff/retry strategy for rate +limits. Guzzle comes with a convenient feature that just needs to be +added as a normal subscriber:
          • +
          +
          use Guzzle\Plugin\Backoff\BackoffPlugin;
          +
          +// set timeout in secs
          +$timeout = 10;
          +
          +// set HTTP error codes
          +$httpErrors = array(500, 503, 408);
          +
          +$backoffPlugin = BackoffPlugin::getExponentialBackoff($timeout, $httpErrors);
          +$client->addSubscriber($backoffPlugin);
          +
          +
          +

          This tells the client to retry up to 10 times for failed requests +have resulted in these HTTP status codes: 500, 503 or 408.

          +
          +
          +

          Setup

          +

          You can access all this functionality by executing:

          +
          $ordService = $client->objectStoreService('cloudFiles', 'ORD');
          +$iadService = $client->objectStoreService('cloudFiles', 'IAD');
          +
          +$oldContainer = $ordService->getContainer('old_container');
          +$newContainer = $iadService->getContainer('new_container');
          +
          +$iadService->migrateContainer($oldContainer, $newContainer);
          +
          +
          +

          It’s advisable to do this process in a Cloud Server in one of the two +regions you’re migrating to/from. This allows you to use privateURL +as the third argument in the objectStoreService methods like this:

          +
          $client->objectStoreService('cloudFiles', 'IAD', 'privateURL');
          +
          +
          +

          This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free.

          +
          +
          +

          Options

          +

          You can pass in an array of arguments to the method:

          +
          $options = array(
          +    'read.batchLimit'  => 100,
          +    'read.pageLimit'   => 100,
          +    'write.batchLimit' => 50
          +);
          +
          +$iadService->migrateContainer($oldContainer, $newContainer, $options);
          +
          +
          +
          +

          Options explained

          + +++++ + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionDefault
          read.pageLimitWhen the process begins, it has to collect all the files that exist in the old container. It does this through a conventional objectList method, which calls the PaginatedIterator. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API)10,000
          read.batchLimitAfter the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together.1,000
          write.batchLimitOnce each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together.100
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/objects.html b/doc/_build/html/services/object-store/objects.html new file mode 100644 index 000000000..8525dcf51 --- /dev/null +++ b/doc/_build/html/services/object-store/objects.html @@ -0,0 +1,644 @@ + + + + + + + + + + Objects — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Objects

          +
          +

          Setup

          +

          In order to interact with this feature, you must first retrieve a particular +container using its unique name:

          +
          $container = $service->getContainer('{containerName}');
          +
          +
          +
          +
          +

          Create an object

          +

          There are three ways to upload a new file, each of which has different +business needs.

          +
          +

          Note

          +

          Unlike previous versions, you do not need to manually specify your object’s +content type. The API will do this for you.

          +
          +
          +

          Note

          +

          When working with names that contain non-standard alphanumerical characters +(such as spaces or non-English characters), you must ensure they are encoded +with urlencode before passing them in.

          +
          +
          +

          Upload a single file (under 5GB)

          +

          The simplest way to upload a local object, without additional metadata, is by +its path:

          +
          $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+'));
          +
          +
          +

          The resource handle will be automatically closed by Guzzle in its destructor, +so there is no need to execute fclose.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Upload a single file (under 5GB) with metadata

          +

          Although the previous section handles most use cases, there are times when you +want greater control over what is being uploaded. For example, you might want +to control the object’s metadata, or supply additional HTTP headers to coerce +browsers to handle the download a certain way. To add metadata to a new object:

          +
          use OpenCloud\ObjectStore\Resource\DataObject;
          +
          +// specify optional metadata
          +$metadata = array(
          +    'Author' => 'Camera Obscura',
          +    'Origin' => 'Glasgow',
          +);
          +
          +// specify optional HTTP headers
          +$httpHeaders = array(
          +    'Content-Type' => 'application/json',
          +);
          +
          +// merge the two
          +$allHeaders = array_merge(DataObject::stockHeaders($metadata), $httpHeaders);
          +
          +// upload as usual
          +$container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+'), $allHeaders);
          +
          +
          +

          As you will notice, the first argument to uploadObject is the remote object +name, i.e. the name it will be uploaded as. The second argument is either a +file handle resource, or a string representation of object content (a temporary +resource will be created in memory), and the third is an array of additional +headers.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Batch upload multiple files (each under 5GB)

          +
          $files = array(
          +    array(
          +        'name' => 'apache.log',
          +        'path' => '/etc/httpd/logs/error_log'
          +    ),
          +    array(
          +        'name' => 'mysql.log',
          +        'body' => fopen('/tmp/mysql.log', 'r+')
          +    ),
          +    array(
          +        'name' => 'to_do_list.txt',
          +        'body' => 'PHONE HOME'
          +    )
          +);
          +
          +$container->uploadObjects($files);
          +
          +
          +

          As you can see, the name key is required for every file. You must +also specify either a path key (to an existing file), or a body. +The body can either be a PHP resource or a string representation of +the content you want to upload.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Upload large files (over 5GB)

          +

          For files over 5GB, you will need to use the +OpenCloud\ObjectStore\Upload\TransferBuilder factory to build and execute your +transfer. For your convenience, the Container resource object contains a simple +method to do this heavy lifting for you:

          +
          $transfer = $container->setupObjectTransfer(array(
          +    'name'        => 'video.mov',
          +    'path'        => '/home/user/video.mov',
          +    'metadata'    => array('Author' => 'Jamie'),
          +    'concurrency' => 4,
          +    'partSize'    => 1.5 * Size::GB
          +));
          +
          +$transfer->upload();
          +
          +
          +

          You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it’s a segment rather than the full file). After all parts +are uploaded, a manifestfile is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download.

          +

          In Swift terminology, the name for this process is Dynamic Large Object (DLO). +To find out more details, please consult the official documentation.

          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          List objects in a container

          +

          To return a list of objects:

          +
          $files = $container->objectList();
          +
          +foreach ($files as $file) {
          +    /** @var $file OpenCloud\ObjectStore\Resource\DataObject */
          +}
          +
          +
          +

          By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the official +docs.

          +
          $container->objectList(array('prefix' => 'logFile_'));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get object

          +

          To retrieve a specific file from Cloud Files:

          +
          /** @var $file OpenCloud\ObjectStore\Resource\DataObject */
          +$file = $container->getObject('summer_vacation.mp4');
          +
          +
          +

          Once you have access to this OpenCloud\ObjectStore\Resource\DataObject +object, you can access these attributes:

          +
          +

          Get object’s parent container

          +
          /** @param $container OpenCloud\ObjectStore\Resource\Container */
          +$container = $object->getContainer();
          +
          +
          +
          +
          +

          Get file name

          +
          /** @param $name string */
          +$name = $object->getName();
          +
          +
          +
          +
          +

          Get file size

          +
          /** @param $size int */
          +$size = $object->getContentLength();
          +
          +
          +
          +
          +

          Get content of file

          +
          /** @param $content Guzzle\Http\EntityBody */
          +$content = $object->getContainer();
          +
          +
          +
          +
          +

          Get type of file

          +
          /** @param $type string */
          +$type = $object->getContentType();
          +
          +
          +
          +
          +

          Get file checksum

          +
          /** @param $etag string */
          +$etag = $object->getEtag();
          +
          +
          +
          +
          +

          Get last modified date of file

          +
          /** @param $lastModified string */
          +$lastModified = $object->getLastModified();
          +
          +
          +
          +
          +

          Conditional requests

          +

          You can also perform conditional requests according to RFC 2616 +specification (§§ 14.24-26). +Supported headers are If-Match, If-None-Match, +If-Modified-Since and If-Unmodified-Since.

          +

          So, to retrieve a file’s contents only if it’s been recently changed

          +
          $file = $container->getObject('error_log.txt', array(
          +    'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT'
          +));
          +
          +if ($file->getContentLength()) {
          +    echo 'Has been changed since the above date';
          +} else {
          +    echo 'Has not been changed';
          +}
          +
          +
          +

          Retrieve a file only if it has NOT been modified (and expect a 412 on +failure):

          +
          use Guzzle\Http\Exception\ClientErrorResponseException;
          +
          +try {
          +    $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array(
          +        'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT'
          +    ));
          +} catch (ClientErrorResponseException $e) {
          +    echo 'This file has been modified...';
          +}
          +
          +
          +

          Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file:

          +
          $snippet = $container->getObject('output.log', array('range' => 'bytes=-20'));
          +
          +
          +
          +
          +
          +

          Update an existing object

          +
          $file->setContent(fopen('/path/to/new/content', 'r+'));
          +$file->update();
          +
          +
          +

          Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file.

          +
          +
          +

          Copy object to new location

          +

          To copy a file to another location, you need to specify a string-based +destination path:

          +
          $object->copy('/container_2/new_object_name');
          +
          +
          +

          Where container_2 is the name of the container, and new_object_name is +the name of the object inside the container that does not exist yet.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Get object metadata

          +

          You can fetch just the object metadata without fetching the full +content:

          +
          $container->getPartialObject('summer_vacation.mp4');
          +
          +
          +

          In order to access the metadata on a partial or complete object, use:

          +
          $object->getMetadata();
          +
          +
          +

          You can turn a partial object into a full object to get the content +after looking at the metadata:

          +
          $object->refresh();
          +
          +
          +

          You can also update to get the latest metadata:

          +
          $object->retrieveMetadata();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update object metadata

          +

          Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you’ll be executing one soon (an update operation for +example):

          +
          // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation
          +$object->setMetadata(array(
          +    'Author' => 'Hemingway'
          +));
          +
          +// ... code here
          +
          +$object->update();
          +
          +
          +

          Alternatively, you can update the API straight away - so that everything +is retained:

          +
          $object->saveMetadata(array(
          +    'Author' => 'Hemingway'
          +));
          +
          +
          +

          Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method:

          +
          $metadata = $object->appendToMetadata(array(
          +  'Author' => 'Hemingway'
          +));
          +
          +$object->saveMetadata($metadata);
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Extract archive

          +

          CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container:

          +
          use OpenCloud\ObjectStore\Constants\UrlType;
          +
          +$service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ);
          +
          +
          +

          You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive.

          +

          Get the executable PHP script for this example

          +
          +
          +

          Delete object

          +
          $object->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Delete multiple objects

          +

          Bulk delete a set of paths:

          +
          $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log');
          +
          +$service->bulkDelete($pathsToBeDeleted);
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/object-store/rs-only.html b/doc/_build/html/services/object-store/rs-only.html new file mode 100644 index 000000000..783588e1d --- /dev/null +++ b/doc/_build/html/services/object-store/rs-only.html @@ -0,0 +1,283 @@ + + + + + + + + + + <no title> — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Note

          +

          This feature is only available to Rackspace users.

          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/README.md.html b/doc/_build/html/services/orchestration/README.md.html new file mode 100644 index 000000000..4342e1f82 --- /dev/null +++ b/doc/_build/html/services/orchestration/README.md.html @@ -0,0 +1,270 @@ + + + + + + + + + + Orchestration — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Orchestration

          +

          Orchestration is a service that can be used to create and manage +cloud resources. Examples of such resources are databases, load +balancers, servers and software installed on them.

          +
          +

          Concepts

          +

          To use the Orchestration service effectively, you should understand +several key concepts:

          +
            +
          • Template: An Orchestration template is a JSON or YAML document +that describes how a set of resources should be assembled to produce +a working deployment. The template specifies what resources should be +used, what attributes of these resources are parameterized and what +information is output to the user when a template is instantiated.
          • +
          • Resource: A resource is a template artifact that represents some +component of your desired architecture (a Cloud Server, a group of +scaled Cloud Servers, a load balancer, some configuration management +system, and so forth).
          • +
          • Stack: A stack is a running instance of a template. When a stack +is created, the resources specified in the template are created.
          • +
          +
          +
          +

          Getting started

          +
          +

          1. Instantiate an OpenStack or Rackspace client.

          +

          To use the Orchestration service, you must first instantiate a +OpenStack or Rackspace client object.

          +
            +
          • If you are working with an OpenStack cloud, instantiate an +OpenCloud\OpenStack client as follows:

            +
            use OpenCloud\OpenStack;
            +
            +$client = new OpenStack('<OPENSTACK CLOUD IDENTITY ENDPOINT URL>', array(
            +    'username' => '<YOUR OPENSTACK CLOUD ACCOUNT USERNAME>',
            +    'password' => '<YOUR OPENSTACK CLOUD ACCOUNT PASSWORD>'
            +));
            +
            +
            +
          • +
          • If you are working with the Rackspace cloud, instantiate a +OpenCloud\Rackspace client as follows:

            +
            use OpenCloud\Rackspace;
            +
            +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            + ));
            +
            +
            +
          • +
          +
          +
          +

          2. Obtain an Orchestration service object from the client.

          +

          All Orchestration operations are done via an orchestration service +object. To instantiate this object, call the orchestrationService +method on the $client object as shown in the following example:

          +
          $region = '<CLOUD REGION NAME>';
          +$orchestrationService = $client->orchestrationService(null, $region);
          +
          +
          +

          Any stacks and resources created with this $orchestrationService +instance will be stored in the cloud region specified by $region.

          +
          +
          +

          3. Create a stack from a template.

          +
          $stack = $orchestrationService->createStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'templateUrl'  => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'  => 5
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Next steps

          +

          Once you have created a stack, there is more you can do with it. See +complete user guide for orchestration.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/USERGUIDE.md.html b/doc/_build/html/services/orchestration/USERGUIDE.md.html new file mode 100644 index 000000000..8fe353d2e --- /dev/null +++ b/doc/_build/html/services/orchestration/USERGUIDE.md.html @@ -0,0 +1,921 @@ + + + + + + + + + + Complete User Guide for the Orchestration Service — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Complete User Guide for the Orchestration Service

          +

          Orchestration is a service that you can use to create and manage cloud +resources such as databases, load balancers, and servers, and the +software installed on servers.

          + +
          +

          Concepts

          +

          To use the Orchestration service effectively, you should understand the +following key concepts:

          +
            +
          • Template: A JSON or YAML document that describes how a set of +resources should be assembled to produce a working deployment. The +template specifies the resources to use, the attributes of these +resources that are parameterized and the information that is sent to +the user when a template is instantiated.
          • +
          • Resource: Some component of your architecture (a cloud server, a +group of scaled cloud servers, a load balancer, some configuration +management system, and so on) that is defined in a template.
          • +
          • Stack: A running instance of a template. When a stack is created, +the resources specified in the template are created.
          • +
          +
          +
          +

          Prerequisites

          +
          +

          Client

          +

          To use the Orchestration service, you must first instantiate a +OpenStack or Rackspace client object.

          +
            +
          • If you are working with an OpenStack cloud, instantiate an +OpenCloud\OpenStack client as follows:

            +
            use OpenCloud\OpenStack;
            +
            +$client = new OpenStack('<OPENSTACK CLOUD IDENTITY ENDPOINT URL>', array(
            +    'username' => '<YOUR OPENSTACK CLOUD ACCOUNT USERNAME>',
            +    'password' => '<YOUR OPENSTACK CLOUD ACCOUNT PASSWORD>'
            +));
            +
            +
            +
          • +
          • If you are working with the Rackspace cloud, instantiate a +OpenCloud\Rackspace client as follows:

            +
            use OpenCloud\Rackspace;
            +
            +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
            +    'username' => '<YOUR RACKSPACE CLOUD ACCOUNT USERNAME>',
            +    'apiKey'   => '<YOUR RACKSPACE CLOUD ACCOUNT API KEY>'
            +));
            +
            +
            +
          • +
          +
          +
          +

          Orchestration service

          +

          All Orchestration operations are done via an orchestration service +object. To instantiate this object, call the orchestrationService +method on the $client object. This method takes two arguments:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
          PositionDescriptionData typeRequired?Default valueExample value
          1Name of the service, as it appears in the service catalogStringNonull; automatically determined when possiblecloudOrchestration
          2Cloud regionStringYes
            +
          • +
          +
          DFW
          +
          $region = '<CLOUD REGION NAME>';
          +$orchestrationService = $client->orchestrationService(null, $region);
          +
          +
          +

          Any stacks and resources created with this $orchestrationService +instance will be stored in the cloud region specified by $region.

          +
          +
          +
          +

          Templates

          +

          An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a stack). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated.

          +
          +

          Validate template

          +

          Before you use a template to create a stack, you might want to validate +it.

          +
          +

          Validate a template from a file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example:

          +
          use OpenCloud\Common\Exceptions\InvalidTemplateError;
          +
          +try {
          +    $orchestrationService->validateTemplate(array(
          +        'template' => file_get_contents(__DIR__ . '/lamp.yaml')
          +    ));
          +} catch (InvalidTemplateError $e) {
          +    // Use $e->getMessage() for explanation of why template is invalid
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +
          +
          +

          Validate Template from URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example:

          +
          use OpenCloud\Common\Exceptions\InvalidTemplateError;
          +
          +try {
          +    $orchestrationService->validateTemplate(array(
          +        'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml'
          +    ));
          +} catch (InvalidTemplateError $e) {
          +    // Use $e->getMessage() for explanation of why template is invalid
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +
          +
          +
          +
          +

          Stacks

          +

          A stack is a running instance of a template. When a stack is created, +the resources specified in the template are +created.

          +
          +

          Preview stack

          +

          Before you create a stack from a template, you might want to see what +that stack will look like. This is called previewing the stack.

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameName of the stackString. Must start with an alphabetic character, and must contain only alphanumeric, _, - or . charactersYes
            +
          • +
          +
          simple-lamp-setup
          templateTemplate contentsString. JSON or YAMLNo, if templateUrl is specifiednullheat_template_version: 2013-05-23\ndescription: LAMP server\n
          templateUrlURL of the template fileString. HTTP or HTTPS URLNo, if template is specifiednullhttps://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml
          parametersArguments to the template, based on the template’s parameters. For example, see the parameters in this template sectionAssociative arrayNonullarray('flavor_id' => 'general1-1')
          +
          +

          Preview a stack from a template file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example:

          +
          $stack = $orchestrationService->previewStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'template'     => file_get_contents(__DIR__ . '/lamp.yml'),
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    )
          +));
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +
          +
          +

          Preview a stack from a template URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example:

          +
          $stack = $orchestrationService->previewStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'templateUrl'  => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    )
          +));
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +
          +
          +
          +

          Create stack

          +

          You can create a stack from a template.

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameName of the stackString. Must start with an alphabetic character, and must contain only alphanumeric, _, - or . characters.Yes
            +
          • +
          +
          simple-lamp-setup
          templateTemplate contentsString. JSON or YAMLNo, if templateUrl is specifiednullheat_template_version: 2013-05-23\ndescription: LAMP server\n
          templateUrlURL of template fileString. HTTP or HTTPS URLNo, if template is specifiednullhttps://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml
          parametersArguments to the template, based on the template’s parametersAssociative arrayNonullarray('server_hostname' => 'web01')
          timeoutMinsDuration, in minutes, after which stack creation should time outIntegerYes
            +
          • +
          +
          5
          +
          +

          Create a stack from a template file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example:

          +
          $stack = $orchestrationService->createStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'templateUrl'  => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'  => 5
          +));
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +
          +
          +

          Create a stack from a template URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example:

          +
          $stack = $orchestrationService->stack();
          +$stack->create(array(
          +    'name'          => 'simple-lamp-setup',
          +    'templateUrl'   => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'    => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'   => 5
          +));
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          List stacks

          +

          You can list all the stacks that you have created as shown in the +following example:

          +
          $stacks = $orchestrationService->listStacks();
          +foreach ($stacks as $stack) {
          +    /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get stack

          +

          You can retrieve a specific stack using its name, as shown in the +following example:

          +
          $stack = $orchestrationService->getStack('simple-lamp-setup');
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get stack template

          +

          You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack.

          +
          $stackTemplate = $stack->getTemplate();
          +/** @var $stackTemplate string **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Update stack

          +

          You can update a running stack.

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          templateTemplate contentsString. JSON or YAMLNo, if templateUrl is specifiednullheat_template_version: 2013-05-23\ndescription: LAMP server\n
          templateUrlURL of template fileString. HTTP or HTTPS URLNo, if template is specifiednullhttps://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml
          parametersArguments to the template, based on the template’s parametersAssociative arrayNonullarray('flavor_id' => 'general1-1')
          timeoutMinsDuration, in minutes, after which stack update should time outIntegerYes
            +
          • +
          +
          5
          +
          +

          Update a stack from a template file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example:

          +
          $stack->update(array(
          +    'template'      => file_get_contents(__DIR__ . '/lamp-updated.yml'),
          +    'parameters'    => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'   => 5
          +));
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example +]

          +
          +
          +

          Update Stack from Template URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example:

          +
          $stack->update(array(
          +    'templateUrl'   => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml',
          +    'parameters'    => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'   => 5
          +));
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Delete stack

          +

          If you no longer need a stack and all its resources, you can delete the +stack and the resources as shown in the following example:

          +
          $stack->delete();
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Abandon Stack

          +

          If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example:

          +
          $abandonStackData = $stack->abandon();
          +/** @var $abandonStackData string **/
          +
          +file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData);
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +

          Note that this operation returns data about the abandoned stack as a +string. You can use this data to recreate the stack by using the adopt +stack operation.

          +
          +
          +

          Adopt stack

          +

          If you have data from an abandoned stack, you can re-create the stack as +shown in the following example:

          +
          $stack = $orchestrationService->adoptStack(array(
          +    'name'           => 'simple-lamp-setup',
          +    'template'       => file_get_contents(__DIR__ . '/lamp.yml'),
          +    'adoptStackData' => $abandonStackData,
          +    'timeoutMins'    => 5
          +));
          +/** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Stack resources

          +

          A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers.

          +
          +

          List stack resources

          +

          You can list all the resources for a stack as shown in the following +example:

          +
          $resources = $stack->listResources();
          +foreach ($resources as $resource) {
          +    /** @var $resource OpenCloud\Orchestration\Resource\Resource **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get stack resource

          +

          You can retrieve a specific resource in a stack bt using that resource’s +name, as shown in the following example:

          +
          $resource = $stack->getResource('load-balancer');
          +/** @var $resource OpenCloud\Orchestration\Resource\Resource **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get stack resource metadata

          +

          You can retrieve the metadata for a specific resource in a stack as +shown in the following example:

          +
          $resourceMetadata = $resource->getMetadata();
          +/** @var $resourceMetadata \stdClass **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Stack resource events

          +

          Operations on resources within a stack (such as the creation of a +resource) produce events.

          +
          +

          List stack events

          +

          You can list all of the events for all of the resources in a stack as +shown in the following example:

          +
          $stackEvents = $stack->listEvents();
          +foreach ($stackEvents as $stackEvent) {
          +    /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          List stack resource events

          +

          You can list all of the events for a specific resource in a stack as +shown in the following example:

          +
          $resourceEvents = $resource->listEvents();
          +foreach ($resourceEvents as $resourceEvent) {
          +    /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get stack resource event

          +

          You can retrieve a specific event for a specific resource in a stack, by +using the resource event’s ID, as shown in the following example:

          +
          $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729');
          +/** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Resource types

          +

          When you define a template, you must use resource types supported by +your cloud.

          +
          +

          List resource types

          +

          You can list all supported resource types as shown in the following +example:

          +
          $resourceTypes = $orchestrationService->listResourceTypes();
          +foreach ($resourceTypes as $resourceType) {
          +    /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/
          +}
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get resource type

          +

          You can retrieve a specific resource type’s schema as shown in the +following example:

          +
          $resourceType = $orchestrationService->getResourceType('OS::Nova::Server');
          +/** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +

          Get resource type template

          +

          You can retrieve a specific resource type’s representation as it would +appear in a template, as shown in the following example:

          +
          $resourceTypeTemplate = $resourceType->getTemplate();
          +/** @var $resourceTypeTemplate string **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          +

          Build info

          +
          +

          Get build info

          +

          You can retrieve information about the current Orchestration service +build as shown in the following example:

          +
          $buildInfo = $orchestrationService->getBuildInfo();
          +/** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/
          +
          +
          +

          [ Get the executable PHP script for this +example ]

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/build-info.html b/doc/_build/html/services/orchestration/build-info.html new file mode 100644 index 000000000..8f4421b7a --- /dev/null +++ b/doc/_build/html/services/orchestration/build-info.html @@ -0,0 +1,306 @@ + + + + + + + + + + Build info — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Build info

          +
          +

          Get build info

          +

          You can retrieve information about the current Orchestration service +build as shown in the following example:

          +
          /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/
          +$buildInfo = $orchestrationService->getBuildInfo();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/events.html b/doc/_build/html/services/orchestration/events.html new file mode 100644 index 000000000..18f92260d --- /dev/null +++ b/doc/_build/html/services/orchestration/events.html @@ -0,0 +1,334 @@ + + + + + + + + + + Stack resource events — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Stack resource events

          +

          Operations on resources within a stack (such as the creation of a +resource) produce events.

          +
          +

          List stack events

          +

          You can list all of the events for all of the resources in a stack as +shown in the following example:

          +
          $stackEvents = $stack->listEvents();
          +
          +foreach ($stackEvents as $stackEvent) {
          +    /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List stack resource events

          +

          You can list all of the events for a specific resource in a stack as +shown in the following example:

          +
          $resourceEvents = $resource->listEvents();
          +
          +foreach ($resourceEvents as $resourceEvent) {
          +    /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get stack resource event

          +

          You can retrieve a specific event for a specific resource in a stack, by +using the resource event’s ID, as shown in the following example:

          +
          /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/
          +$resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/index.html b/doc/_build/html/services/orchestration/index.html new file mode 100644 index 000000000..bc180c642 --- /dev/null +++ b/doc/_build/html/services/orchestration/index.html @@ -0,0 +1,426 @@ + + + + + + + + + + Orchestration v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Orchestration v1

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Orchestration service

          +

          Now to instantiate the Orchestration service:

          +
          $service = $client->orchestrationService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          template
          +
          An Orchestration template is a JSON or YAML document +that describes how a set of resources should be assembled to produce +a working deployment. The template specifies what resources should be +used, what attributes of these resources are parameterized and what +information is output to the user when a template is instantiated.
          +
          resource
          +
          A resource is a template artifact that represents some +component of your desired architecture (a Cloud Server, a group of +scaled Cloud Servers, a load balancer, some configuration management +system, and so forth).
          +
          stack
          +
          A stack is a running instance of a template. When a stack +is created, the resources specified in the template are created.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/resource-types.html b/doc/_build/html/services/orchestration/resource-types.html new file mode 100644 index 000000000..b2ee5e4b2 --- /dev/null +++ b/doc/_build/html/services/orchestration/resource-types.html @@ -0,0 +1,330 @@ + + + + + + + + + + Resource types — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Resource types

          +

          When you define a template, you must use resource types supported by +your cloud.

          +
          +

          List resource types

          +

          You can list all supported resource types as shown in the following +example:

          +
          $resourceTypes = $orchestrationService->listResourceTypes();
          +foreach ($resourceTypes as $resourceType) {
          +    /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get resource type

          +

          You can retrieve a specific resource type’s schema as shown in the +following example:

          +
          /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/
          +$resourceType = $orchestrationService->getResourceType('OS::Nova::Server');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get resource type template

          +

          You can retrieve a specific resource type’s representation as it would +appear in a template, as shown in the following example:

          +
          /** @var $resourceTypeTemplate string **/
          +$resourceTypeTemplate = $resourceType->getTemplate();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/resources.html b/doc/_build/html/services/orchestration/resources.html new file mode 100644 index 000000000..437e683a3 --- /dev/null +++ b/doc/_build/html/services/orchestration/resources.html @@ -0,0 +1,331 @@ + + + + + + + + + + Stack resources — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Stack resources

          +

          A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers.

          +
          +

          List stack resources

          +

          You can list all the resources for a stack as shown in the following +example:

          +
          $resources = $stack->listResources();
          +
          +foreach ($resources as $resource) {
          +    /** @var $resource OpenCloud\Orchestration\Resource\Resource **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get stack resource

          +

          You can retrieve a specific resource in a stack bt using that resource’s +name, as shown in the following example:

          +
          /** @var $resource OpenCloud\Orchestration\Resource\Resource **/
          +$resource = $stack->getResource('load-balancer');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get stack resource metadata

          +

          You can retrieve the metadata for a specific resource in a stack as +shown in the following example:

          +
          /** @var $resourceMetadata \stdClass **/
          +$resourceMetadata = $resource->getMetadata();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/stacks.html b/doc/_build/html/services/orchestration/stacks.html new file mode 100644 index 000000000..7a2dd4fb2 --- /dev/null +++ b/doc/_build/html/services/orchestration/stacks.html @@ -0,0 +1,667 @@ + + + + + + + + + + Stacks — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Stacks

          +

          A stack is a running instance of a template. When a stack is created, +the resources specified in the template are +created.

          +
          +

          Preview stack

          +

          Before you create a stack from a template, you might want to see what +that stack will look like. This is called previewing the stack.

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameName of the stackString. Must start with an alphabetic character, and must contain only alphanumeric, _, - or . charactersYes
            +
          • +
          +
          simple-lamp-setup
          templateTemplate contentsString. JSON or YAMLNo, if templateUrl is specifiednullheat_template_version: 2013-05-23\ndescription: LAMP server\n
          templateUrlURL of the template fileString. HTTP or HTTPS URLNo, if template is specifiednullhttps://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml
          parametersArguments to the template, based on the template’s parameters. For example, see the parameters in this template sectionAssociative arrayNonullarray('flavor_id' => 'general1-1')
          +
          +

          Preview a stack from a template file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack = $orchestrationService->previewStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'template'     => file_get_contents(__DIR__ . '/lamp.yml'),
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    )
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Preview a stack from a template URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack = $orchestrationService->previewStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'templateUrl'  => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    )
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Create stack

          +

          You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          nameName of the stackString. Must start with an alphabetic character, and must contain only alphanumeric, _, - or . characters.Yes
            +
          • +
          +
          simple-lamp-setup
          templateTemplate contentsString. JSON or YAMLNo, if templateUrl is specifiednullheat_template_version: 2013-05-23\ndescription: LAMP server\n
          templateUrlURL of template fileString. HTTP or HTTPS URLNo, if template is specifiednullhttps://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml
          parametersArguments to the template, based on the template’s parametersAssociative arrayNonullarray('server_hostname' => 'web01')
          timeoutMinsDuration, in minutes, after which stack creation should time outIntegerYes
            +
          • +
          +
          5
          +
          +

          Create a stack from a template file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack = $orchestrationService->createStack(array(
          +    'name'         => 'simple-lamp-setup',
          +    'templateUrl'  => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'   => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'  => 5
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Create a stack from a template URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example:

          +
          $stack = $orchestrationService->stack();
          +$stack->create(array(
          +    'name'          => 'simple-lamp-setup',
          +    'templateUrl'   => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml',
          +    'parameters'    => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'   => 5
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          List stacks

          +

          You can list all the stacks that you have created as shown in the +following example:

          +
          $stacks = $orchestrationService->listStacks();
          +foreach ($stacks as $stack) {
          +    /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get stack

          +

          You can retrieve a specific stack using its name, as shown in the +following example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack = $orchestrationService->getStack('simple-lamp-setup');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get stack template

          +

          You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack.

          +
          /** @var $stackTemplate string **/
          +$stackTemplate = $stack->getTemplate();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update stack

          +

          You can update a running stack.

          +

          This operation takes one parameter, an associative array, with the +following keys:

          + ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameDescriptionData typeRequired?Default valueExample value
          templateTemplate contentsString. JSON or YAMLNo, if templateUrl is specifiednullheat_template_version: 2013-05-23\ndescription: LAMP server\n
          templateUrlURL of template fileString. HTTP or HTTPS URLNo, if template is specifiednullhttps://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml
          parametersArguments to the template, based on the template’s parametersAssociative arrayNonullarray('flavor_id' => 'general1-1')
          timeoutMinsDuration, in minutes, after which stack update should time outIntegerYes
            +
          • +
          +
          5
          +
          +

          Update a stack from a template file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack->update(array(
          +    'template'      => file_get_contents(__DIR__ . '/lamp-updated.yml'),
          +    'parameters'    => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'   => 5
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Update Stack from Template URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack->update(array(
          +    'templateUrl'   => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml',
          +    'parameters'    => array(
          +        'server_hostname' => 'web01',
          +        'image'           => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
          +    ),
          +    'timeoutMins'   => 5
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Delete stack

          +

          If you no longer need a stack and all its resources, you can delete the +stack and the resources as shown in the following example:

          +
          $stack->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Abandon Stack

          +
          +

          Note

          +

          This operation returns data about the abandoned stack as a string. You can +use this data to recreate the stack by using the adopt stack +operation.

          +
          +

          If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example:

          +
          /** @var $abandonStackData string **/
          +$abandonStackData = $stack->abandon();
          +file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData);
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Adopt stack

          +

          If you have data from an abandoned stack, you can re-create the stack as +shown in the following example:

          +
          /** @var $stack OpenCloud\Orchestration\Resource\Stack **/
          +$stack = $orchestrationService->adoptStack(array(
          +    'name'           => 'simple-lamp-setup',
          +    'template'       => file_get_contents(__DIR__ . '/lamp.yml'),
          +    'adoptStackData' => $abandonStackData,
          +    'timeoutMins'    => 5
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/orchestration/templates.html b/doc/_build/html/services/orchestration/templates.html new file mode 100644 index 000000000..8e6f33e21 --- /dev/null +++ b/doc/_build/html/services/orchestration/templates.html @@ -0,0 +1,340 @@ + + + + + + + + + + Templates — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Templates

          +

          An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a stack). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated.

          +
          +

          Validating templates

          +

          Before you use a template to create a stack, you might want to validate it.

          +
          +

          Validate a template from a file

          +

          If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example:

          +
          use OpenCloud\Common\Exceptions\InvalidTemplateError;
          +
          +try {
          +    $orchestrationService->validateTemplate(array(
          +        'template' => file_get_contents(__DIR__ . '/lamp.yaml')
          +    ));
          +} catch (InvalidTemplateError $e) {
          +    // Use $e->getMessage() for explanation of why template is invalid
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Validate Template from URL

          +

          If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example:

          +
          use OpenCloud\Common\Exceptions\InvalidTemplateError;
          +
          +try {
          +    $orchestrationService->validateTemplate(array(
          +        'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml'
          +    ));
          +} catch (InvalidTemplateError $e) {
          +    // Use $e->getMessage() for explanation of why template is invalid
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/Claim.md.html b/doc/_build/html/services/queues/Claim.md.html new file mode 100644 index 000000000..b01ca78fe --- /dev/null +++ b/doc/_build/html/services/queues/Claim.md.html @@ -0,0 +1,323 @@ + + + + + + + + + + 1. Introduction — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          1. Introduction

          +

          A Claim is the process of a worker checking out a message to perform +a task. Claiming a message prevents other workers from attempting to +process the same messages.

          +
          +
          +

          2. Setup

          +

          A Claim is initialized on its parent object, a Queue:

          +
          // To initialize an empty object:
          +$claim = $queue->getClaim();
          +
          +// or retrieve a specific claim:
          +$claim = $queue->getClaim('51db7067821e727dc24df754');
          +
          +
          +
          +
          +

          3. Claim messages

          +
          +

          3.1 Description

          +

          This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +204 No Content message.

          +

          When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it.

          +

          The age given for a claim is relative to the server’s clock. The claim’s +age is useful for determining how quickly messages are getting processed +and whether a given message’s claim is about to expire.

          +

          When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message.

          +
          +
          +

          3.2 Attributes

          +

          The ttl attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request.

          +

          The grace attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted.

          +

          The limit attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional.

          +
          +
          +

          3.3 Code

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$queue->claimMessages(array(
          +    'limit' => 15,
          +    'grace' => 5 * Datetime::MINUTE,
          +    'ttl'   => 5 * Datetime::MINUTE
          +));
          +
          +
          +
          +
          +
          +

          4. Query claim

          +
          +

          4.1 Description

          +

          This operation queries the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored.

          +
          +
          +

          4.2 Attributes

          +

          Claim ID.

          +
          +
          +

          4.3 Code

          +
          $claim = $queue->getClaim('51db7067821e727dc24df754');
          +
          +
          +
          +
          +
          +

          5. Update claim

          +
          +

          5.1 Description

          +

          This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored.

          +

          Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific Claim and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL.

          +
          +
          +

          5.2 Attributes

          +

          See section 4.2.

          +
          +
          +

          5.3 Code

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$claim->update(array(
          +    'ttl' => 10 * Datetime::MINUTE
          +));
          +
          +
          +
          +
          +
          +

          6. Release claim

          +
          +

          6.1 Description

          +

          This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored.

          +

          This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers.

          +
          +
          +

          6.2 Attributes

          +

          See section 4.2.

          +
          +
          +

          6.3 Code

          +
          $message->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/Message.md.html b/doc/_build/html/services/queues/Message.md.html new file mode 100644 index 000000000..cffa9aa2b --- /dev/null +++ b/doc/_build/html/services/queues/Message.md.html @@ -0,0 +1,430 @@ + + + + + + + + + + 1. Introduction — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          1. Introduction

          +

          A Message is a task, a notification, or any meaningful data that a +producer or publisher sends to the queue. A message exists until it is +deleted by a recipient or automatically by the system based on a TTL +(time-to-live) value.

          +
          +
          +

          2. Setup

          +

          A message is initialized from its parent object, a Queue:

          +
          // Setup an empty object
          +$message = $queue->getMessage();
          +
          +// or retrieve an existing one
          +$message = $queue->getMessage('<message_id>');
          +
          +
          +
          +
          +

          3. Post message

          +
          +

          3.1 Description

          +

          This operation posts the specified message or messages. You can submit +up to 10 messages in a single request.

          +

          When posting new messages, you specify only the body and ttl for +the message. The API will insert metadata, such as ID and age.

          +
          +
          +

          3.2 Parameters

          +

          How you pass through the array structure depends on whether you are +executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the +same.

          +

          The body attribute specifies an arbitrary document that constitutes +the body of the message being sent. The size of this body is limited to +256 KB, excluding whitespace.

          +

          The ttl attribute specifies how long the server waits before marking +the message as expired and removing it from the queue. The value of ttl +must be between 60 and 1209600 seconds (14 days). Note that the server +might not actually delete the message until its age has reached up to +(ttl + 60) seconds, to allow for flexibility in storage implementations.

          +
          +
          +

          3.3 Code samples

          +
          +

          3.3.1 Posting a single message

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$queue->createMessage(array(
          +    'body' => (object) array(
          +        'event'    => 'BackupStarted',
          +        'deadline' => '26.12.2013
          +    ),
          +    'ttl'  => 2 * Datetime::DAY
          +));
          +
          +
          +
          +
          +

          3.3.2 Post a batch of messages

          +

          Please note that the list of messages will be truncated at 10. For more, +please execute another method call.

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$messages = array(
          +    array(
          +        'body' => (object) array(
          +            'play' => 'football'
          +        ),
          +        'ttl'  => 2 * Datetime::DAY
          +    ),
          +    array(
          +        'body' => (object) array(
          +            'play' => 'tennis'
          +        ),
          +        'ttl'  => 50 * Datetime::HOUR
          +    )
          +);
          +
          +$queue->createMessages($messages);
          +
          +
          +
          +
          +
          +
          +

          4. Get messages

          +
          +

          4.1 Description

          +

          This operation gets the message or messages in the specified queue.

          +

          Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations.

          +

          Results are ordered by age, oldest message first.

          +
          +
          +

          4.2 Parameters

          +

          A hash of options.

          + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          NameStyleTypeDescription
          markerQueryStringSpecifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional.
          limitQueryIntegerWhen more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the “next” field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional.
          echoQueryBooleanDetermines whether the API returns a client’s own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client’s own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional.
          include_claimedQuery​BooleanDetermines whether the API returns claimed messages and unclaimed messages. The include_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include_claimed uses the default value of false (only unclaimed messages are returned). Optional.
          +
          +
          +

          4.3 Code sample

          +
          $messages = $queue->listMessages(array(
          +    'marker' => '51db6f78c508f17ddc924357',
          +    'limit'  => 20,
          +    'echo'   => true
          +));
          +
          +while ($message = $messages->next()) {
          +    echo $message->getId() . PHP_EOL;
          +}
          +
          +
          +
          +
          +
          +

          5. Get a set of messages by ID

          +
          +

          5.1 Description

          +

          This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned.

          +
          +
          +

          5.2 Parameters

          +

          A hash of options.

          +

          |Name|Style|Type|Description| |—-|—–|—-|———–| +|ids|Query|String|Specifies the IDs of the messages to get. Format +multiple message ID values by separating them with commas +(comma-separated). Optional.| |claim_id|Query|​String|Specifies +the claim ID with which the message is associated. Optional.| +|—-|—–|—-|———–|

          +
          +
          +

          5.3 Code sample

          +
          $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6');
          +
          +$messages = $queue->listMessages(array('ids' => $ids));
          +
          +while ($message = $messages->next()) {
          +    echo $message->getId() . PHP_EOL;
          +}
          +
          +
          +
          +
          +
          +

          6. Delete a set of messages by ID

          +
          +

          6.1 Description

          +

          This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted.

          +
          +
          +

          6.2 Parameters

          +

          An array of IDs.

          +
          +
          +

          6.3 Code sample

          +
          $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6');
          +
          +$response = $queue->deleteMessages($ids);
          +
          +
          +
          +
          +
          +

          7. Get a specific message

          +
          +

          7.1 Description

          +

          This operation gets the specified message from the specified queue.

          +
          +
          +

          7.2 Parameters

          +

          Message ID.

          +
          +
          +

          7.3 Object properties

          +

          href is an opaque relative URI that the client can use to uniquely +identify a message resource and interact with it.

          +

          ttl is the TTL that was set on the message when it was posted. The +message expires after (ttl - age) seconds.

          +

          age is the number of seconds relative to the server’s clock.

          +

          body is the arbitrary document that was submitted with the original +request to post the message.

          +
          +
          +

          7.4 Code sample

          +
          $message = $queue->getMessage('51db6f78c508f17ddc924357');
          +
          +
          +
          +
          +
          +

          8. Delete message

          +
          +

          8.1 Description

          +

          This operation immediately deletes the specified message.

          +
          +
          +

          8.2 Parameters

          +

          None.

          +
          +
          +

          8.3 Code sample

          +
          $message->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/Queue.md.html b/doc/_build/html/services/queues/Queue.md.html new file mode 100644 index 000000000..56da5f483 --- /dev/null +++ b/doc/_build/html/services/queues/Queue.md.html @@ -0,0 +1,353 @@ + + + + + + + + + + 1. Introduction — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          1. Introduction

          +

          A Queue is an entity that holds messages. Ideally, a queue is created +per work type. For example, if you want to compress files, you would +create a queue dedicated to this job. Any application that reads from +this queue would only compress files.

          +
          +
          +

          2. Setup

          +
          $service = $client->queuesService('cloudQueues', 'ORD');
          +
          +
          +
          +
          +

          3. Client IDs

          +

          With most of Marconi’s operation, you must specify a Client ID which +will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string.

          +
          $service->setClientId();
          +
          +echo $service->getClientId();
          +
          +
          +

          If you call setClientId without any parameters, a UUID is +automatically generated for you.

          +
          +
          +

          4. List queues

          +
          +

          4.1 Description

          +

          This operation lists queues for the project. The queues are sorted +alphabetically by name.

          +
          +
          +

          4.2 Parameters

          +

          |Name|Style|Type|Description| |—-|—–|—-|———–| +|marker|Query|​String|Specifies the name of the last queue received +in a previous request, or none to get the first page of results. +Optional.| |limit|Query|Integer|Specifies the number of queues to +return. The default value for the number of queues returned is 10. If +you do not specify this parameter, the default number of queues is +returned. Optional.| |detailed|Query|​Boolean|Determines whether +queue metadata is included in the response. The default value for this +parameter is false, which excludes the metadata. Optional.| +|—-|—–|—-|———–|

          +
          +
          +

          4.3 Code sample

          +
          $queues = $service->listQueues();
          +
          +while ($queue = $queues->next()) {
          +    echo $queue->getName() . PHP_EOL;
          +}
          +
          +
          +
          +
          +
          +

          5. Create queue

          +
          +

          5.1 Description

          +

          This operation creates a new queue.

          +
          +
          +

          5.2 Parameters

          +

          A string representation of the name for your new Queue. The name must +not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens.

          +
          +
          +

          5.3 Code sample

          +
          $queue = $service->createQueue('new_queue');
          +
          +
          +
          +
          +
          +

          6. Retrieve queue

          +
          +

          6.1 Description

          +

          Returns a Queue object for use.

          +
          +
          +

          6.2 Parameters

          +

          Queue name.

          +
          +
          +

          6.3 Code sample

          +
          $queue = $service->getQueue('new_queue');
          +
          +
          +
          +
          +
          +

          7. Check queue existence

          +
          +

          7.1 Description

          +

          This operation verifies whether the specified queue exists by returning +TRUE or FALSE.

          +
          +
          +

          7.2 Parameters

          +
          +
          +

          7.3 Code sample

          +
          if ($service->hasQueue('new_queue')) {
          +    // do something
          +}
          +
          +
          +
          +
          +
          +

          8. Update queue metadata (permanently to the API)

          +
          +

          4.1 Description

          +

          This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to append metadata, ensure you merge a new +array to the existing values.

          +
          +
          +

          4.2 Parameters

          +

          Hash of key pairs.

          +
          +
          +

          4.3 Code sample

          +
          $queue->saveMetadata(array(
          +    'foo' => 'bar'
          +));
          +
          +
          +
          +
          +
          +

          9. Retrieve the queue metadata (fresh from the API)

          +
          +

          4.1 Description

          +

          This operation returns metadata, such as message TTL, for the queue.

          +
          +
          +

          4.2 Parameters

          +

          None.

          +
          +
          +

          4.3 Code sample

          +
          $metadata = $queue->retrieveMetadata();
          +
          +print_r($metadata->toArray());
          +
          +
          +
          +
          +
          +

          10. Get queue stats

          +
          +

          4.1 Description

          +

          This operation returns queue statistics, including how many messages are +in the queue, categorized by status.

          +
          +
          +

          4.2 Parameters

          +

          None.

          +
          +
          +

          4.3 Code sample

          +
          $queue->getStats();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/claims.html b/doc/_build/html/services/queues/claims.html new file mode 100644 index 000000000..6ed6cb8f8 --- /dev/null +++ b/doc/_build/html/services/queues/claims.html @@ -0,0 +1,392 @@ + + + + + + + + + + Claims — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Claims

          +
          +

          Setup

          +

          In order to work with messages, you must first retrieve a queue by its name:

          +
          $queue = $service->getQueue('{queueName}');
          +
          +
          +
          +
          +

          Claim messages

          +

          This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +204 No Content message.

          +

          When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it.

          +

          The age given for a claim is relative to the server’s clock. The claim’s +age is useful for determining how quickly messages are getting processed +and whether a given message’s claim is about to expire.

          +

          When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message.

          +
          +

          Parameters

          +

          The ttl attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request.

          +

          The grace attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted.

          +

          The limit attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional.

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$queue->claimMessages(array(
          +    'limit' => 15,
          +    'grace' => 5 * Datetime::MINUTE,
          +    'ttl'   => 5 * Datetime::MINUTE
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +
          +

          Query claim

          +

          This operation queries the specified claim for the specified queue. Claims with +malformed IDs or claims that are not found by ID are ignored.

          +
          $claim = $queue->getClaim('{claimId}');
          +
          +
          +
          +
          +

          Update claim

          +

          This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored.

          +

          Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific Claim and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL.

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$claim->update(array(
          +    'ttl' => 10 * Datetime::MINUTE
          +));
          +
          +
          +
          +
          +

          Release claim

          +

          This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored.

          +

          This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers.

          +
          $message->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/index.html b/doc/_build/html/services/queues/index.html new file mode 100644 index 000000000..207f9661b --- /dev/null +++ b/doc/_build/html/services/queues/index.html @@ -0,0 +1,418 @@ + + + + + + + + + + Queues v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Queues v1

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Queues service

          +

          Now to instantiate the Queues service:

          +
          $service = $client->queuesService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          claim
          +
          A Claim is the process of a worker checking out a message to perform +a task. Claiming a message prevents other workers from attempting to +process the same messages.
          +
          queue
          +
          A Queue is an entity that holds messages. Ideally, a queue is created +per work type. For example, if you want to compress files, you would +create a queue dedicated to this job. Any application that reads from +this queue would only compress files.
          +
          message
          +
          A Message is a task, a notification, or any meaningful data that a +producer or publisher sends to the queue. A message exists until it is +deleted by a recipient or automatically by the system based on a TTL +(time-to-live) value.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/messages.html b/doc/_build/html/services/queues/messages.html new file mode 100644 index 000000000..5fb2b69e8 --- /dev/null +++ b/doc/_build/html/services/queues/messages.html @@ -0,0 +1,463 @@ + + + + + + + + + + Messages — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Messages

          +
          +

          Setup

          +

          In order to work with messages, you must first retrieve a queue by its name:

          +
          $queue = $service->getQueue('{queueName}');
          +
          +
          +
          +
          +

          Post new message

          +

          This operation posts the specified message or messages. You can submit +up to 10 messages in a single request.

          +

          When posting new messages, you specify only the body and ttl for +the message. The API will insert metadata, such as ID and age.

          +

          How you pass through the array structure depends on whether you are +executing multiple or single posts, but the keys are the +same:

          +
            +
          • The body attribute specifies an arbitrary document that constitutes +the body of the message being sent. The size of this body is limited to +256 KB, excluding whitespace.
          • +
          • The ttl attribute specifies how long the server waits before marking +the message as expired and removing it from the queue. The value of ttl +must be between 60 and 1209600 seconds (14 days). Note that the server +might not actually delete the message until its age has reached up to +(ttl + 60) seconds, to allow for flexibility in storage implementations.
          • +
          +
          +

          Posting a single message

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$queue->createMessage(array(
          +    'body' => (object) array(
          +        'event'    => 'BackupStarted',
          +        'deadline' => '26.12.2013',
          +    ),
          +    'ttl'  => 2 * Datetime::DAY
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Post a batch of messages

          +

          Please note that the list of messages will be truncated at 10. For more, +please execute another method call.

          +
          use OpenCloud\Common\Constants\Datetime;
          +
          +$messages = array(
          +    array(
          +        'body' => (object) array(
          +            'play' => 'football'
          +        ),
          +        'ttl'  => 2 * Datetime::DAY
          +    ),
          +    array(
          +        'body' => (object) array(
          +            'play' => 'tennis'
          +        ),
          +        'ttl'  => 50 * Datetime::HOUR
          +    )
          +);
          +
          +$queue->createMessages($messages);
          +
          +
          +
          +
          +
          +

          Get messages

          +

          This operation gets the message or messages in the specified queue.

          +

          Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations.

          +

          Results are ordered by age, oldest message first.

          +
          +

          Parameters

          +

          When retrieving messages, you can filter using these options:

          +
          $messages = $queue->listMessages(array(
          +    'marker' => '51db6f78c508f17ddc924357',
          +    'limit'  => 20,
          +    'echo'   => true
          +));
          +
          +foreach ($messages as $message) {
          +    echo $message->getId() . PHP_EOL;
          +}
          +
          +
          +
          +
          +
          +

          Get a set of messages by ID

          +

          This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned.

          +
          +

          Parameters

          +
          $ids = array('id_1', 'id_2');
          +
          +$messages = $queue->listMessages(array('ids' => $ids));
          +
          +foreach ($messages as $message) {
          +    echo $message->getId() . PHP_EOL;
          +}
          +
          +
          +
          +
          +
          +

          Delete a set of messages by ID

          +

          This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted.

          +
          $ids = array('id_1', 'id_2');
          +$response = $queue->deleteMessages($ids);
          +
          +
          +
          +
          +

          Get a specific message

          +

          This operation gets the specified message from the specified queue.

          +
          /** @var $message OpenCloud\Queues\Message */
          +$message = $queue->getMessage('{messageId}');
          +
          +
          +

          Once you have access to the Message object, you access its attributes:

          + +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
          attributemethoddescription
          hrefgetHrefAn opaque relative URI that the client can use to uniquely identify a message resource and interact with it.
          ttlgetTtlThe TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds.
          agegetAgeThe number of seconds relative to the server’s clock.
          bodygetBodyThe arbitrary document that was submitted with the original request to post the message.
          +
          +
          +

          Delete message

          +
          $message->delete();
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/queues/queues.html b/doc/_build/html/services/queues/queues.html new file mode 100644 index 000000000..207655b6d --- /dev/null +++ b/doc/_build/html/services/queues/queues.html @@ -0,0 +1,390 @@ + + + + + + + + + + Queues — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Queues

          +
          +

          A note on Client IDs

          +

          For most of the operations in Cloud Queues, you must specify a Client ID +which will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string.

          +
          $service->setClientId();
          +
          +echo $service->getClientId();
          +
          +
          +

          If you call setClientId without any parameters, a UUID is +automatically generated for you.

          +
          +
          +

          List queues

          +

          This operation lists queues for the project. The queues are sorted alphabetically by name.

          +
          $queues = $service->listQueues();
          +
          +foreach ($queues as $queue) {
          +    echo $queue->getName() , PHP_EOL;
          +}
          +
          +
          +
          +

          Filtering lists

          +

          You can also filter collections using the following query parameters:

          +
          $queues = $service->listQueues(array('detailed' => false));
          +
          +
          +
          +
          +
          +

          Create queue

          +

          The only parameter required is the name of the queue you’re creating. The name +must not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens.

          +
          $queue = $service->createQueue('new_queue');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Find queue details

          +
          /** @var $queue OpenCloud\Queues\Resource\Queues */
          +$queue = $service->getQueue('{name}');
          +
          +
          +
          +
          +

          Check queue existence

          +

          This operation verifies whether the specified queue exists by returning +TRUE or FALSE.

          +
          if ($service->hasQueue('new_queue')) {
          +    // do something
          +}
          +
          +
          +
          +
          +

          Update queue metadata

          +

          This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to append metadata, ensure you merge a new +array to the existing values.

          +
          $queue->saveMetadata(array(
          +    'foo' => 'bar'
          +));
          +
          +
          +
          +
          +

          Retrieve the queue metadata

          +

          This operation returns metadata, such as message TTL, for the queue.

          +
          $metadata = $queue->retrieveMetadata();
          +print_r($metadata->toArray());
          +
          +
          +
          +
          +

          Get queue stats

          +

          This operation returns queue statistics, including how many messages are +in the queue, categorized by status.

          +
          $queue->getStats();
          +
          +
          +
          +
          +

          Delete queue

          +
          $queue->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/volume/index.html b/doc/_build/html/services/volume/index.html new file mode 100644 index 000000000..2200448ee --- /dev/null +++ b/doc/_build/html/services/volume/index.html @@ -0,0 +1,388 @@ + + + + + + + + + + Volumes v1 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Volumes v1

          +
          +

          Setup

          +
          +

          Rackspace setup

          +

          The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key:

          +
          use OpenCloud\Rackspace;
          +
          +$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
          +  'username' => '{username}',
          +  'apiKey'   => '{apiKey}',
          +));
          +
          +
          +
          +
          +

          OpenStack setup

          +

          If you’re an OpenStack user, you will also need to prove a few other +configuration parameters:

          +
          $client = new OpenCloud\OpenStack('{keystoneUrl}', array(
          +  'username' => '{username}',
          +  'password' => '{apiKey}',
          +  'tenantId' => '{tenantId}',
          +));
          +
          +
          +
          +
          +

          Volume service

          +

          Now to instantiate the Volume service:

          +
          $service = $client->volumeService('{catalogName}', '{region}', '{urlType}');
          +
          +
          +
            +
          • {catalogName} is the name of the service as it appears in the service +catalog. OpenStack users must set this value. For Rackspace users, a +default will be provided if you pass in null.
          • +
          • {region} is the region the service will operate in. For Rackspace +users, you can select one of the following from the supported regions page.
          • +
          • {urlType} is the type of URL to use, depending on which +endpoints your catalog provides. If omitted, it will default to the public +network.
          • +
          +
          +
          + +
          +

          Glossary

          +
          +
          volume
          +
          A volume is a detachable block storage device. You can think of it as a USB +hard drive. It can only be attached to one instance at a time.
          +
          volume type
          +
          Providers may support multiple types of volumes; at Rackspace, a volume +can either be SSD (solid state disk: expensive, high-performance) or +SATA (serial attached storage: regular disks, less expensive).
          +
          snapshot
          +
          A snapshot is a point-in-time copy of the data contained in a volume.
          +
          +
          + +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/volume/snapshots.html b/doc/_build/html/services/volume/snapshots.html new file mode 100644 index 000000000..5c330bf09 --- /dev/null +++ b/doc/_build/html/services/volume/snapshots.html @@ -0,0 +1,338 @@ + + + + + + + + + + Snapshots — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Snapshots

          +
          +

          Create a snapshot

          +

          A Snapshot object is created from the Cloud Block Storage service. +However, it is associated with a volume, and you must specify a volume +to create one:

          +
          // New instance of OpenCloud\Volume\Resource\Snapshot
          +$snapshot = $service->snapshot();
          +
          +// Send to API
          +$snapshot->create(array(
          +    'display_name' => 'Name that snapshot',
          +    'volume_id'    => $volume->id
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List snapshots

          +
          $snapshots = $service->snapshotList();
          +
          +foreach ($snapshots as $snapshot) {
          +    /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          To get details on a single snapshot

          +
          $snapshot = $dallas->snapshot('{snapshotId}');
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          To delete a snapshot

          +
          $snapshot->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/volume/volume-types.html b/doc/_build/html/services/volume/volume-types.html new file mode 100644 index 000000000..d6cdd1443 --- /dev/null +++ b/doc/_build/html/services/volume/volume-types.html @@ -0,0 +1,320 @@ + + + + + + + + + + Volume Types — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Volume Types

          +
          +

          List volume types

          +
          $volumeTypes = $service->volumeTypeList();
          +
          +foreach ($volumeTypes as $volumeType) {
          +    /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */
          +}
          +
          +
          +
          +
          +

          Describe a volume type

          +

          If you know the ID of a volume type, use the volumeType method to +retrieve information on it:

          +
          $volumeType = $service->volumeType(1);
          +
          +
          +

          A volume type has three attributes:

          +
            +
          • id the volume type identifier
          • +
          • name its name
          • +
          • extra_specs additional information for the provider
          • +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/services/volume/volumes.html b/doc/_build/html/services/volume/volumes.html new file mode 100644 index 000000000..f6bfbbd24 --- /dev/null +++ b/doc/_build/html/services/volume/volumes.html @@ -0,0 +1,364 @@ + + + + + + + + + + Volumes — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Volumes

          +
          +

          Create a volume

          +

          To create a volume, you must specify its size (in gigabytes). All other +parameters are optional:

          +
          // Create instance of OpenCloud\Volume\Resource\Volume
          +$volume = $service->volume();
          +
          +$volume->create(array(
          +    'size'                => 200,
          +    'volume_type'         => $service->volumeType('<volume_type_id>'),
          +    'display_name'        => 'My Volume',
          +    'display_description' => 'Used for large object storage'
          +));
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          List volumes

          +
          $volumes = $service->volumeList();
          +
          +foreach ($volumes as $volume) {
          +    /** @param $volumeType OpenCloud\Volume\Resource\Volume */
          +}
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Get details on a single volume

          +

          If you specify an ID on the volume() method, it retrieves +information on the specified volume:

          +
          $volume = $dallas->volume('<volume_id>');
          +echo $volume->size;
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          To delete a volume

          +
          $volume->delete();
          +
          +
          +

          Get the executable PHP script for this example

          +
          +
          +

          Attach a volume to a server

          +
          // retrieve server
          +$computeService = $client->computeService('{catalogName}', '{region}');
          +$server = $computeService->server('{serverId}');
          +
          +// attach volume
          +$server->attachVolume($volume, '{mountPoint}')
          +
          +
          +

          The {mountPoint} is the location on the server on which to mount +the volume (usually /dev/xvhdd or similar). You can also supply +'auto' as the mount point, in which case the mount point will be +automatically selected for you. auto is the default value for +{mountPoint}, so you do not actually need to supply anything for +that parameter.

          +
          +
          +

          Detach a volume from a server

          +
          $server->detachVolume($volume);
          +
          +
          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/url-types.html b/doc/_build/html/url-types.html new file mode 100644 index 000000000..423c13076 --- /dev/null +++ b/doc/_build/html/url-types.html @@ -0,0 +1,303 @@ + + + + + + + + + + URL types — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          URL types

          +
          +

          internalURL

          +

          An internal URL is a URL that is accessible only from within the Rackspace +Cloud network. Access to an internal URL is always free of charge.

          +
          +
          +

          publicURL

          +

          A public URL is a URL that is accessible from anywhere. Access to a public URL +usually incurs traffic charges.

          +
          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/_build/html/using-php-5.3.html b/doc/_build/html/using-php-5.3.html new file mode 100644 index 000000000..0a142de39 --- /dev/null +++ b/doc/_build/html/using-php-5.3.html @@ -0,0 +1,294 @@ + + + + + + + + + + Using the SDK with PHP v5.3 — php-opencloud 1.12.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          + + + + +
          + + + + + + +
          +
          +
          + +
          +
          +
          + +
          +

          Using the SDK with PHP v5.3

          +

          Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this.

          +

          The first way is by requiring it through the command line:

          +
          composer require rackspace/php-opencloud:1.12
          +
          +
          +

          The second way is by updating your composer.json file, and specifying the appropriate version of the SDK:

          +
          "require": {
          +  "rackspace/php-opencloud": "~1.12"
          +}
          +
          +
          +

          Note that 1.12 is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4.

          +
          + + +
          + +
          +
          + +
          + +
          + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3d4b02cd2..c2aeed2d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -26,8 +26,8 @@ - - + + From 049ea958c70d9097ea4c2cec0b34e2ba0655fd03 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 13 Jan 2015 12:36:20 +0100 Subject: [PATCH 530/835] Tweaks based on code review --- .../LoadBalancer/Collection/LoadBalancerIterator.php | 8 ++------ lib/OpenCloud/LoadBalancer/Service.php | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php index d6326048e..7febf1af3 100644 --- a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -30,12 +30,8 @@ public function parseResponseBody($body) $response = parent::parseResponseBody($body); if (count($response) >= $this->getOption('limit.page')) { - // Save last element (we will need it for the next marker) - $this->nextElement = end($response); - - // Since we previously asked for n+1 elements, pop the unwanted element - array_pop($response); - reset($response); + // Pop last element and save (we will need it for the next marker) + $this->nextElement = array_pop($response); } return $response; diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 6f5052f2c..7f8d01f74 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -63,7 +63,7 @@ public function loadBalancerList($detail = true, array $filter = array()) $url->addPath(Resource\LoadBalancer::resourceName()); $url->setQuery($filter); - $options += array('baseUrl' => $url, 'key.marker' => 'id'); + $options = array_merge($options, array('baseUrl' => $url, 'key.marker' => 'id')); return LoadBalancerIterator::factory($this, $options); } From d290f7bc7c51c6db7f584873b86d52437ee420b7 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 13 Jan 2015 17:28:57 -0800 Subject: [PATCH 531/835] Add comment to clarify intent of code. --- lib/OpenCloud/Common/Service/CatalogItem.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/OpenCloud/Common/Service/CatalogItem.php b/lib/OpenCloud/Common/Service/CatalogItem.php index b55274762..5d0e44d1a 100644 --- a/lib/OpenCloud/Common/Service/CatalogItem.php +++ b/lib/OpenCloud/Common/Service/CatalogItem.php @@ -141,6 +141,8 @@ public function getEndpoints() public function getEndpointFromRegion($region) { foreach ($this->endpoints as $endpoint) { + // Return the endpoint if it is regionless OR if the endpoint's + // region matches the $region supplied by the caller. if (!isset($endpoint->region) || $endpoint->region == $region) { return $endpoint; } From f1ace545e617ab6d5ceabd28e2645d0796a65201 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 14 Jan 2015 03:41:21 -0800 Subject: [PATCH 532/835] Clarifying where to place autoloader require statement. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b05d1d58..42948e8c3 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,15 @@ php composer.phar require rackspace/php-opencloud ``` Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): +namespaces). To do this, place the following line of PHP code at the top of your application's PHP files: ```php require 'vendor/autoload.php'; ``` +**Note**: this assumes your application's PHP files are located in the same folder as `vendor/`. If your files are located +elsewhere, please supply the path to `vendor/autoload.php` in the `require` statement above. + And you're ready to go! You can also check out the [Getting Started guide](docs/getting-started.md) for a quick tutorial. From ed2d72de02085e964903efb7592c6d2334bec603 Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 14 Jan 2015 12:53:14 -0500 Subject: [PATCH 533/835] Update documentation Document the correct returned objects. --- lib/OpenCloud/Compute/Service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Compute/Service.php b/lib/OpenCloud/Compute/Service.php index d34f6101a..bf8e4a571 100644 --- a/lib/OpenCloud/Compute/Service.php +++ b/lib/OpenCloud/Compute/Service.php @@ -102,7 +102,7 @@ public function server($id = null) * not having all the information you need. * @param array $filter - a set of key/value pairs that is passed to the * servers list for filtering - * @returns \OpenCloud\Common\Collection + * @returns \OpenCloud\Common\Collection\PaginatedIterator */ public function serverList($details = true, array $filter = array()) { @@ -128,7 +128,7 @@ public function network($id = null) * * @api * @param array $filter array of filter key/value pairs - * @return \OpenCloud\Common\Collection + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function networkList($filter = array()) { From b2cb7461abbc415fae8eea0416e8a1ef661f6aba Mon Sep 17 00:00:00 2001 From: Jeremy Pry Date: Wed, 14 Jan 2015 13:01:54 -0500 Subject: [PATCH 534/835] Change @returns to @return --- lib/OpenCloud/Compute/Service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Compute/Service.php b/lib/OpenCloud/Compute/Service.php index bf8e4a571..37d66a036 100644 --- a/lib/OpenCloud/Compute/Service.php +++ b/lib/OpenCloud/Compute/Service.php @@ -81,7 +81,7 @@ public function __construct(Client $client, $type = null, $name = null, $region * * @api * @param string $id - if specified, the server with the ID is retrieved - * @returns Resource\Server object + * @return Resource\Server object */ public function server($id = null) { @@ -102,7 +102,7 @@ public function server($id = null) * not having all the information you need. * @param array $filter - a set of key/value pairs that is passed to the * servers list for filtering - * @returns \OpenCloud\Common\Collection\PaginatedIterator + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function serverList($details = true, array $filter = array()) { From 4c1c35dbd893b6d5c6435ef78ce0a1c13ec16977 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 10:42:32 +0100 Subject: [PATCH 535/835] Fix CDN container population when iterating --- lib/OpenCloud/ObjectStore/AbstractService.php | 16 --------- lib/OpenCloud/ObjectStore/CDNService.php | 35 +++++++++++++++++++ lib/OpenCloud/ObjectStore/Service.php | 13 +++++++ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/AbstractService.php b/lib/OpenCloud/ObjectStore/AbstractService.php index 8e24c0b96..8f92874f8 100644 --- a/lib/OpenCloud/ObjectStore/AbstractService.php +++ b/lib/OpenCloud/ObjectStore/AbstractService.php @@ -29,22 +29,6 @@ abstract class AbstractService extends CatalogService const MAX_OBJECT_NAME_LEN = 1024; const MAX_OBJECT_SIZE = 5102410241025; - /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. - * - * @param array $filter - * @return Collection - */ - public function listContainers(array $filter = array()) - { - $filter['format'] = 'json'; - - $class = ($this instanceof Service) ? 'Container' : 'CDNContainer'; - - return $this->resourceList($class, $this->getUrl(null, $filter), $this); - } - /** * @return Resource\Account */ diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index e6b20cd5c..7d38c9956 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -16,6 +16,8 @@ */ namespace OpenCloud\ObjectStore; +use OpenCloud\ObjectStore\Resource\CDNContainer; +use OpenCloud\ObjectStore\Resource\ContainerMetadata; /** * This is the CDN version of the ObjectStore service. @@ -24,4 +26,37 @@ class CDNService extends AbstractService { const DEFAULT_NAME = 'cloudFilesCDN'; const DEFAULT_TYPE = 'rax:object-cdn'; + + /** + * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular + * service, normal containers are returned. + * + * @param array $filter + * @return CDNContainer + */ + public function listContainers(array $filter = array()) + { + $filter['format'] = 'json'; + return $this->resourceList('CDNContainer', $this->getUrl(null, $filter), $this); + } + + public function cdnContainer($data) + { + $container = new CDNContainer($this, $data); + + $metadata = new ContainerMetadata(); + $metadata->setArray(array( + 'Streaming-Uri' => $data->cdn_streaming_uri, + 'Ios-Uri' => $data->cdn_ios_uri, + 'Ssl-Uri' => $data->cdn_ssl_uri, + 'Enabled' => $data->cdn_enabled, + 'Ttl' => $data->ttl, + 'Log-Retention' => $data->log_retention, + 'Uri' => $data->cdn_uri, + )); + + $container->setMetadata($metadata); + + return $container; + } } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index 66735dea8..df9ffa6c8 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -66,6 +66,19 @@ public function getCdnService() return $this->cdnService; } + /** + * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular + * service, normal containers are returned. + * + * @param array $filter + * @return Container + */ + public function listContainers(array $filter = array()) + { + $filter['format'] = 'json'; + return $this->resourceList('Container', $this->getUrl(null, $filter), $this); + } + /** * @param $data * @return Container From 9880efa8b2b9d2a5e72cd9c791e20e383d6ab0e9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 10:50:13 +0100 Subject: [PATCH 536/835] Minor fixes --- lib/OpenCloud/ObjectStore/CDNService.php | 6 +++--- lib/OpenCloud/ObjectStore/Service.php | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index 7d38c9956..d45fc494d 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -16,6 +16,7 @@ */ namespace OpenCloud\ObjectStore; + use OpenCloud\ObjectStore\Resource\CDNContainer; use OpenCloud\ObjectStore\Resource\ContainerMetadata; @@ -28,11 +29,10 @@ class CDNService extends AbstractService const DEFAULT_TYPE = 'rax:object-cdn'; /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. + * List CDN-enabled containers. * * @param array $filter - * @return CDNContainer + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function listContainers(array $filter = array()) { diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index df9ffa6c8..f0e1c3dc9 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -67,11 +67,10 @@ public function getCdnService() } /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. + * List all available containers. * * @param array $filter - * @return Container + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function listContainers(array $filter = array()) { From c2beabd32a106921fa23abbb5ab9dc9bf4af5304 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 15:13:34 +0100 Subject: [PATCH 537/835] [ci skip] Adding better documentation --- docs/userguide/ObjectStore/CDN/Container.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 3d06216fc..642ee968d 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -21,7 +21,16 @@ $cdn = $container->getCdn(); ## List CDN-enabled container To list CDN-only containers, follow the same operation for Storage which lists all containers. The only difference is -which service object you execute the method on. +which service object you execute the method on: + +```php +$cdnService = $service->getCdnService(); +$cdnContainers = $cdnService->listContainers(); + +foreach ($cdnContainers as $cdnContainer) { + +} +``` ## CDN-enable and -disable a container @@ -66,4 +75,4 @@ To enable and disable logging for your CDN: ```php $cdn->enableCdnLogging(); $cdn->disableCdnLogging(); -``` \ No newline at end of file +``` From 4ce48a9d6e925c41699e235e53eeb95e030f7162 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:24:42 -0800 Subject: [PATCH 538/835] Generate and publish SDK API reference from Travis builds of master branch. --- .travis.yml | 3 +++ composer.json | 3 ++- docs/generate.sh | 50 +++++++++++++++++++++++++++++++----------------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d45afa70..fe792bec4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,6 @@ notifications: - jamie.hannaford@rackspace.com - glen.campbell@rackspace.com - shaunak.kashyap@rackspace.com + +env: + - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" \ No newline at end of file diff --git a/composer.json b/composer.json index 9c1752463..abaae2a78 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", - "fabpot/php-cs-fixer": "1.0.*@dev" + "fabpot/php-cs-fixer": "1.0.*@dev", + "apigen/apigen": "~4.0" } } diff --git a/docs/generate.sh b/docs/generate.sh index 01a43a58e..3970fe309 100644 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -1,25 +1,39 @@ -#!/bin/bash -# (c)2013 Rackspace Hosting. See COPYING for license. +#!/bin/sh -DOC_DIR=docs/api -LIB_DIR=lib -BIN_FILE=vendor/bin/apigen.php +# Script to be used by Travis CI builds to generate the php-opencloud SDK API +# reference and publish it to the gh-pages branch of the rackerlabs/php-opencloud +# repository. -if [ ! -f $BIN_FILE ]; then - rm composer.lock - php composer.phar require apigen/apigen:dev-master --dev -fi +SOURCE_DIR=lib +WORK_DIR=build/api +API_DOCS_DIR=docs/api +REPO_REMOTE_URL=https://$GH_TOKEN@github.com/rackspace/php-opencloud -if [ ! -d $DOC_DIR ]; then - mkdir $DOC_DIR +# We want our generated API reference to reflect what is +# on the master branch. So if we aren't currently on +# the master branch, or we aren't part of a PR targetted +# to the master branch, do nothing. +if [ "$TRAVIS_BRANCH" != "master" ]; then + exit 0 fi -if [ ! -d docs ]; then - echo "No docs/ directory found; run this script from the top directory" - exit; -fi +# Generate the API references +rm -rf $API_DOCS_DIR && \ +./vendor/bin/apigen generate \ + --source $SOURCE_DIR \ + --destination $WORK_DIR + +# Switch the branch to gh-pages +git checkout gh-pages + +# Commit the generated API references +rm -rf $API_DOCS_DIR +mv $WORK_DIR $API_DOCS_DIR +git add -f $API_DOCS_DIR +git commit -m "Re-generated API documentation" -rm -rf DOCS_DIR +# Push to the remote gh-pages branch so +# changes show up on php-opencloud.com +git push $REPO_REMOTE_URL gh-pages -# regenerate all the docs! -php $BIN_FILE -s $LIB_DIR -d $DOC_DIR --title="PHP OpenCloud API" --groups="namespaces" --download --progressbar \ No newline at end of file +git checkout master From 5231bd839e15e99bf9e83edfb64901295e40af14 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:46:19 -0800 Subject: [PATCH 539/835] Using an older version of apigen that works with PHP 5.3. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index abaae2a78..5fdc047a5 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,6 @@ "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", - "apigen/apigen": "~4.0" + "apigen/apigen": "~2.8" } } From de29897033cd7245589be63840d37a734dbe8207 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:59:52 -0800 Subject: [PATCH 540/835] Making generation+publish script executable. --- docs/generate.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 docs/generate.sh diff --git a/docs/generate.sh b/docs/generate.sh old mode 100644 new mode 100755 From 3d05fa845da76d13f8f36f5582aeea549a6be3ea Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:11:02 -0800 Subject: [PATCH 541/835] Pull in latest changes before making more changes. --- docs/generate.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generate.sh b/docs/generate.sh index 3970fe309..ed06cb3a6 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -25,6 +25,7 @@ rm -rf $API_DOCS_DIR && \ # Switch the branch to gh-pages git checkout gh-pages +git pull $REPO_REMOTE_URL gh-pages # Commit the generated API references rm -rf $API_DOCS_DIR From e52adf3d34cb62ce16bcbcece95470433f8f7a8a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:14:17 -0800 Subject: [PATCH 542/835] Adding error checking if the pull/merge failed. --- docs/generate.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/generate.sh b/docs/generate.sh index ed06cb3a6..a27c6e404 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -27,6 +27,10 @@ rm -rf $API_DOCS_DIR && \ git checkout gh-pages git pull $REPO_REMOTE_URL gh-pages +if [ $? -ne 0 ]; then + exit 1 +fi + # Commit the generated API references rm -rf $API_DOCS_DIR mv $WORK_DIR $API_DOCS_DIR From 310e5422d67b397bb4123af9cb381867487aa0ea Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:20:58 -0800 Subject: [PATCH 543/835] Attempt merge commit if necessary. --- docs/generate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generate.sh b/docs/generate.sh index a27c6e404..d7965bfb7 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -25,7 +25,7 @@ rm -rf $API_DOCS_DIR && \ # Switch the branch to gh-pages git checkout gh-pages -git pull $REPO_REMOTE_URL gh-pages +git pull --commit $REPO_REMOTE_URL gh-pages if [ $? -ne 0 ]; then exit 1 From 44dd0dc85f0b1ead6528bcb22a432bedf362b9f4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 23 Jan 2015 03:48:41 -0800 Subject: [PATCH 544/835] Removing unnecessary line. --- docs/generate.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/generate.sh b/docs/generate.sh index d7965bfb7..2c88f3eca 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -18,7 +18,6 @@ if [ "$TRAVIS_BRANCH" != "master" ]; then fi # Generate the API references -rm -rf $API_DOCS_DIR && \ ./vendor/bin/apigen generate \ --source $SOURCE_DIR \ --destination $WORK_DIR From 01644ed7d8ea22c6f6835eaad8367aff6acd1b77 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 23 Jan 2015 04:17:32 -0800 Subject: [PATCH 545/835] Adding apigen config file. --- apigen.neon | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 apigen.neon diff --git a/apigen.neon b/apigen.neon new file mode 100644 index 000000000..1907d65f4 --- /dev/null +++ b/apigen.neon @@ -0,0 +1,3 @@ +templateTheme: bootstrap + +accessLevels: [public] From 2a95232225b9cc4b7d97c62616cf4fc0778bd427 Mon Sep 17 00:00:00 2001 From: Glen Campbell Date: Sat, 7 Feb 2015 11:25:26 -0600 Subject: [PATCH 546/835] Docs said 'COMPLETE' but code uses 'COMPLETED' --- docs/userguide/DNS/Domains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index 827020256..f0d94efee 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -90,7 +90,7 @@ This call provides the BIND (Berkeley Internet Name Domain) 9 formatted contents ```php $asyncResponse = $domain->export(); -$body = $asyncResponse->waitFor('COMPLETE'); +$body = $asyncResponse->waitFor('COMPLETED'); echo $body['contents']; ``` From dff61a8157fb3ad66b0c7a7dfdd41bb1fa68083a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 14:20:34 +0100 Subject: [PATCH 547/835] Adding explanation for prefixing user-agents --- docs/userguide/Clients.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 70f1a8131..6f1e35075 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -116,6 +116,26 @@ To set default HTTP headers: $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); ``` +## User agents + +php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: + +> OpenCloud/xxx cURL/yyy PHP/zzz + +where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: + +```php +$client->setUserAgent('MyCustomUserAgent'); +``` + +If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: + +```php +$client->setUserAgent('MyPrefix', true); +``` + +where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. + ## 5. Other functionality For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). From f04765024e7f5161b6b1054dc90e4724e6a32a0f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 15:03:22 +0100 Subject: [PATCH 548/835] Install entire Guzzle package --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 5fdc047a5..fe720466b 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,6 @@ }, "require-dev" : { "phpunit/phpunit": "4.3.*", - "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", From 946ed07b4e10964fcaa20ab2a2152349a6b55797 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 20:25:43 +0100 Subject: [PATCH 549/835] Add example outputs --- docs/userguide/Clients.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 6f1e35075..c7270d69e 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -120,7 +120,7 @@ $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: -> OpenCloud/xxx cURL/yyy PHP/zzz +> User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: @@ -128,12 +128,20 @@ where `xxx` is the current version of the SDK, `yyy` is the current version of c $client->setUserAgent('MyCustomUserAgent'); ``` +which will result in: + +> User-Agent: MyCustomUserAgent + If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: ```php $client->setUserAgent('MyPrefix', true); ``` +which will result in: + +> User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. ## 5. Other functionality From 04fd105901a85ad73dff7277e7ae0295c3f96e84 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 25 Feb 2015 15:17:22 -0800 Subject: [PATCH 550/835] Return array of DataObjects if such is requested. --- .../ObjectStore/Resource/Container.php | 23 +++++++++++++++---- .../ObjectStore/Resource/ContainerTest.php | 14 +++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 18e541bc7..ef8f27e68 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -30,6 +30,7 @@ use OpenCloud\ObjectStore\Exception\ObjectNotFoundException; use OpenCloud\ObjectStore\Upload\DirectorySync; use OpenCloud\ObjectStore\Upload\TransferBuilder; +use OpenCloud\ObjectStore\Enum\ReturnType; /** * A container is a storage compartment for your data and provides a way for you @@ -475,7 +476,7 @@ public function uploadObject($name, $data, array $headers = array()) * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError * @return \Guzzle\Http\Message\Response */ - public function uploadObjects(array $files, array $commonHeaders = array()) + public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY) { $requests = $entities = array(); @@ -514,11 +515,23 @@ public function uploadObjects(array $files, array $commonHeaders = array()) $responses = $this->getClient()->send($requests); - foreach ($entities as $entity) { - $entity->close(); + if (ReturnType::RESPONSE_ARRAY === $returnType) { + foreach ($entities as $entity) { + $entity->close(); + } + return $responses; + } else { + // Convert responses to DataObjects before returning + $dataObjects = array(); + foreach ($responses as $index => $response) { + $dataObject = $this->dataObject() + ->populateFromResponse($response) + ->setName($files[$index]['name']) + ->setContent($entities[$index]); + $dataObjects[] = $dataObject; + } + return $dataObjects; } - - return $responses; } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 2ada2c41c..5e3171e6e 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -20,6 +20,7 @@ use Guzzle\Http\Message\Response; use OpenCloud\Common\Constants\Size; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; +use OpenCloud\ObjectStore\Enum\ReturnType; class ContainerTest extends ObjectStoreTestCase { @@ -225,6 +226,19 @@ public function test_Upload_Multiple() )); } + public function test_Upload_Multiple_Return_DataObject_Array() + { + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test', 'body' => 'FOOBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); + $this->assertEquals('test', $dataObjects[0]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[0]->getContent()); + $this->assertEquals('FOOBAR', (string) $dataObjects[0]->getContent()); + } + public function test_Upload() { $this->assertInstanceOf( From 8ee0703be4897f499814fdb69e45b16be9fef343 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 25 Feb 2015 16:35:05 -0800 Subject: [PATCH 551/835] Adding documentation. --- docs/userguide/ObjectStore/USERGUIDE.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 8bb3f3d45..fa04bdeaf 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -198,7 +198,8 @@ $localFileName = '/path/to/local/php-elephant.jpg'; $remoteFileName = 'php-elephant.jpg'; $fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); +$object = $container->uploadObject($remoteFileName, $fileData); +/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object.php) ] @@ -258,7 +259,10 @@ $objects = array( a ); -$container->uploadObjects($objects); +$responses = $container->uploadObjects($objects); +foreach ($responses as $response) { + /** @var $response \Guzzle\Http\Message\Response **/ +} ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects.php) ] @@ -266,7 +270,7 @@ In the above example, the contents of two files present on the local filesystem Instead of specifying the `path` key in an element of the `$objects` array, you can specify a `body` key whose value is a string or a stream representation. -Finally, you can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. +You can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. ``` $metadata = array('author' => 'Jane Doe'); @@ -281,6 +285,17 @@ $container->uploadObjects($objects, $allHeaders); In the example above, every object referenced within the `$objects` array will be uploaded with the same metadata. +Finally, if you want the `uploadObjects` method to return an array of `OpenCloud\ObjectStore\Resource\DataObject` objects instead of an array of `Guzzle\Http\Message\Response` objects, you can specify a third parameter to the `uploadObjects` method: + +``` +use OpenCloud\ObjectStore\Enum\ReturnType; + +$dataObjects = $container->uploadObjects($objects, $allHeaders, ReturnType::DATA_OBJECT_ARRAY); +foreach ($dataObjects as $dataObject) { + /** @var $dataObject OpenCloud\ObjectStore\Resource\DataObject **/ +} +``` + ### Large Objects If you want to upload objects larger than 5GB in size, you must use a different upload process. From 5f24bbe6395db052f7c1d8bd03f1674efe4921b1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 05:57:35 -0800 Subject: [PATCH 552/835] Adding more test data. --- .../ObjectStore/Resource/ContainerTest.php | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 5e3171e6e..e5e002eda 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -228,15 +228,38 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { - $container = $this->container; + $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); + echo $tempFileName; + try { + $tempFile = fopen($tempFileName, 'w+'); + fwrite($tempFile, 'BAZQUX'); + + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test1', 'body' => 'FOOBAR'), + array('name' => 'test2', 'path' => $tempFileName), + array('name' => 'test2', 'body' => 'BARBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + } finally { + fclose($tempFile); + unlink($tempFileName); + } - $dataObjects = $container->uploadObjects(array( - array('name' => 'test', 'body' => 'FOOBAR') - ), array(), ReturnType::DATA_OBJECT_ARRAY); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); - $this->assertEquals('test', $dataObjects[0]->getName()); + $this->assertEquals('test1', $dataObjects[0]->getName()); $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[0]->getContent()); $this->assertEquals('FOOBAR', (string) $dataObjects[0]->getContent()); + + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[1]); + $this->assertEquals('test2', $dataObjects[1]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[1]->getContent()); + $this->assertEquals('BAZQUX', (string) $dataObjects[1]->getContent()); + + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[2]); + $this->assertEquals('test2', $dataObjects[2]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[2]->getContent()); + $this->assertEquals('BARBAR', (string) $dataObjects[2]->getContent()); } public function test_Upload() From 6cdb0af0b425232e2abc968009ca0bc627141774 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:14:30 -0800 Subject: [PATCH 553/835] Adding inline documentation. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index ef8f27e68..edcea7397 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -472,9 +472,12 @@ public function uploadObject($name, $data, array $headers = array()) * `path' Path to an existing file, OR * `body' Either a string or stream representation of the file contents to be uploaded. * @param array $headers Optional headers that will be sent with the request (useful for object metadata). + * @param string $returnType One of OpenCloud\ObjectStore\Enum\ReturnType::RESPONSE_ARRAY (to return an array of + * Guzzle\Http\Message\Response objects) or OpenCloud\ObjectStore\Enum\ReturnType::DATA_OBJECT_ARRAY + * (to return an array of OpenCloud\ObjectStore\Resource\DataObject objects). * * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError - * @return \Guzzle\Http\Message\Response + * @return Guzzle\Http\Message\Response[] or OpenCloud\ObjectStore\Resource\DataObject[] depending on $returnType */ public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY) { From 997e09db9a6473bb32b14fa9115b16ba76bef68d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:14:52 -0800 Subject: [PATCH 554/835] Oops. Forgot to check-in this file! --- lib/OpenCloud/ObjectStore/Enum/ReturnType.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 lib/OpenCloud/ObjectStore/Enum/ReturnType.php diff --git a/lib/OpenCloud/ObjectStore/Enum/ReturnType.php b/lib/OpenCloud/ObjectStore/Enum/ReturnType.php new file mode 100644 index 000000000..ac9193ed4 --- /dev/null +++ b/lib/OpenCloud/ObjectStore/Enum/ReturnType.php @@ -0,0 +1,29 @@ + Date: Thu, 26 Feb 2015 06:15:57 -0800 Subject: [PATCH 555/835] Condensing to one line. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index edcea7397..0f7b0df08 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -527,11 +527,10 @@ public function uploadObjects(array $files, array $commonHeaders = array(), $ret // Convert responses to DataObjects before returning $dataObjects = array(); foreach ($responses as $index => $response) { - $dataObject = $this->dataObject() - ->populateFromResponse($response) - ->setName($files[$index]['name']) - ->setContent($entities[$index]); - $dataObjects[] = $dataObject; + $dataObjects[] = $this->dataObject() + ->populateFromResponse($response) + ->setName($files[$index]['name']) + ->setContent($entities[$index]); } return $dataObjects; } From 1928c468e5520545157ff13086230a10be8c051f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:16:09 -0800 Subject: [PATCH 556/835] Remove debugging statement. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index e5e002eda..12daa574f 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -229,7 +229,6 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); - echo $tempFileName; try { $tempFile = fopen($tempFileName, 'w+'); fwrite($tempFile, 'BAZQUX'); From 000967bda28153d79998ca27a87d12f4be9b6389 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 06:22:06 -0800 Subject: [PATCH 557/835] Adding catch block so this works with PHP 5.4. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 12daa574f..11786c4dc 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -240,6 +240,8 @@ public function test_Upload_Multiple_Return_DataObject_Array() array('name' => 'test2', 'path' => $tempFileName), array('name' => 'test2', 'body' => 'BARBAR') ), array(), ReturnType::DATA_OBJECT_ARRAY); + } catch (Exception $e) { + throw $e; } finally { fclose($tempFile); unlink($tempFileName); From bdf43035a69e2ece6e264c4ed4414d7da993176c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 07:33:35 -0800 Subject: [PATCH 558/835] Adding directive to let Travis builds finish fast. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index fe792bec4..bdd5f76af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ sudo: false matrix: allow_failures: - php: hhvm + fast_finish: true branches: only: From bce7ebe90a7c14d702f2d7c2633c1da25b4f9578 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 07:38:48 -0800 Subject: [PATCH 559/835] Duh! PHP5.4 and below don't support finally at all. Removing altogether :( --- .../ObjectStore/Resource/ContainerTest.php | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 11786c4dc..b5d2bc602 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -229,23 +229,19 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); - try { - $tempFile = fopen($tempFileName, 'w+'); - fwrite($tempFile, 'BAZQUX'); - - $container = $this->container; - - $dataObjects = $container->uploadObjects(array( - array('name' => 'test1', 'body' => 'FOOBAR'), - array('name' => 'test2', 'path' => $tempFileName), - array('name' => 'test2', 'body' => 'BARBAR') - ), array(), ReturnType::DATA_OBJECT_ARRAY); - } catch (Exception $e) { - throw $e; - } finally { - fclose($tempFile); - unlink($tempFileName); - } + + $tempFile = fopen($tempFileName, 'w+'); + fwrite($tempFile, 'BAZQUX'); + + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test1', 'body' => 'FOOBAR'), + array('name' => 'test2', 'path' => $tempFileName), + array('name' => 'test2', 'body' => 'BARBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + fclose($tempFile); + unlink($tempFileName); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); $this->assertEquals('test1', $dataObjects[0]->getName()); From 943dba6dfa83ab8d2063b7c743b5858201cedd4e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:02:14 +0100 Subject: [PATCH 560/835] Add gitignore to prevent builds from being pushed --- doc/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/.gitignore diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..69fa449dd --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +_build/ From 1a4cd18916197d8038ece4a48ebe0ede83b668cf Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:02:47 +0100 Subject: [PATCH 561/835] Add basic config and make scripts --- doc/Makefile | 177 +++++++++++++++++++++++++++++++++++++ doc/conf.py | 63 ++++++++++++++ doc/make.bat | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 doc/Makefile create mode 100644 doc/conf.py create mode 100644 doc/make.bat diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..b7805cd67 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/php-opencloud.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/php-opencloud.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/php-opencloud" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/php-opencloud" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000..7f9e29414 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# +# php-opencloud documentation build configuration file, created by +# sphinx-quickstart on Tue Mar 3 12:28:19 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'php-opencloud' +copyright = u'2015, Jamie Hannaford, Shaunak Kashyap' +version = '1.12' +release = '1.12.1' +exclude_patterns = ['_build'] +pygments_style = 'sphinx' +html_theme = 'default' + +if not on_rtd: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "_templates"] + +html_static_path = ['_static'] +html_use_index = True + +# Output file base name for HTML help builder. +htmlhelp_basename = 'php-openclouddoc' + +latex_documents = [ + ('index', 'php-opencloud.tex', u'php-opencloud Documentation', + u'Jamie Hannaford, Shaunak Kashyap', 'manual'), +] + +man_pages = [ + ('index', 'php-opencloud', u'php-opencloud Documentation', + [u'Jamie Hannaford, Shaunak Kashyap'], 1) +] + +texinfo_documents = [ + ('index', 'php-opencloud', u'php-opencloud Documentation', + u'Jamie Hannaford, Shaunak Kashyap', 'php-opencloud', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 000000000..219d9213f --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\php-opencloud.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\php-opencloud.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end From a81b6d60a9ebef5ced9932a167dfa5bf6d031def Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:03:16 +0100 Subject: [PATCH 562/835] Add initial AutoScale --- doc/services/autoscale/group-config.rst | 63 ++++++++++++++++ doc/services/autoscale/groups.rst | 90 +++++++++++++++++++++++ doc/services/autoscale/index.rst | 51 +++++++++++++ doc/services/autoscale/policies.rst | 83 +++++++++++++++++++++ doc/services/autoscale/service.sample.rst | 7 ++ doc/services/autoscale/webhooks.rst | 64 ++++++++++++++++ 6 files changed, 358 insertions(+) create mode 100644 doc/services/autoscale/group-config.rst create mode 100644 doc/services/autoscale/groups.rst create mode 100644 doc/services/autoscale/index.rst create mode 100644 doc/services/autoscale/policies.rst create mode 100644 doc/services/autoscale/service.sample.rst create mode 100644 doc/services/autoscale/webhooks.rst diff --git a/doc/services/autoscale/group-config.rst b/doc/services/autoscale/group-config.rst new file mode 100644 index 000000000..a0d7c98f7 --- /dev/null +++ b/doc/services/autoscale/group-config.rst @@ -0,0 +1,63 @@ +Group configurations +==================== + +.. contents:: + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with the functionality of a group's configuration, +you must first retrieve the details of the group itself. To do this, you must +substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get group configuration +----------------------- + +.. code-block:: php + + /** @var */ + $groupConfig = $group->getGroupConfig(); + + +Edit group configuration +------------------------ + +.. code-block:: php + + $groupConfig->update(array( + 'name' => 'New name!' + )); + + +Get launch configuration +------------------------ + +.. code-block:: php + + /** @var */ + $launchConfig = $group->getLaunchConfig(); + + +Edit group/launch configuration +------------------------------- + +.. code-block:: php + + $launchConfig = $group->getLaunchConfig(); + + $server = $launchConfig->args->server; + $server->name = "BRAND NEW SERVER NAME"; + + $launchConfig->update(array + 'args' => array( + 'server' => $server, + 'loadBalancers' => $launchConfig->args->loadBalancers + ) + )); diff --git a/doc/services/autoscale/groups.rst b/doc/services/autoscale/groups.rst new file mode 100644 index 000000000..8e781a2aa --- /dev/null +++ b/doc/services/autoscale/groups.rst @@ -0,0 +1,90 @@ +Groups +====== + +.. contents:: + + +Setup +----- + +.. include:: service.sample.rst + + +List all groups +--------------- + +.. code-block:: php + + $groups = $service->groupList(); + foreach ($group as $group) { + /** @var $group OpenCloud\Autoscale\Resources\Group */ + } + +Please consult the `iterator guide `__ for more information about +iterators. + + +Retrieve group by ID +-------------------- + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Create a new group +------------------ + +.. code-block:: php + + // Set the config object for this autoscale group; contains all of properties + // which determine its behaviour + $groupConfig = array( + 'name' => 'new_autoscale_group', + 'minEntities' => 5, + 'maxEntities' => 25, + 'cooldown' => 60, + ); + + // We need specify what is going to be launched. For now, we'll launch a new server + $launchConfig = array( + 'type' => 'launch_server', + 'args' => array( + 'server' => array( + 'flavorRef' => 3, + 'name' => 'webhead', + 'imageRef' => '0d589460-f177-4b0f-81c1-8ab8903ac7d8' + ), + 'loadBalancers' => array( + array('loadBalancerId' => 2200, 'port' => 8081), + ) + ) + ); + + // Do we want particular scaling policies? + $policy = array( + 'name' => 'scale up by 10', + 'change' => 10, + 'cooldown' => 5, + 'type' => 'webhook', + ); + + $group->create(array( + 'groupConfiguration' => $groupConfig, + 'launchConfiguration' => $launchConfig, + 'scalingPolicies' => array($policy), + )); + +Delete a group +-------------- + +.. code-block:: php + + $group->delete(); + +Get the current state of the scaling group +------------------------------------------ + +.. code-block:: php + + $group->getState(); diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst new file mode 100644 index 000000000..3077a0a2c --- /dev/null +++ b/doc/services/autoscale/index.rst @@ -0,0 +1,51 @@ +Auto Scale v2 +============= + +.. toctree:: + + groups + group-config + policies + webhooks + +Glossary +-------- + +.. glossary:: + + group + The scaling group is at the heart of an Auto Scale deployment. The scaling + group specifies the basic elements of the Auto Scale configuration. It + manages how many servers can participate in the scaling group. It also + specifies information related to load balancers if your configuration uses + a load balancer. + + group configuration + Outlines the basic elements of the Auto Scale configuration. The group + configuration manages how many servers can participate in the scaling group. + It sets a minimum and maximum limit for the number of entities that can be + used in the scaling process. It also specifies information related to load + balancers. + + launch configuration + Creates a blueprint for how new servers will be created. The launch + configuration specifies what type of server image will be started on + launch, what flavor the new server is, and which load balancer the new + server connects to. + + policy + Auto Scale uses policies to define the scaling activity that will take + place, as well as when and how that scaling activity will take place. + Scaling policies specify how to modify the scaling group and its behavior. + You can specify multiple policies to manage a scaling group. + + webhook + A webhook is a reachable endpoint that when visited will execute a scaling + policy for a particular scaling group. + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ + - `API release history `_ diff --git a/doc/services/autoscale/policies.rst b/doc/services/autoscale/policies.rst new file mode 100644 index 000000000..f5d2605f4 --- /dev/null +++ b/doc/services/autoscale/policies.rst @@ -0,0 +1,83 @@ +Scaling Policies +================ + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with the functionality of a group's scaling +policies, you must first retrieve the details of the group itself. To do this, +you must substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get all policies +---------------- + +.. code-block:: php + + $policies = $group->getScalingPolicies(); + + foreach ($policies as $policy) { + printf("Name: %s Type: %s\n", $policy->name, $policy->type); + } + + +Create new scaling policies +--------------------------- + +Creating policies is achieved through passing an array to the ``create`` +method. + +.. code-block:: php + + $policies = array( + array( + 'name' => 'NEW NAME', + 'change' => 1, + 'cooldown' => 150, + 'type' => 'webhook', + ) + ); + + $group->createScalingPolicies($policies); + + +Get an existing scaling policy +------------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + + +Update a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->update(array( + 'name' => 'More relevant name' + )); + + +Delete a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->delete(); + +Execute a scaling policy +------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->execute(); diff --git a/doc/services/autoscale/service.sample.rst b/doc/services/autoscale/service.sample.rst new file mode 100644 index 000000000..7e5d166ab --- /dev/null +++ b/doc/services/autoscale/service.sample.rst @@ -0,0 +1,7 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); diff --git a/doc/services/autoscale/webhooks.rst b/doc/services/autoscale/webhooks.rst new file mode 100644 index 000000000..68b16b0dc --- /dev/null +++ b/doc/services/autoscale/webhooks.rst @@ -0,0 +1,64 @@ +Webhooks +======== + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with webhooks, you must first retrieve the +details of the group and scaling policy you want to execute: + +.. code-block:: php + + $group = $service->group('{groupId}'); + $policy = $group->getScalingPolicy('{policyId}'); + +Get all webhooks +---------------- + +.. code-block:: php + + $webhooks = $policy->getWebookList(); + +Create a new webhook +-------------------- + +.. code-block:: php + + $policy->createWebhooks(array( + array( + 'name' => 'Alice', + 'metadata' => array( + 'firstKey' => 'foo', + 'secondKey' => 'bar' + ) + ) + )); + +Get webhook +----------- + +.. code-block:: php + + $webhook = $policy->getWebhook('{webhookId}'); + +Update webhook +-------------- + +.. code-block:: php + + // Update the metadata + $metadata = $webhook->metadata; + $metadata->thirdKey = 'blah'; + $webhook->update(array( + 'metadata' => $metadata + )); + + +Delete webhook +-------------- + +.. code-block: php + + $webhook->delete(); From e76b9042514f8197ff3649191cea981f8049c3b0 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:03:38 +0100 Subject: [PATCH 563/835] Add initial Common and Compute --- doc/services/common/os-client.sample.rst | 13 ++ doc/services/common/rs-client.sample.rst | 15 ++ doc/services/compute/flavors.rst | 66 ++++++++ doc/services/compute/images.rst | 87 ++++++++++ doc/services/compute/index.rst | 36 ++++ doc/services/compute/keypairs.rst | 71 ++++++++ doc/services/compute/servers.rst | 202 +++++++++++++++++++++++ doc/services/compute/service.sample.rst | 24 +++ 8 files changed, 514 insertions(+) create mode 100644 doc/services/common/os-client.sample.rst create mode 100644 doc/services/common/rs-client.sample.rst create mode 100644 doc/services/compute/flavors.rst create mode 100644 doc/services/compute/images.rst create mode 100644 doc/services/compute/index.rst create mode 100644 doc/services/compute/keypairs.rst create mode 100644 doc/services/compute/servers.rst create mode 100644 doc/services/compute/service.sample.rst diff --git a/doc/services/common/os-client.sample.rst b/doc/services/common/os-client.sample.rst new file mode 100644 index 000000000..b4614474d --- /dev/null +++ b/doc/services/common/os-client.sample.rst @@ -0,0 +1,13 @@ +.. code-block:: php + + '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/services/common/rs-client.sample.rst b/doc/services/common/rs-client.sample.rst new file mode 100644 index 000000000..e33983270 --- /dev/null +++ b/doc/services/common/rs-client.sample.rst @@ -0,0 +1,15 @@ +The first thing to do is pass in your credentials and instantiate a Rackspace +client: + +.. code-block:: php + + '{username}', + 'apiKey' => '{apiKey}', + )); diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst new file mode 100644 index 000000000..f79caa171 --- /dev/null +++ b/doc/services/compute/flavors.rst @@ -0,0 +1,66 @@ +Flavors +======= + +Setup +----- + +.. include:: service.sample.rst + + +Get a flavor +------------ + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + + +List flavors +------------ + +.. code-block:: php + + $flavors = $service->flavorList(); + + foreach ($flavors as $flavor) { + /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ + } + + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the ``flavorList`` method returns full details on all flavors. +However, because of the overhead involved in retrieving all the details, this +function can be slower than might be expected. To disable this feature and +keep bandwidth at a minimum, just pass ``false`` as the first argument: + +.. code-block:: php + + // Name and ID only + $compute->flavorList(false); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+================================================================================================================================================================================================+ +| minDisk | Filters the list of flavors to those with the specified minimum number of gigabytes of disk storage. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| minRam | Filters the list of flavors to those with the specified minimum amount of RAM in megabytes. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to return all flavors over 4GB in RAM: + +.. code-block:: php + + $flavors = $service->flavorList(true, array('minRam' => 4)); diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst new file mode 100644 index 000000000..b2aae4a65 --- /dev/null +++ b/doc/services/compute/images.rst @@ -0,0 +1,87 @@ +Images +====== + +.. note:: + + **Images on Rackspace servers:** with standard servers, the entire disk + (OS and data) is captured in the image. With Performance servers, only the s + ystem disk is captured in the image. The data disks should be backed up using + Cloud Backup or Cloud Block Storage to ensure availability in case you need + to rebuild or restore a server. + +Setup +----- + +.. include:: service.sample.rst + + +List images +----------- + +Below is the simplest usage for retrieving a list of images: + +.. code-block:: php + + $images = $service->imageList(); + + foreach ($images as $image) { + + } + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the only fields returned in a list call are `id` and `name`, but +you can enable more detailed information to be result by passing in `true` as +the first argument of the call, like so: + +.. code-block:: php + + $images = $service->imageList(true); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+====================================================================================================================================================================================================================================================================================================================================================+ +| server | Filters the list of images by server. Specify the server reference by ID or by full URL. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | Filters the list of images by image name. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| status | Filters the list of images by status. In-flight images have a status of ``SAVING`` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the ``OpenCloud\Compute\Constants\ImageState`` class. Images with an ``ACTIVE`` status are available for use. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| changes-since | Filters the list of images to those that have changed since the changes-since time. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| type | Filters base Rackspace images or any custom server images that you have created. Can either be ``BASE`` or ``SNAPSHOT``. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to filter images for a particular server: + +.. code-block:: php + + $images = $service->imageList(false, array('server' => '{serverId}')); + + +Retrieve details about an image +------------------------------- + +.. code-block:: php + + $image = $service->image('{imageId}'); + + +Delete an image +--------------- + +.. code-block:: php + + $image->delete(); diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst new file mode 100644 index 000000000..94c41979f --- /dev/null +++ b/doc/services/compute/index.rst @@ -0,0 +1,36 @@ +Compute v2 +========== + +.. note:: + + This is a joint service that supports both Rackspace Cloud Servers v2 API, and + OpenStack Nova v2 API. + +.. toctree:: + + images + flavors + servers + keypairs + +Glossary +-------- + +.. glossary:: + + image + An image is a collection of files for a specific operating system that you + use to create or rebuild a server. Rackspace provides prebuilt images. You + can also create custom images from servers that you have launched. + + flavor + A flavor is a named definition of certain server parameters such as + the amount of RAM and disk space available. (There are other parameters + set via the flavor, such as the amount of disk space and the number of + virtual CPUs, but a discussion of those is too in-depth for a simple + Getting Started Guide like this one.) + + server + A server is a virtual machine instance in the Cloud Servers environment. + + keypair diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst new file mode 100644 index 000000000..4040c0e65 --- /dev/null +++ b/doc/services/compute/keypairs.rst @@ -0,0 +1,71 @@ +Keypairs +======== + +Setup +----- + +.. include:: service.sample.rst + + +Generate a new keypair +---------------------- + +This operation creates a new keypair under a provided name; the public key +value is automatically generated for you. + +.. code-block:: php + + // Instantiate empty object + $keypair = $service->keypair(); + + // Send to API + $keypair->create(array( + 'name' => 'jamie_keypair_1' + )); + + // Save these! + $pubKey = $keypair->getPublicKey(); + $priKey = $keypair->getPrivateKey(); + + +Upload existing keypair +----------------------- + +This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem. + +.. code-block:: php + + $keypair = $service->keypair(); + + // $key needs to be the string content of the key file, not the filename + $content = file_get_contents('~/.ssh/id_rsa.pub'); + + $keypair->create(array( + 'name' => 'main_key', + 'publicKey' => $content + )); + +List keypairs +------------- + +To list all existing keypairs: + +.. code-block:: php + + $keys = $service->listKeypairs(); + + foreach ($keys as $key) { + + } + + +Delete keypairs +--------------- + +To delete a specific keypair: + +.. code-block:: php + + $keypair->delete(); diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst new file mode 100644 index 000000000..4d2f2363c --- /dev/null +++ b/doc/services/compute/servers.rst @@ -0,0 +1,202 @@ +Servers +======= + +Setup +----- + +.. include:: service.sample.rst + +Get server +---------- + +The easiest way to retrieve a specific server is by its unique ID: + +.. code-block:: php + + $server = $service->server('{serverId}'); + + +List servers +------------ + +You can list servers in two different ways: + +- return an *overview* of each server (ID, name and links) +- return *detailed information* for each server + +Knowing which option to use might help save unnecessary bandwidth and +reduce latency. + +.. code-block:: php + + // overview + $servers = $service->serverList(); + + // detailed + $servers = $service->serverList(true); + +URL parameters for filtering servers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| Name | Description | Type | ++==========================+====================================================================================================================================================================================================================================================================================================================+=================================================+ +| image | The image ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| flavor | The flavor ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| name | The server name | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| status | The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult ``OpenCloud\Compute\Constants\ServerState`` | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| changes-since | Value for checking for changes since a previous request | A valid ISO 8601 dateTime (2011-01-24T17:08Z) | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ + +Create server +------------- + +Using an image +~~~~~~~~~~~~~~ + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Using a bootable volume +~~~~~~~~~~~~~~~~~~~~~~~ + +Firstly we need to find our volume using their IDs. + +.. code-block:: php + + $bootableVolume = $client->volumeService()->volume('{volumeId}'); + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $response = $server->create(array( + 'name' => 'My lovely server', + 'volume' => $bootableVolume, + 'flavorId' => '{flavorId}' + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Create parameters +~~~~~~~~~~~~~~~~~ + ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| Name | Description | Type | Required | ++=============================+=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+===========+==============================+ +| name | The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync. | string | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| flavor | A populated ``OpenCloud\Compute\Resource\Flavor`` object representing your chosen flavor | object | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| image | A populated ``OpenCloud\Compute\Resource\Image`` object representing your chosen image | object | No, if volume is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volume | A populated ``OpenCloud\Volume\Resource\Volume`` object representing your chosen bootable volume | object | No, if image is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volumeDeleteOnTermination | ``true`` if the bootable volume should be deleted when the server is terminated; ``false``, otherwise | boolean | No; default = ``false`` | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| OS-DCF:diskConfig | The disk configuration value. You can use two options: ``AUTO`` or ``MANUAL``. \ ``AUTO`` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. ``AUTO`` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.\ ``MANUAL`` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration. | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| networks | An array of populated ``OpenCloud\Compute\Resource\Network`` objects that indicate which networks your instance resides in. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| metadata | An array of arbitrary data (key-value pairs) that adds additional meaning to your server. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| keypair | You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| personality | Files that you can upload to your newly created instance's filesystem. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ + +Creating a server with keypairs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'New server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'keypair' => 'main_key' + )); + +So, as you can see, you specify the **name** of an existing keypair that +you previously created on the API. + + +Creating a server with personality files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you execute the create operation, you can add "personality" files +to your ``OpenCloud\Compute\Resource\Server`` object. These files are +structured as a flat array. + +.. code-block:: php + + $server->addFile('/var/test_file', 'FILE CONTENT'); + +As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the `official +documentation `__. + +Update server +------------- + +You can update certain attributes of an existing server instance. These +attributes are detailed in the next section. + +.. code-block:: php + + $server->update(array( + 'name' => 'NEW SERVER NAME' + )); + +Updatable attributes +~~~~~~~~~~~~~~~~~~~~ + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | description | ++==============+==================================================================================================================================================+ +| name | The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv4 | The IP version 4 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv6 | The IP version 6 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Delete server +------------- + +.. code-block:: php + + $server->delete(); diff --git a/doc/services/compute/service.sample.rst b/doc/services/compute/service.sample.rst new file mode 100644 index 000000000..c684caf9a --- /dev/null +++ b/doc/services/compute/service.sample.rst @@ -0,0 +1,24 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + + +``{catalogName}`` is the **name** of the service, as it appears in the service +catalog. For Rackspace users, this will default to `cloudServersOpenStack`; for +OpenStack users, you must set your own value since it can depend on your +environment setup. + +``{region}`` is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the `supported regions page`. + +``{urlType}`` is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either `internalURL` or `publicURL`. +The former will execute HTTP transactions over the internal Rackspace network, +reducing latency and the overall bandwidth cost - the caveat is that all of your +resources must be in same region. `publicURL`, however, which is the default, +will operate over the public Internet and is to be used for multi-region +installations. From 36b6d5517bdd5a75cdc25b33ccf0b5c4be2a61e6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:00 +0100 Subject: [PATCH 564/835] Add databases and DNS --- doc/services/database/README.md.rst | 125 ++++++++++++ doc/services/database/index.rst | 0 doc/services/dns/Domains.md.rst | 290 ++++++++++++++++++++++++++++ doc/services/dns/Limits.md.rst | 70 +++++++ doc/services/dns/Records.md.rst | 111 +++++++++++ doc/services/dns/Reverse-DNS.md.rst | 96 +++++++++ doc/services/dns/Service.md.rst | 13 ++ doc/services/dns/index.rst | 0 8 files changed, 705 insertions(+) create mode 100644 doc/services/database/README.md.rst create mode 100644 doc/services/database/index.rst create mode 100644 doc/services/dns/Domains.md.rst create mode 100644 doc/services/dns/Limits.md.rst create mode 100644 doc/services/dns/Records.md.rst create mode 100644 doc/services/dns/Reverse-DNS.md.rst create mode 100644 doc/services/dns/Service.md.rst create mode 100644 doc/services/dns/index.rst diff --git a/doc/services/database/README.md.rst b/doc/services/database/README.md.rst new file mode 100644 index 000000000..3f6bdd3c2 --- /dev/null +++ b/doc/services/database/README.md.rst @@ -0,0 +1,125 @@ +Databases +========= + +A **cloud database** is a MySQL relational database service that allows +customers to programatically provision database instances of varying +virtual resource sizes without the need to maintain and/or update MySQL. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\Rackspace; + use OpenCloud\Common\Constants\State; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Create a database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $databaseService = $client->databaseService('cloudDatabases', 'DFW'); + + $twoGbFlavor = $databaseService->flavor(3); + + $dbInstance = $databaseService->instance(); + $dbInstance->name = 'Demo database instance'; + $dbInstance->volume = new stdClass(); + $dbInstance->volume->size = 20; // GB + $dbInstance->flavor = $twoGbFlavor; + $dbInstance->create(); + + $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { + + printf("Database instance build status: %s\n", $dbInstance->status); + + }); + +The example above creates a database server instance with 20GB of disk +space and 2GB of memory, then waits for it to become ACTIVE. + +3. Create a database on the database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $db = $dbInstance->database(); + $db->name = 'demo_db'; + + $db->create(); + +The example above creates a database named ``demo_db`` on the database +server instance created in the previous step. + +4. Create database user and give it access to database. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $user = $dbInstance->user(); + $user->name = 'demo_user'; + $user->password = 'h@X0r!'; + $user->databases = array('demo_db'); + + $user->create(); + +The example above creates a database user named ``demo_user``, sets its +password and gives it access to the ``demo_db`` database created in the +previous step. + +5. Optional step: Create a load balancer to allow access to the database from the Internet. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The database created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $loadBalancer->name = 'Load balancer - DB'; + $loadBalancer->addNode($dbInstance->hostname, 3306); + $loadBalancer->port = 3306; + $loadBalancer->protocol = 'MYSQL'; + $loadBalancer->addVirtualIp('PUBLIC'); + + $loadBalancer->create(); + + $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { + printf("Load balancer build status: %s\n", $lb->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/dns/Domains.md.rst b/doc/services/dns/Domains.md.rst new file mode 100644 index 000000000..824c05099 --- /dev/null +++ b/doc/services/dns/Domains.md.rst @@ -0,0 +1,290 @@ +Domains +======= + +A domain is an entity/container of all DNS-related information +containing one or more records. + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name. + +.. code:: php + + $domain = $service->domain(12345); + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the aforementioned search by using the ``name`` parameter +in a key/value array supplied as a method argument. For example, +providing ``array('name' => 'hoola.com')`` will return hoola.com and +similar names such as main.hoola.com and sub.hoola.com. + +.. code:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'hoola.com' + )); + +Filter criteria may consist of: + +- Any letter (A-Za-z) +- Numbers (0-9) +- Hyphen ("-") +- 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +If you know a domain's name, but not its unique identifier, you can do +this: + +.. code:: php + + $domains = $service->domainList(array( + 'name' => 'foo.com' + )); + + foreach ($domains as $domain) { + $id = $domain->id; + } + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. The since parameter is optional and defaults to midnight of +the current day. + +.. code:: php + + $changes = $domain->changes(); + + # Changes since last week + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + +Export domain +------------- + +This call provides the BIND (Berkeley Internet Name Domain) 9 formatted +contents of the requested domain. This call is for a single domain only, +and as such, does not traverse up or down the domain hierarchy for +details (that is, no subdomain information is provided). + +.. code:: php + + $asyncResponse = $domain->export(); + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + +Create domain +------------- + +A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` +records) and an optional list of sub-domains. You will need to specify +these before creating the domain itself: + +.. code:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + $domain->addRecord($aRecord); + + // add optional C record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add optional MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add optional NS records + $nsRecord1 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord1); + + $nsRecord2 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns2.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord2); + + // add optional subdomains + $sub1 = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($sub1); + + // send to API + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + +Clone domain +------------ + +This call will duplicate a single existing domain configuration with a +new domain name for the specified Cloud account. By default, all records +and, optionally, subdomain(s) are duplicated as well. + +The method signature you will need to use is: + +.. code:: php + + cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) + ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Data type | Default | Description | ++======================+==============+============+====================================================================================================================================================================================+ +| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +For example: + +.. code:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true); + +Import domain +------------- + +This call provisions a new DNS domain under the account specified by the +BIND 9 formatted file configuration contents defined in the request +object. + +You will need to ensure that the BIND 9 formatted file configuration +contents are valid by adhering to the following rules: + +- Each record starts on a new line and on the first column. If a record + will not fit on one line, use the BIND\_9 line continuation + convention where you put a left parenthesis and continue the one + record on the next line and put a right parenthesis when the record + ends. For example, + + example2.net. 3600 IN SOA dns1.stabletransit.com. ( + sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +- The attribute values of a record must be separated by a single blank + or tab. No other white space characters. + +- If there are any NS records, the data field should not be + dns1.stabletransit.com or dns2.stabletransit.com. They will result in + "duplicate record" errors. + +For example: + +.. code:: php + + $bind9Data = <<import($bind9Data); + +Modify domain +------------- + +This call modifies DNS domain(s) attributes only. Only the TTL, email +address and comment attributes of a domain can be modified. Records +cannot be added, modified, or removed through this API operation - you +will need to use the `add +records `__, `modify +records `__ or `remove +records `__ operations +respectively. + +.. code:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + +Remove domain +------------- + +.. code:: php + + $domain->delete(); + diff --git a/doc/services/dns/Limits.md.rst b/doc/services/dns/Limits.md.rst new file mode 100644 index 000000000..72f8219a0 --- /dev/null +++ b/doc/services/dns/Limits.md.rst @@ -0,0 +1,70 @@ +Limits +====== + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +List all limits +--------------- + +This call provides a list of all applicable limits for the specified +account. + +.. code:: php + + $limits = $service->limits(); + +Absolute limits +~~~~~~~~~~~~~~~ + +There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain: + +.. code:: php + + $absoluteLimits = $limits->absolute; + + # Domain limit + echo $absoluteLimits->domains; + + # Record limit per domain + echo $absoluteLimits->{'records per domain'}; + +List limit types +---------------- + +To find out the different limit types you can query, run: + +.. code:: php + + $limitTypes = $service->limitTypes(); + +will return: + +:: + + array(3) { + [0] => + string(10) "RATE_LIMIT" + [1] => + string(12) "DOMAIN_LIMIT" + [2] => + string(19) "DOMAIN_RECORD_LIMIT" + } + +Query a specific limit +---------------------- + +.. code:: php + + $limit = $service->limits('DOMAIN_LIMIT'); + + echo $limit->absolute->limits->value; + + >>> 500 + diff --git a/doc/services/dns/Records.md.rst b/doc/services/dns/Records.md.rst new file mode 100644 index 000000000..4e492e8ef --- /dev/null +++ b/doc/services/dns/Records.md.rst @@ -0,0 +1,111 @@ +Records +======= + +A DNS record belongs to a particular domain and is used to specify +information about the domain. + +There are several types of DNS records. Examples include mail exchange +(MX) records, which specify the mail server for a particular domain, and +name server (NS) records, which specify the authoritative name servers +for a domain. + +It is represented by the ``OpenCloud\DNS\Resource\Record`` class. +Records belong to a `Domain `__. + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('NS-1234567'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+---------------+ +| Name | Data type | Default | Description | ++============+==============+========================+===============+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+---------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+---------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+---------------+ + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record(123456); + $record->ttl -= 100; + $record->update(); + +Delete record +------------- + +.. code:: php + + $record->delete(); + diff --git a/doc/services/dns/Reverse-DNS.md.rst b/doc/services/dns/Reverse-DNS.md.rst new file mode 100644 index 000000000..b8a5c0e76 --- /dev/null +++ b/doc/services/dns/Reverse-DNS.md.rst @@ -0,0 +1,96 @@ +Reverse DNS +=========== + +DNS usually determines an IP address associated with a domain name. +Reverse DNS is the opposite process: resolving a domain name from an IP +address. This is usually achieved with a domain name pointer. + +Get PTR record +-------------- + +PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one's PTR record: + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptr = $service->ptrRecord(array( + 'parent' => $parent + )); + +So, in the above example, a ``$parent`` could be an instance of +``OpenCloud\Compute\Resource\Server`` or +``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both +implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please +consult the `server documentation <../Compute/Server.md>`__ and `load +balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +detailed usage instructions. + +List PTR records +---------------- + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptrRecords = $service->ptrRecordList($parent); + + foreach ($ptrRecords as $ptrRecord) { + + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add PTR record +-------------- + +.. code:: php + + $parent = $computeService->server('foo-server-id'); + + $ptr = $dnsService->ptrRecord(array( + 'parent' => $parent, + 'ttl' => 3600, + 'name' => 'example.com', + 'type' => 'PTR', + 'data' => '192.0.2.7' + )); + + $ptr->create(); + +Here is a table that explains the above attributes: + ++-----------+------------------------------------------------------------------------------------+------------+ +| Name | Description | Required | ++===========+====================================================================================+============+ +| type | Specifies the record type as "PTR". | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| name | Specifies the name for the domain or subdomain. Must be a valid domain name. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| data | The data field for PTR records must be a valid IPv4 or IPv6 IP address. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| ttl | If specified, must be greater than 300. Defaults to 3600 if no TTL is specified. | No | ++-----------+------------------------------------------------------------------------------------+------------+ +| comment | If included, its length must be less than or equal to 160 characters. | No | ++-----------+------------------------------------------------------------------------------------+------------+ + +Modify PTR record +----------------- + +.. code:: php + + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + +Delete PTR record +----------------- + +.. code:: php + + $ptr->delete(); + diff --git a/doc/services/dns/Service.md.rst b/doc/services/dns/Service.md.rst new file mode 100644 index 000000000..29ea79193 --- /dev/null +++ b/doc/services/dns/Service.md.rst @@ -0,0 +1,13 @@ +DNS Service +=========== + +To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the `Clients documentation <../Clients.md>`__. + +You will then need to run: + +.. code:: php + + $service = $client->dnsService(); + diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst new file mode 100644 index 000000000..e69de29bb From 091114340b7c4ad3ef24fcfe6ac4e2ff719b5dd2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:13 +0100 Subject: [PATCH 565/835] Add Identity and Images --- doc/services/identity/Roles.md.rst | 92 +++++++++++++++ doc/services/identity/Service.md.rst | 35 ++++++ doc/services/identity/Tenants.md.rst | 29 +++++ doc/services/identity/Tokens.md.rst | 105 +++++++++++++++++ doc/services/identity/Users.md.rst | 170 +++++++++++++++++++++++++++ doc/services/identity/index.rst | 0 doc/services/image/Images.md.rst | 107 +++++++++++++++++ doc/services/image/Schemas.md.rst | 170 +++++++++++++++++++++++++++ doc/services/image/Sharing.md.rst | 129 ++++++++++++++++++++ doc/services/image/Tags.md.rst | 28 +++++ doc/services/image/index.rst | 0 11 files changed, 865 insertions(+) create mode 100644 doc/services/identity/Roles.md.rst create mode 100644 doc/services/identity/Service.md.rst create mode 100644 doc/services/identity/Tenants.md.rst create mode 100644 doc/services/identity/Tokens.md.rst create mode 100644 doc/services/identity/Users.md.rst create mode 100644 doc/services/identity/index.rst create mode 100644 doc/services/image/Images.md.rst create mode 100644 doc/services/image/Schemas.md.rst create mode 100644 doc/services/image/Sharing.md.rst create mode 100644 doc/services/image/Tags.md.rst create mode 100644 doc/services/image/index.rst diff --git a/doc/services/identity/Roles.md.rst b/doc/services/identity/Roles.md.rst new file mode 100644 index 000000000..96a56488f --- /dev/null +++ b/doc/services/identity/Roles.md.rst @@ -0,0 +1,92 @@ +Roles +===== + +Intro +----- + +A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user's roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service. + +Setup +----- + +Role objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++---------------+------------------------+------------------------+ +| Property | Getter | Setter | ++===============+========================+========================+ +| id | ``getId()`` | ``setId()`` | ++---------------+------------------------+------------------------+ +| name | ``getName()`` | ``setName()`` | ++---------------+------------------------+------------------------+ +| description | ``getDescription()`` | ``setDescription()`` | ++---------------+------------------------+------------------------+ + +List roles +---------- + +This call lists the global roles available within a specified service. + +.. code:: php + + $roles = $service->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get role +-------- + +This call lists detailed information (id, name, description) for a +specified role. + +.. code:: php + + $roleId = '123abc'; + $role = $service->getRole($roleId); + +Add/delete user roles +--------------------- + +To add/remove user roles, you must first instantiate a +`user `__ object: + +.. code:: php + + $roleId = '123abc'; + + // add role to user + $user->addRole($roleId); + + // remove role from user + $user->removeRole($roleId); + +List user global roles +---------------------- + +This call returns a list of global roles associated with a user: + +.. code:: php + + $roles = $user->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Service.md.rst b/doc/services/identity/Service.md.rst new file mode 100644 index 000000000..f7e8e12ba --- /dev/null +++ b/doc/services/identity/Service.md.rst @@ -0,0 +1,35 @@ +Identity service +================ + +Intro +----- + +The Identity service is regionless, so you do not need to specify a +region when instantiating the service object. Although this was +primarily based on Rackspace's implementation of Cloud Identity, it +should also work for OpenStack Keystone. + +A note on object creation +------------------------- + +Normally, when services are created the client handles authenticates +automatically. But because Keystone/Identity is fundamental to the +authentication process itself, it proves difficult to do this procedure +as its normally done. For this reason, you have two options when +creating the service object: + +1: Use the client's factory method + +.. code:: php + + $identity = $client->identityService(); + +2: Authenticate manually + +.. code:: php + + use OpenCloud\Identity\Service as IdentityService; + + $identity = IdentityService::factory($client); + $identity->getClient()->authenticate(); + diff --git a/doc/services/identity/Tenants.md.rst b/doc/services/identity/Tenants.md.rst new file mode 100644 index 000000000..9b58efd1a --- /dev/null +++ b/doc/services/identity/Tenants.md.rst @@ -0,0 +1,29 @@ +Tenants +======= + +Intro +----- + +A tenant is a container used to group or isolate resources and/or +identity objects. Depending on the service operator, a tenant may map to +a customer, account, organization, or project. + +Setup +----- + +Tenant objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +List tenants +------------ + +.. code:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Tokens.md.rst b/doc/services/identity/Tokens.md.rst new file mode 100644 index 000000000..c42ce1573 --- /dev/null +++ b/doc/services/identity/Tokens.md.rst @@ -0,0 +1,105 @@ +Tokens +====== + +Intro +----- + +A token is an opaque string that represents an authorization to access +cloud resources. Tokens may be revoked at any time and are valid for a +finite duration. + +Setup +----- + +Token objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| Property | Description | Getter | Setter | ++============+===========================================+========================================+====================+ +| id | The unique ID of the token | ``getId()`` | ``setId()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +Request body for Rackspace's generate token operation: + +.. code:: json + + { + "auth": { + "RAX-KSKEY:apiKeyCredentials": { + "username": "foo", + "apiKey": "aaaaa-bbbbb-ccccc-12345678" + }, + "tenantId": "1100111" + } + } + +Request body for Keystone's generate token operation: + +.. code:: json + + { + "auth": { + "passwordCredentials":{ + "username":"demoauthor", + "password":"theUsersPassword" + }, + "tenantId": "12345678" + } + } + +The only real differences you'll notice is the name of the object key +(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret +(``apiKey``/``password``). The ``tenantId`` property in both templates +are optional. You can also add ``tenantName`` too. + +.. code:: php + + use OpenCloud\Common\Http\Message\Formatter; + + $template = sprintf( + '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', + 'my_username', + 'my_api_key' + ); + + $response = $service->generateToken($template); + + $body = Formatter::decode($response); + + // service catalog + $catalog = $body->access->serviceCatalog; + + // token + $token = $body->access->token; + + // user + $user = $body->access->user; + +As you will notice, these variables will be stdClass objects - for fully +fledged functionality, let the client authenticate by itself because it +ends up stocking the necessary models for you. + +To see the response body structure, consult the `official +docs `__. + +Revoke token (destroy session) +------------------------------ + +.. code:: php + + $tokenId = '1234567'; + $service->revokeToken($tokenId); + diff --git a/doc/services/identity/Users.md.rst b/doc/services/identity/Users.md.rst new file mode 100644 index 000000000..e8c2e6d63 --- /dev/null +++ b/doc/services/identity/Users.md.rst @@ -0,0 +1,170 @@ +Users +===== + +Intro +----- + +A user is a digital representation of a person, system, or service who +consumes cloud services. Users have credentials and may be assigned +tokens; based on these credentials and tokens, the authentication +service validates that incoming requests are being made by the user who +claims to be making the request, and that the user has the right to +access the requested resources. Users may be directly assigned to a +particular tenant and behave as if they are contained within that +tenant. + +Setup +----- + +User objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| Property | Description | Getter | Setter | ++=================+===============================================================================================================================================================================================================================================================================================================================+============================================+===============================================================================================================+ +| id | The unique ID for this user | ``getId()`` | ``setId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| username | Username for this user | ``getUsername()`` | ``setUsername()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| email | User's email address | ``getEmail()`` | ``setEmail()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| enabled | Whether or not this user can consume API functionality | ``getEnabled()`` or ``isEnabled()`` | ``setEnabled()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| password | Either a user-defined string, or an automatically generated one, that provides security when authenticating. | ``getPassword()`` only valid on creation | ``setPassword()`` to set local property only. To set password on API (retention), use ``updatePassword()``. | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| defaultRegion | Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region. | ``getDefaultRegion()`` | ``setDefaultRegion()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| domainId | Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account. | ``getDomainId()`` | ``setDomainId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ + +List users +---------- + +.. code:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get user +-------- + +There are various ways to get a specific user: by name, ID and email +address. + +.. code:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + // Get user by name + $user1 = $service->getUser('jamie'); + + // Get user by ID + $user2 = $service->getUser(123456, UserConst::MODE_ID); + + // Get user by email + $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); + +Create user +----------- + +There are a few things to remember when creating a user: + +- This operation is available only to users who hold the + ``identity:user-admin`` role. This admin can create a user who holds + the ``identity:default`` user role. + +- The created user **will** have access to APIs but **will not** have + access to the Cloud Control Panel. + +- Within an account, a maximum of 100 account users can be added. + +- If you attempt to add a user who already exists, an HTTP error 409 + results. + +The ``username`` and ``email`` properties are required for creating a +user. Providing a ``password`` is optional; if omitted, one will be +automatically generated and provided in the response. + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + // execute operation + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + } catch (ClientErrorResponseException $e) { + // catch 4xx HTTP errors + echo $e->getResponse()->toString(); + } + + // show generated password + echo $user->getPassword(); + +Update user +----------- + +When updating a user, specify which attribute/property you want to +update: + +.. code:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); + +Updating a user password +~~~~~~~~~~~~~~~~~~~~~~~~ + +Updating a user password requires calling a distinct method: + +.. code:: php + + $user->updatePassword('password123'); + +Delete user +----------- + +.. code:: php + + $user->delete(); + +List credentials +---------------- + +This operation allows you to see your non-password credential types for +all authentication methods available. + +.. code:: php + + $creds = $user->getOtherCredentials(); + +Get user API key +---------------- + +.. code:: php + + echo $user->getApiKey(); + +Reset user API key +------------------ + +When resetting an API key, a new one will be automatically generated for +you: + +.. code:: php + + $user->resetApiKey(); + echo $user->getApiKey(); + diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/image/Images.md.rst b/doc/services/image/Images.md.rst new file mode 100644 index 000000000..ef187f083 --- /dev/null +++ b/doc/services/image/Images.md.rst @@ -0,0 +1,107 @@ +Images +====== + +A virtual machine image is a single file which contains a virtual disk +that has an installed bootable operating system. In the Cloud Images +API, an image is represented by a JSON-encoded data structure (the image +schema) and its raw binary data (the image file). + +An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. + +Setup +----- + +You instantiate an Image object from its ``OpenCloud\Image\Service`` +class, which is available from the OpenStack/Rackspace client: + +.. code:: php + + $service = $client->imageService('cloudImages', 'IAD'); + +View the guides for more information about `clients <../Clients.md>`__ +or `services <../Services.md>`__. + +List images +----------- + +.. code:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Get image details +----------------- + +.. code:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage(''); + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and +``OpenCloud\Image\Resource\Member`` extend the +``AbstractSchemaResource`` abstract class, which offers some unique +functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a way +different than conventional getter/setter methods, and even class +properties. For this reason, they implement SPL's native +```ArrayAccess`` `__ +interface which allows you to access their state as a conventional +array: + +.. code:: php + + $image = $service->getImage(''); + + $id = $image['id']; + $tags = $image['tags']; + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +- If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +- If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code:: php + + $image->delete(); + diff --git a/doc/services/image/Schemas.md.rst b/doc/services/image/Schemas.md.rst new file mode 100644 index 000000000..8f792c4c0 --- /dev/null +++ b/doc/services/image/Schemas.md.rst @@ -0,0 +1,170 @@ +JSON schemas +============ + +The Cloud Images API supplies json documents describing the JSON-encoded +data structures that represent domain objects, so that a client knows +exactly what to expect in an API response. + +A JSON Schema is represented by the +``OpenCloud\Image\Resource\Schema\Schema`` class. + +Schema types +------------ + +There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema. + +Example response from the API +----------------------------- + +A sample response from the API, for an Images schema might be: + +.. code:: json + + { + "name": "images", + "properties": { + "images": { + "items": { + "type": "array", + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + }, + "schema": {"type": "string"}, + "next": {"type": "string"}, + "first": {"type": "string"} + }, + "links": [ + {"href": "{first}", "rel": "first"}, + {"href": "{next}", "rel": "next"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + +The top-level schema is called ``images``, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level ``images`` object. So we know that it will +take this form: + +.. code:: json + + { + "images": [something...] + } + +Within this object, we can see that it contains an array of anonymous +objects, each of which is called ``image`` and has its own set of nested +properties: + +.. code:: json + + { + "images": [ + { + [object 1...] + }, + { + [object 2...] + }, + { + [object 3...] + } + ] + } + +The structure of these nested objects are defined as another schema - +i.e. a *subschema*. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +``private`` or ``public``), etc. + +.. code:: json + + { + "images": [ + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + } + ] + } + +Each nested property of a schema is represented by the +``OpenCloud\Image\Resource\Schema\Property`` class. + +If you would like to find out more about schemas, Guzzle has good +documentation about `service +descriptions `__, +which is fairly analogous. + +JSON Patch +---------- + +The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in `RFC +6902 `__. + +Requests need to use the +``application/openstack-images-v2.1-json-patch`` content-type. + +In order for the operation to occur, the request entity body needs to +contain a very particular structure: + +:: + + [ + {"op": "replace", "path": "/name", "value": "Fedora 17"}, + {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} + ] + +The ``op`` key refers to the type of Operation (see +``OpenCloud\Image\Enum\OperationType`` for a full list). + +The ``path`` key is a JSON pointer to the document property you want to +modify or insert. JSON pointers are defined in `RFC +6901 `__. + +The ``value`` key is the value. + +Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the `official +documentation `__ +for additional information. diff --git a/doc/services/image/Sharing.md.rst b/doc/services/image/Sharing.md.rst new file mode 100644 index 000000000..b1a1ea382 --- /dev/null +++ b/doc/services/image/Sharing.md.rst @@ -0,0 +1,129 @@ +Sharing images +============== + +Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers: + ++-------------+-----------------+-----------------+ +| Operation | Producer can? | Consumer can? | ++=============+=================+=================+ +| Created | Yes | No | ++-------------+-----------------+-----------------+ +| Deleted | Yes | No | ++-------------+-----------------+-----------------+ +| Updated | No | Yes | ++-------------+-----------------+-----------------+ +| Listed | Yes | Yes | ++-------------+-----------------+-----------------+ + +The producer shares an image with the consumer by making the consumer a +*member* of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer's image list. + +Typical workflow +---------------- + +1. The producer posts the availability of specific images on a public + website. + +2. A potential consumer provides the producer with his/her tenant ID and + email address. + +3. The producer `creates a new Image Member <>`__ with the consumer's + details + +4. The producer notifies the consumer via email that the image has been + shared and provides the image's ID. + +5. If the consumer wishes the image to appear in his/her image list, the + consumer `updates their own Member status <>`__ to ``ACCEPTED``. + +Additional notes +~~~~~~~~~~~~~~~~ + +- If the consumer subsequently wishes to hide the image, the consumer + can change their Member status to ``REJECTED``. + +- If the consumer wishes to hide the image, but is open to the + possibility of being reminded by the producer that the image is + available, the consumer can change their Member status to + ``PENDING``. + +- Image producers add or remove image members, but may not modify the + member status of an image member. + +- Image consumers change their own member status, but may not add or + remove themselves as an image member. + +- Image consumers can boot from any image shared by the image producer, + regardless of the member status, as long as the image consumer knows + the image ID. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +List image members +------------------ + +This operation is available for both producers and consumers. + +.. code:: php + + $members = $image->listMembers(); + + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Create image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember($tenantId); + +Delete image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->delete(); + +Update image member status +-------------------------- + +This operation is only available for consumers. + +.. code:: php + + use OpenCloud\Images\Enum\MemberStatus; + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->updateStatus(MemberStatus::ACCEPTED); + +The acceptable states you may pass in are made available to you through +the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` +class. diff --git a/doc/services/image/Tags.md.rst b/doc/services/image/Tags.md.rst new file mode 100644 index 000000000..af5baf157 --- /dev/null +++ b/doc/services/image/Tags.md.rst @@ -0,0 +1,28 @@ +Image tags +========== + +An image tag is a string of characters used to identify a specific image +or images. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +Add image tag +------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); + diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst new file mode 100644 index 000000000..e69de29bb From 6c8c58c49f82902662a678c958ae0c9e90190b86 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:26 +0100 Subject: [PATCH 566/835] Add remaining --- doc/services/load-balancer/README.md.rst | 92 ++ doc/services/load-balancer/USERGUIDE.md.rst | 840 ++++++++++++++++ doc/services/load-balancer/index.rst | 0 doc/services/monitoring/Agents.md.rst | 202 ++++ doc/services/monitoring/Alarms.md.rst | 81 ++ doc/services/monitoring/Changelogs.md.rst | 21 + doc/services/monitoring/Checks.md.rst | 211 ++++ doc/services/monitoring/Entities.md.rst | 77 ++ doc/services/monitoring/Metrics.md.rst | 131 +++ doc/services/monitoring/Notifications.md.rst | 281 ++++++ doc/services/monitoring/Service.md.rst | 23 + doc/services/monitoring/Views.md.rst | 27 + doc/services/monitoring/Zones.md.rst | 67 ++ doc/services/monitoring/index.rst | 0 doc/services/networking/README.md.rst | 108 +++ doc/services/networking/USERGUIDE.md.rst | 582 +++++++++++ doc/services/networking/index.rst | 0 doc/services/object-store/Access.md.rst | 75 ++ doc/services/object-store/Account.md.rst | 33 + .../object-store/Container.md.cdn.rst | 90 ++ .../object-store/Container.md.storage.rst | 218 +++++ .../object-store/Migrating.md.storage.rst | 101 ++ doc/services/object-store/Object.md.cdn.rst | 25 + .../object-store/Object.md.storage.rst | 330 +++++++ doc/services/object-store/README.md.rst | 86 ++ doc/services/object-store/USERGUIDE.md.rst | 900 ++++++++++++++++++ doc/services/object-store/index.rst | 0 doc/services/orchestration/README.md.rst | 98 ++ doc/services/orchestration/USERGUIDE.md.rst | 672 +++++++++++++ doc/services/orchestration/index.rst | 0 doc/services/queues/Claim.md.rst | 164 ++++ doc/services/queues/Message.md.rst | 257 +++++ doc/services/queues/Queue.md.rst | 197 ++++ doc/services/queues/index.rst | 0 doc/services/volume/index.rst | 0 35 files changed, 5989 insertions(+) create mode 100644 doc/services/load-balancer/README.md.rst create mode 100644 doc/services/load-balancer/USERGUIDE.md.rst create mode 100644 doc/services/load-balancer/index.rst create mode 100644 doc/services/monitoring/Agents.md.rst create mode 100644 doc/services/monitoring/Alarms.md.rst create mode 100644 doc/services/monitoring/Changelogs.md.rst create mode 100644 doc/services/monitoring/Checks.md.rst create mode 100644 doc/services/monitoring/Entities.md.rst create mode 100644 doc/services/monitoring/Metrics.md.rst create mode 100644 doc/services/monitoring/Notifications.md.rst create mode 100644 doc/services/monitoring/Service.md.rst create mode 100644 doc/services/monitoring/Views.md.rst create mode 100644 doc/services/monitoring/Zones.md.rst create mode 100644 doc/services/monitoring/index.rst create mode 100644 doc/services/networking/README.md.rst create mode 100644 doc/services/networking/USERGUIDE.md.rst create mode 100644 doc/services/networking/index.rst create mode 100644 doc/services/object-store/Access.md.rst create mode 100644 doc/services/object-store/Account.md.rst create mode 100644 doc/services/object-store/Container.md.cdn.rst create mode 100644 doc/services/object-store/Container.md.storage.rst create mode 100644 doc/services/object-store/Migrating.md.storage.rst create mode 100644 doc/services/object-store/Object.md.cdn.rst create mode 100644 doc/services/object-store/Object.md.storage.rst create mode 100644 doc/services/object-store/README.md.rst create mode 100644 doc/services/object-store/USERGUIDE.md.rst create mode 100644 doc/services/object-store/index.rst create mode 100644 doc/services/orchestration/README.md.rst create mode 100644 doc/services/orchestration/USERGUIDE.md.rst create mode 100644 doc/services/orchestration/index.rst create mode 100644 doc/services/queues/Claim.md.rst create mode 100644 doc/services/queues/Message.md.rst create mode 100644 doc/services/queues/Queue.md.rst create mode 100644 doc/services/queues/index.rst create mode 100644 doc/services/volume/index.rst diff --git a/doc/services/load-balancer/README.md.rst b/doc/services/load-balancer/README.md.rst new file mode 100644 index 000000000..862cf20d2 --- /dev/null +++ b/doc/services/load-balancer/README.md.rst @@ -0,0 +1,92 @@ +Load Balancers +============== + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Retrieve the server instances you want to add as nodes of the load balancer. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); + + $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); + $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); + +The example above uses two server instances that have already been +created. It retrieves the server instances using their IDs. See also: +`creating server instances <>`__. + +3. Obtain a Load Balancer service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This object will be used to first define the load balancer nodes and +later create the load balancer itself. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +4. Define a load balancer node for each server. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + +In the example above, each node runs a service that listens on port +8080. Further, each node will start out as ``ENABLED``, which means it +will be ready to receive network traffic from the load balancer as soon +as it is created. + +5. Create the load balancer with the two nodes. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +In the example above, the load balancer will have a virtual IP address +accessible from the public Internet. Also notice that the port the load +balancer listens on (80) does not need to match the ports of its nodes +(8080). + +Next steps +---------- + +Once you have created a load balancer, there is a lot you can do with +it. See the `complete user guide for load balancers `__. diff --git a/doc/services/load-balancer/USERGUIDE.md.rst b/doc/services/load-balancer/USERGUIDE.md.rst new file mode 100644 index 000000000..788651f2b --- /dev/null +++ b/doc/services/load-balancer/USERGUIDE.md.rst @@ -0,0 +1,840 @@ +The Complete User Guide to Load Balancers +========================================= + +Prerequisites +------------- + +Client +~~~~~~ + +To use the load balancers service, you must first instantiate a +``Rackspace`` client object. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Load Balancer Service +~~~~~~~~~~~~~~~~~~~~~ + +All operations on load balancers are done via a load balancer service +object. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +Cloud Servers +~~~~~~~~~~~~~ + +Many of the examples in this document use two cloud servers as nodes for +the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer +to these two cloud servers. + +Load Balancers +-------------- + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Create Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +List Load Balancer Details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single load balancer's details by using its ID. + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer('254889'); + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $loadBalancers = $loadBalancerService->loadBalancerList(); + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + +Update Load Balancer Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +Updating a single attribute of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name' + )); + +Updating multiple attributes of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it. + +.. code:: php + + $loadBalancer->delete(); + +Nodes +----- + +A **node** is a backend device that provides a service on specified IP +and port. An example of a load balancer node might be a web server +serving HTTP traffic on port 8080. + +A load balancer typically has multiple nodes attached to it so it can +distribute incoming network traffic amongst them. + +List Nodes +~~~~~~~~~~ + +You can list the nodes attached to a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodes = $loadBalancer->nodeList(); + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + +Add Nodes +~~~~~~~~~ + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| --------------\| -------------- \| +----------------- \| +|  1 \| IP address of node \| String \| Yes \| - \| +|  2 \| Port used by node's service \| Integer \| Yes \| - \| +|  3 \| Starting condition of node: +|  4 \| Type of node to add: +|  5 \| Weight, between 1 and 100, given to node when distributing +traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the +``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| +No \| 1 \| + +Modify Nodes +~~~~~~~~~~~~ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +Modifying a single attribute of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + + $node->update(array( + 'condition' => NodeCondition::DISABLED + )); + +Modifying multiple attributes of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + +Remove Nodes +~~~~~~~~~~~~ + +There are two ways to remove a node. + +Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $node->delete(); + +Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->removeNode(490639); + +The ``removeNode`` method, as shown above, accepts the following +arguments: + ++------------+---------------+-------------+-------------+-----------------+ +| Position | Description | Data type | Required? | Default value | ++============+===============+=============+=============+=================+ +| 1 | ID of node | Integer | Yes | - | ++------------+---------------+-------------+-------------+-----------------+ + +View Node Service Events +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can view events associated with the activity between a node and a +load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } + +Virtual IPs +----------- + +A **virtual IP (VIP)** makes a load balancer accessible by clients. The +load balancing service supports either a public VIP address +(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP +address (``SERVICENET``), routable only within the region in which the +load balancer resides. + +List Virtual IPs +~~~~~~~~~~~~~~~~ + +You can list the VIPs associated with a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $vips = $loadBalancer->virtualIpList(); + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + +Add Virtual IPv6 +~~~~~~~~~~~~~~~~ + +You can add additional IPv6 VIPs to a load balancer. + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +The ``addVirtualIp`` method, as shown above, accepts the following +arguments: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| -------------- \|-------------- \| +----------------- \| +|  1 \| Type of VIP: +|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| + +Remove Virtual IPs +~~~~~~~~~~~~~~~~~~ + +You can remove a VIP from a load balancer. + +.. code:: php + + $vip->remove(); + +Please note that a load balancer must have at least one VIP associated +with it. If you try to remove a load balancer's last VIP, a +``ClientErrorResponseException`` will be thrown. + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported load balancing algorithms using a load +balancer service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $algorithms = $loadBalancerService->algorithmList(); + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols using a load balancer +service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $protocols = $loadBalancerService->protocolList(); + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + +Session Persistence +------------------- + +**Session persistence** is a feature of the load balancing service that +forces multiple requests, of the same protocol, from clients to be +directed to the same node. This is common with many web applications +that do not inherently share application state between back-end servers. + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +List Session Persistence Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistenceType = $sessionPersistence->persistenceType; + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + +Enable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + +Disable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); + +Connection Logging +------------------ + +The **connection logging** feature allows logs to be delivered to a +Cloud Files account every hour. For HTTP-based protocol traffic, these +are Apache-style access logs. For all other traffic, this is connection +and transfer logging. + +Check Logging Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $connectionLogging bool **/ + + $connectionLogging = $loadBalancer->hasConnectionLogging(); + +In the example above: + +- If connection logging is enabled, the value of ``$connectionLogging`` + is ``true``. +- If connection logging is disabled, the value of + ``$connectionLogging`` is ``false``. + +Enable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(true); + +Disable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(false); + +Error Page +---------- + +An **error page** is the html file that is shown to the end user when an +error in the service has been thrown. By default every virtual server is +provided with the default error file. It is also possible to set a +custom error page for a load balancer. + +View Error Page Content +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + +Set Custom Error Page +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + +Delete Custom Error Page +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); + +Allowed Domains +--------------- + +**Allowed domains** are a restricted set of domain names that are +allowed to add load balancer nodes. + +List Allowed Domains +~~~~~~~~~~~~~~~~~~~~ + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code:: php + + $allowedDomains = $loadBalancerService->allowedDomainList(); + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + +Access Lists +------------ + +**Access Lists** allow fine-grained network access to a load balancer's +VIP. Using access lists, network traffic to a load balancer's VIP can be +allowed or denied from a single IP address, multiple IP addresses or +entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` +network items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +View Access List +~~~~~~~~~~~~~~~~ + +You can view a load balancer's access list. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $accessList = $loadBalancer->accessList(); + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + +Add Network Items To Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can add network items to a load balancer's access list very easily: + +.. code:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + +Remove Network Item From Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You an remove a network item from a load balancer's access list. + +.. code:: php + + $networkItem->delete(); + +Content Caching +--------------- + +When **content caching** is enabled on a load balancer, +recently-accessed files are stored on the load balancer for easy +retrieval by web clients. Requests to the load balancer for these files +are serviced by the load balancer itself, which reduces load off its +back-end nodes and improves response times as well. + +Check Content Caching Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $contentCaching bool **/ + + $contentCaching = $loadBalancer->hasContentCaching(); + +In the example above: + +- If content caching is enabled, the value of ``$contentCaching`` is + ``true``. +- If content caching is disabled, the value of ``$contentCaching`` is + ``false``. + +Enable Content Caching +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(true); + +Disable Content Caching +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(false); + +SSL Termination +--------------- + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +View current SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + + $sslConfig = $loadBalancer->SSLTermination(); + +Update SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Delete SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->delete(); + +Metadata +-------- + +Metadata can be associated with each load balancer and each node for the +client's personal use. It is defined using key-value pairs where the key +and value consist of alphanumeric characters. A key is unique per load +balancer. + +List metadata +~~~~~~~~~~~~~ + +.. code:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add metadata +~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + +Modify metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + +Remove metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem->delete(); + +Monitors +-------- + +The load balancing service includes a health monitoring operation which +periodically checks your back-end nodes to ensure they are responding +correctly. If a node is not responding, it is removed from rotation +until the health monitor determines that the node is functional. In +addition to being performed periodically, the health check also is +performed against every node that is added to ensure that the node is +operating properly before allowing it to service traffic. Only one +health monitor is allowed to be enabled on a load balancer at a time. + +.. code:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Update or delete +~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + // Delete + $healthMonitor->delete(); + +Statistics +---------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect\_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load + balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive\_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + + $stats = $loadBalancer->stats(); + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } + diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/monitoring/Agents.md.rst b/doc/services/monitoring/Agents.md.rst new file mode 100644 index 000000000..943983f7e --- /dev/null +++ b/doc/services/monitoring/Agents.md.rst @@ -0,0 +1,202 @@ +Agents +====== + +Intro +----- + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Setup +----- + +.. code:: php + + $agentId = '00-agent.example.com'; + $agent = $service->getAgent($agentId); + +You can view the `service page `__ for more information +about setting up the Cloud Monitoring service. + +List agents +----------- + +.. code:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +List connections +---------------- + +.. code:: php + + $connections = $agent->getConnections(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Get connection +-------------- + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('cntl4qsIbA'); + +Agent Connection properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------+---------------+--------+---------------------------+ +| Name | Description | Type | Method | ++====================+===============+========+===========================+ +| id | - | - | ``getId()`` | ++--------------------+---------------+--------+---------------------------+ +| guid | - | - | ``getGuid()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_id | - | - | ``getAgentId()`` | ++--------------------+---------------+--------+---------------------------+ +| endpoint | - | - | ``getEndpoint()`` | ++--------------------+---------------+--------+---------------------------+ +| process\_version | - | - | ``getProcessVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| bundle\_version | - | - | ``getBundleVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_ip | - | - | ``getAgentIp()`` | ++--------------------+---------------+--------+---------------------------+ + +Agent tokens +============ + +Intro +----- + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Setup +----- + +.. code:: php + + $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; + $agentToken = $service->getAgentToken($tokenId); + +Create agent token +------------------ + +.. code:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + +List agent tokens +----------------- + +.. code:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +Update and delete Agent Token +----------------------------- + +.. code:: php + + // Update + $token->update(array( + 'label' => 'New label' + )); + + // Delete + $token->delete(); + +Agent Host Information +====================== + +Info +---- + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code:: php + + $host = $monitoringService->getAgentHost(); + +Get some metrics +---------------- + +.. code:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Info +---- + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } + diff --git a/doc/services/monitoring/Alarms.md.rst b/doc/services/monitoring/Alarms.md.rst new file mode 100644 index 000000000..2918637e9 --- /dev/null +++ b/doc/services/monitoring/Alarms.md.rst @@ -0,0 +1,81 @@ +Alarms +====== + +Info +---- + +Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (``OK``, +``WARNING`` or ``CRITICAL``) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the `reference +documentation `__. + +Setup +----- + +Alarms are sub-resources of Entities: + +.. code:: php + + $alarmId = 'alAAAA'; + $alarm = $check->getAlarm(); + +For more information about working with Checks, please see the +`appropriate documentation `__. + +Attributes +---------- + ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| Name | Description | Required? | Method | ++==========================+=============================================================================+=============+=================================+ +| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| disabled | Disable processing and alerts on this alarm | Optional | ``isDisabled()`` <``bool``\ > | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| label | A friendly label for an alarm. | Optional | ``getLabel()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + +Create Alarm +------------ + +.. code:: php + + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); + +List Alarms +----------- + +.. code:: php + + $alarms = $entity->getAlarms(); + + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } + +Update and delete Alarm +----------------------- + +.. code:: php + + // Update + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + // Delete + $alarm->delete(); + diff --git a/doc/services/monitoring/Changelogs.md.rst b/doc/services/monitoring/Changelogs.md.rst new file mode 100644 index 000000000..2e9059e26 --- /dev/null +++ b/doc/services/monitoring/Changelogs.md.rst @@ -0,0 +1,21 @@ +Changelogs +========== + +Info +---- + +The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information. + +Working with Changelogs +----------------------- + +.. code:: php + + $changelog = $service->getChangelog(); + + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } + diff --git a/doc/services/monitoring/Checks.md.rst b/doc/services/monitoring/Checks.md.rst new file mode 100644 index 000000000..0e8e902a5 --- /dev/null +++ b/doc/services/monitoring/Checks.md.rst @@ -0,0 +1,211 @@ +Checks +====== + +Info +---- + +A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information: + +- A name for the check +- The check's parent entity +- The type of check you're creating +- Details of the check +- The monitoring zones that will launch the check + +The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate). + +Setup +----- + +Checks are sub-resources of Entities: + +.. code:: php + + $checkId = 'chAAAA'; + $check = $entity->getCheck($checkId); + +Attributes +---------- + ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| Name | Description | Required? | Data type | ++============+=============================================================================================================+=============+==========================================+ +| type | The type of check. | Required | Valid check type. String (1..25 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| details | Details specific to the check type. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| disabled | Disables the check. | Optional | Boolean | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| label | A friendly label for a check. | Optional | String (1..255 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| period | The period in seconds for a check. The value must be greater than the minimum period set on your account. | Optional | Integer (30..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ + +Attributes used for remote Checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| Name | Description | Required? | Data type | ++===========================+========================================================================================================================================================+=============+============================================================+ +| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ + +Test parameters (before create) +------------------------------- + +.. code:: php + + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); + + // You can do a test to see what would happen + // if a Check is launched with these params + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + +Create a Check +-------------- + +.. code:: php + + $entity->createCheck($params); + +Test existing Check +------------------- + +.. code:: php + + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; + +List Checks +----------- + +.. code:: php + + $checks = $entity->getChecks(); + + foreach ($checks as $check) { + echo $check->getId(); + } + +Update and delete Check +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $check->update(array('period' => 500)); + + // Delete + $check->delete(); + +Check types +=========== + +Info +---- + +Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. **Note:** Users cannot create, update or delete +check types. + +Check types for commonly encountered web protocols, such as HTTP +(``remote.http``), IMAP (``remote.imap-banner``) , SMTP +(``remote.stmp``), and DNS (``remote.dns``) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(``remote.mysql-banner``) and PostgreSQL (``remote.postgresql-banner``) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the 'extract' attribute to define the format. + +In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +`official API +documentation `__. + +Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check's parameters. For example, +using the 'extract' attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided. + +Setup +----- + +If you want to see the type for an existing Check: + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + +Alternatively, you can retrieve a specific type based on its ID: + +.. code:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); + +Attributes +---------- + ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| Name | Description | Data type | Method | ++========================+==========================================================================================================================================================================================+=============+===============================+ +| type | The name of the supported check type. | String | ``getType()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| fields | Check type fields. | Array | ``getFields()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ + +List all possible check types +----------------------------- + +.. code:: php + + $checkTypes = $service->getCheckTypes(); + + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } + diff --git a/doc/services/monitoring/Entities.md.rst b/doc/services/monitoring/Entities.md.rst new file mode 100644 index 000000000..068aec122 --- /dev/null +++ b/doc/services/monitoring/Entities.md.rst @@ -0,0 +1,77 @@ + Entities +========= + +Info +---- + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Setup +----- + +.. code:: php + + $entity = $service->getEntity(); + +For more information about setting up the ``$service`` object, please +see the userguide tutorial for `services `__. + +Attributes +---------- + ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| Name | Description | Required? | Data type | Method | ++=================+=========================================================================+=============+=====================================================+========================+ +| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ + +Create Entity +------------- + +.. code:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + +Update and delete Entity +------------------------ + +.. code:: php + + // Update + $entity->update(array( + 'label' => 'New label for my entity' + )); + + // Delete + $entity->delete(); + diff --git a/doc/services/monitoring/Metrics.md.rst b/doc/services/monitoring/Metrics.md.rst new file mode 100644 index 000000000..4231cf914 --- /dev/null +++ b/doc/services/monitoring/Metrics.md.rst @@ -0,0 +1,131 @@ + Metrics +======== + +Info +---- + +When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points. + +Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time. + +Data Granularity +---------------- + +Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. + +When you fetch metrics data points, you specify several parameters to +control the granularity of data returned: + +- A time range for the points +- Either the number of points you want returned OR the resolution of + the data you want returned + +When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution. + +**Note:** Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request. + +Consider that you want to query data for a 48-hour time range between +the timestamps ``from=1354647221000`` and ``to=1358794421000`` ( +**specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970** ). The following table shows the number +of points that the API returns for a given resolution. + +Specifying resolution to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-----------------------------+-------------------------+ +| You specify resolution... | API returns points... | ++=============================+=========================+ +| FULL | 5760 | ++-----------------------------+-------------------------+ +| MIN5 | 576 | ++-----------------------------+-------------------------+ +| MIN20 | 144 | ++-----------------------------+-------------------------+ +| MIN60 | 48 | ++-----------------------------+-------------------------+ +| MIN240 | 12 | ++-----------------------------+-------------------------+ +| MIN1440 | 2 | ++-----------------------------+-------------------------+ + +Specifying number of points to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++--------------------------------------+-----------------------------+ +| You specify points in the range... | API calculates resolution | ++======================================+=============================+ +| 3168-∞ | FULL | ++--------------------------------------+-----------------------------+ +| 360-3167 | MIN5 | ++--------------------------------------+-----------------------------+ +| 96-359 | MIN20 | ++--------------------------------------+-----------------------------+ +| 30-95 | MIN60 | ++--------------------------------------+-----------------------------+ +| 7-29 | MIN240 | ++--------------------------------------+-----------------------------+ +| 0-6 | MIN1440 | ++--------------------------------------+-----------------------------+ + +Data Point Expiration +^^^^^^^^^^^^^^^^^^^^^ + +Cloud Monitoring expires data points according to the following +schedule: + ++--------------+--------------+ +| Resolution | Expiration | ++==============+==============+ +| FULL | 2 days | ++--------------+--------------+ +| MIN5 | 7 days | ++--------------+--------------+ +| MIN20 | 15 days | ++--------------+--------------+ +| MIN60 | 30 days | ++--------------+--------------+ +| MIN240 | 60 days | ++--------------+--------------+ +| MIN1440 | 365 days | ++--------------+--------------+ + + Setup +------ + +Metrics are sub-resources of Checks. For more information about working +with Checks, please see the `relevant documentation `__. + +List all metrics +---------------- + +.. code:: php + + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } + +Fetch data points +----------------- + +.. code:: php + + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); + diff --git a/doc/services/monitoring/Notifications.md.rst b/doc/services/monitoring/Notifications.md.rst new file mode 100644 index 000000000..f361e3c9c --- /dev/null +++ b/doc/services/monitoring/Notifications.md.rst @@ -0,0 +1,281 @@ +Notifications +============= + +Info +---- + +A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time. + +For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from ``OK`` -> ``CRITICAL`` and so on). + +Setup +----- + +.. code:: php + + $id = 'ntAAAA'; + $notification = $service->getNotification($id); + +Attributes +---------- + ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| Name | Description | Data type | Method | ++===========+===========================================================================+===========================================================+====================+ +| details | A hash of notification specific details based on the notification type. | Array | ``getDetails()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| label | Friendly name for the notification. | String (1..255 chars) | ``getLabel()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ + +Test parameters +--------------- + +.. code:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + // Test it + $response = $notification->testParams($params); + + if ($response->status == 'Success') { + echo $response->message; + } + +Create Notification +------------------- + +.. code:: php + + $notification->create($params); + +Test existing notification +-------------------------- + +.. code:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; + +List Notifications +------------------ + +.. code:: php + + $notifications = $service->getNotifications(); + + foreach ($notifications as $notification) { + echo $notification->getId(); + } + +Update and delete Notifications +------------------------------- + +.. code:: php + + // Update + $notification->update(array( + 'label' => 'New notification label' + )); + + // Delete + $notification->delete(); + +Notification types +================== + +Info +---- + +Pretty self-explanatory. Rackspace Cloud Monitoring currently supports +the following notification types: + +Webhook +^^^^^^^ + +Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes: + ++-----------+------------------------------------------+---------------+ +| Name | Description | Data type | ++===========+==========================================+===============+ +| address | Email address to send notifications to | Valid email | ++-----------+------------------------------------------+---------------+ + +Email +^^^^^ + +Email alerts where the message is delivered to a specified address. It +has these attributes: + ++--------+-----------------------------------+-------------+ +| Name | Description | Data type | ++========+===================================+=============+ +| url | An HTTP or HTTPS URL to POST to | Valid URL | ++--------+-----------------------------------+-------------+ + +Setup +----- + +If you've already set up a main Notification object, and want to access +functionality for this Notification's particular Notification Type, you +can access its property: + +.. code:: php + + $type = $notification->getNotificationType(); + +Alternatively, you can retrieve an independent resource using the ID: + +.. code:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); + +List all possible notification types +------------------------------------ + +.. code:: php + + $types = $service->getNotificationTypes(); + + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } + +Notification plans +================== + +Info +---- + +A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications. + +Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states: + +- Critical +- Warning +- OK + +A notification plan, ``npTechnicalContactsEmail``, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change. + +Setup +----- + +.. code:: php + + $planId = 'npAAAA'; + $plan = $service->getNotificationPlan(); + +Attributes +---------- + ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| Name | Description | Required? | Data type | Method | ++===================+====================================================================+=============+=========================+==========================+ +| label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ + +Create Notification Plan +------------------------ + +.. code:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + +Update and delete Notification Plan +----------------------------------- + +.. code:: php + + // Update + $plan->update(array( + 'label' => 'New label for my plan' + )); + + // Delete + $plan->delete(); + +Alarm Notification History +========================== + +Info +---- + +The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm's statuses. + +Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information. + + Setup +------ + +Notification History is a sub-resource of an Alarm. For more information +about working with Alarms, please consult the relevant +`documentation `__. + +Discover which Checks have a Notification History +------------------------------------------------- + +This operation list checks for which alarm notification history is +available: + +.. code:: php + + $checks = $alarm->getRecordedChecks(); + +List Alarm Notification History for a particular Check +------------------------------------------------------ + +.. code:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); + +Get a particular Notification History item +------------------------------------------ + +.. code:: php + + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + diff --git a/doc/services/monitoring/Service.md.rst b/doc/services/monitoring/Service.md.rst new file mode 100644 index 000000000..09c25c21d --- /dev/null +++ b/doc/services/monitoring/Service.md.rst @@ -0,0 +1,23 @@ +Cloud Monitoring Service +======================== + +Initializing the Cloud Monitoring is easy - and can be done in a similar +way to all other Rackspace services: + +1. Create client and pass in auth details. For more information about + creating clients, please consult the `Client + documentation <../Clients.md>`__. +2. Use the factory method, specifying additional parameters where + necessary: + +.. code:: php + + $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); + +All three parameters are optional - if not specified, it will revert to +the service's default values which are: + +- Name = ``cloudMonitoring`` +- Region = ``DFW`` +- URL type = ``publicURL`` + diff --git a/doc/services/monitoring/Views.md.rst b/doc/services/monitoring/Views.md.rst new file mode 100644 index 000000000..a5b7245fa --- /dev/null +++ b/doc/services/monitoring/Views.md.rst @@ -0,0 +1,27 @@ +Views +===== + +Info +---- + +Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint. + +List all Views +-------------- + +\`\`\`php $views = $service->getViews(); + +foreach ($views as $view) { $entity = $view->getEntity(); + +:: + + echo $view->getTimestamp(); + +} \`\`\` + +Please consult the `iterator doc `__ for +more information about iterators. diff --git a/doc/services/monitoring/Zones.md.rst b/doc/services/monitoring/Zones.md.rst new file mode 100644 index 000000000..ffe4bba1e --- /dev/null +++ b/doc/services/monitoring/Zones.md.rst @@ -0,0 +1,67 @@ +Zones +===== + +Info +---- + +A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". +It is an abstraction for a general location from which data is +collected. + +An "endpoint," also known as a "collector," collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range. + +A check references a list of monitoring zones it should be run from. + + Setup +------ + +.. code:: php + + $zoneId = 'mzAAAAA'; + $zone = $monitoringService->getMonitoringZone($zoneId); + +Attributes +---------- + ++-----------------+------------------+-----------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+==================+===================================+========================+ +| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| label | Label | String | ``getLabel()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| source\_ips | Source IP list | Array | ``getSourceIps()`` | ++-----------------+------------------+-----------------------------------+------------------------+ + + List all zones +--------------- + +.. code:: php + + $zones = $service->getMonitoringZones(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Perform a traceroute +-------------------- + +.. code:: php + + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); + + // How many hops? + echo count($traceroute); + + // What was the first hop's IP? + echo $traceroute[0]->ip; + diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/networking/README.md.rst b/doc/services/networking/README.md.rst new file mode 100644 index 000000000..605805fe1 --- /dev/null +++ b/doc/services/networking/README.md.rst @@ -0,0 +1,108 @@ +Networking +========== + +**Networking** is a service that you can use to create virtual networks +and attach cloud devices such as servers to these networks. + +Concepts +-------- + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: A network is an isolated virtual layer-2 broadcast + domain that is typically reserved for the tenant who created it + unless you configure the network to be shared. The network is the + main entity in the Networking service. Ports and subnets are always + associated with a network. + +- **Subnet**: A subnet represents an IP address block that can be used + to assign IP addresses to virtual instances (such as servers created + using the Compute service). Each subnet must have a CIDR and must be + associated with a network. + +- **Port**: A port represents a virtual switch port on a logical + network switch. Virtual instances (such as servers created using the + Compute service) attach their interfaces into ports. The port also + defines the MAC address and the IP address(es) to be assigned to the + interfaces plugged into them. When IP addresses are associated to a + port, this also implies the port is associated with a subet, as the + IP address is taken from the allocation pool for a specific subnet. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Networking service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via an *networking service object*. +To instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance will be stored in the cloud region +specified by ``$region``. + +3. Create a network. +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a network, there is more you can do with it. See +`complete user guide for networking `__. diff --git a/doc/services/networking/USERGUIDE.md.rst b/doc/services/networking/USERGUIDE.md.rst new file mode 100644 index 000000000..bfeb27774 --- /dev/null +++ b/doc/services/networking/USERGUIDE.md.rst @@ -0,0 +1,582 @@ +Complete User Guide for the Networking Service +============================================== + +Networking is a service that you can use to create virtual networks and +attach cloud devices such as servers to these networks. + +This user guide introduces you the entities in the Networking service — +networks, subnets, and ports — and shows you how to create and manage +these entities. + +Table of contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ + + - `Client <#client>`__ + - `Networking service <#networking-service>`__ + +- `Networks <#networks>`__ + + - `Create a network <#create-a-network>`__ + - `Create multiple networks <#create-multiple-networks>`__ + - `List networks <#list-networks>`__ + - `Get a network <#get-a-network>`__ + - `Update a network <#update-a-network>`__ + - `Delete a network <#delete-a-network>`__ + +- `Subnets <#subnets>`__ + + - `Create a subnet <#create-a-subnet>`__ + - `Create multiple subnets <#create-multiple-subnets>`__ + - `List subnets <#list-subnets>`__ + - `Get a subnet <#get-a-subnet>`__ + - `Update a subnet <#update-a-subnet>`__ + - `Delete a subnet <#delete-a-subnet>`__ + +- `Ports <#ports>`__ + + - `Create a port <#create-a-port>`__ + - `Create multiple ports <#create-multiple-ports>`__ + - `List ports <#list-ports>`__ + - `Get a port <#get-a-port>`__ + - `Update a port <#update-a-port>`__ + - `Delete a port <#delete-a-port>`__ + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: An isolated virtual layer-2 broadcast domain that is + typically reserved for the tenant who created it unless it is + configured to be shared. The network is the main entity in the + Networking service. Ports and subnets are always associated with a + network. + +- **Subnet**: An IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the + Compute service). Each subnet must have a CIDR and must be associated + with a network. + +- **Port**: A virtual switch port on a logical network switch. Virtual + instances (such as servers created using the Compute service) attach + their interfaces into ports. The port also defines the MAC address + and the IP address or addresses to be assigned to the interfaces + plugged into them. When IP addresses are associated with a port, this + also implies the port is associated with a subnet because the IP + address is taken from the allocation pool for a specific subnet. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate an + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Networking service +~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via a *networking service object*. To +instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes the following arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance are stored in the cloud region specified +by ``$region``. + +Networks +-------- + +A network is an isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it unless it is configured +to be shared. The network is the main entity in the Networking service. +Ports and subnets are always associated with a network. + +Create a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple networks +~~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List networks +~~~~~~~~~~~~~ + +You can list all the networks to which you have access as shown in the +following example: + +.. code:: php + + $networks = $networkingService->listNetworks(); + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a network +~~~~~~~~~~~~~ + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code:: php + + $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code:: php + + $network->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Subnets +------- + +A subnet represents an IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the +Compute service). Each subnet must have a CIDR and must be associated +with a network. + +Create a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code:: php + + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple subnets +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List subnets +~~~~~~~~~~~~ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a subnet +~~~~~~~~~~~~ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code:: php + + $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a subnet +~~~~~~~~~~~~~~~ + +You can delete a subnet as shown in the following example: + +.. code:: php + + $subnet->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Ports +----- + +A port represents a virtual switch port on a logical network switch. +Virtual instances (such as servers created using the Compute service) +attach their interfaces into ports. The port also defines the MAC +address and the IP address or addresses to be assigned to the interfaces +plugged into them. When IP addresses are associated with a port, this +also implies the port is associated with a subnet because the IP address +is taken from the allocation pool for a specific subnet. + +Create a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code:: php + + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple ports +~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List ports +~~~~~~~~~~ + +You can list all the ports to which you have access as shown in the +following example: + +.. code:: php + + $ports = $networkingService->listPorts(); + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a port +~~~~~~~~~~ + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code:: php + + $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a port +~~~~~~~~~~~~~ + +You can delete a port as shown in the following example: + +.. code:: php + + $port->delete(); + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/object-store/Access.md.rst b/doc/services/object-store/Access.md.rst new file mode 100644 index 000000000..ee85a938e --- /dev/null +++ b/doc/services/object-store/Access.md.rst @@ -0,0 +1,75 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want + +Temporary URLs +-------------- + +Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they're CDN-enabled. + +Set "temporary URL" metadata key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You must set this "secret" value on your account, where it can be used +in a global state: + +.. code:: php + + $account = $service->getAccount(); + $account->setTempUrlSecret('my_secret'); + + echo $account->getTempUrlSecret(); + +The string argument of ``setTempUrlSecret()`` is optional - if left out, +the SDK will generate a random hashed secret for you. + +Create a temporary URL +~~~~~~~~~~~~~~~~~~~~~~ + +Once you've set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute: + +.. code:: php + + $object->getTemporaryUrl(60, 'GET'); + +To allow PUT access for 1 hour: + +.. code:: php + + $object->getTemporaryUrl(360, 'PUT'); + +Hosting websites on CloudFiles +------------------------------ + +To host a static (i.e. HTML) website on CloudFiles, you must follow +these steps: + +1. CDN-enable a container +2. Upload all HTML content. You can use nested directory structures. +3. Tell CloudFiles what to use for your default index page like this: + +.. code:: php + + $container->setStaticIndexPage('index.html'); + +4. (Optional) Tell CloudFiles which error page to use by default: + +.. code:: php + + $container->setStaticErrorPage('error.html'); + +Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object. diff --git a/doc/services/object-store/Account.md.rst b/doc/services/object-store/Account.md.rst new file mode 100644 index 000000000..a2d380b2a --- /dev/null +++ b/doc/services/object-store/Account.md.rst @@ -0,0 +1,33 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +View Account Details +-------------------- + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +.. code:: php + + $account = $service->getAccount(); + + // Either return the full Metadata object + $details = $account->getDetails(); + + // or individual values + $account->getContainerCount(); + $account->getObjectCount(); + $account->getBytesUsed(); + diff --git a/doc/services/object-store/Container.md.cdn.rst b/doc/services/object-store/Container.md.cdn.rst new file mode 100644 index 000000000..2f1c8ea68 --- /dev/null +++ b/doc/services/object-store/Container.md.cdn.rst @@ -0,0 +1,90 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +To access the CDN functionality of a particular container: + +.. code:: php + + $container = $service->getContainer('foo_bar'); + + $cdn = $container->getCdn(); + +List CDN-enabled container +-------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code:: php + + $cdnService = $service->getCdnService(); + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + + } + +CDN-enable and -disable a container +----------------------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code:: php + + $container->enableCdn(); + $container->disableCdn(); + +Serving containers through SSL +------------------------------ + +.. code:: php + + $cdn->getCdnSslUri(); + +Streaming CDN-enabled containers +-------------------------------- + +.. code:: php + + $cdn->getCdnStreamingUri(); + +iOS streaming +------------- + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code:: php + + $cdn->getIosStreamingUri(); + +CDN logging +----------- + +To enable and disable logging for your CDN: + +.. code:: php + + $cdn->enableCdnLogging(); + $cdn->disableCdnLogging(); + diff --git a/doc/services/object-store/Container.md.storage.rst b/doc/services/object-store/Container.md.storage.rst new file mode 100644 index 000000000..89798fdd0 --- /dev/null +++ b/doc/services/object-store/Container.md.storage.rst @@ -0,0 +1,218 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + // Create a client object to communicate with various Rackspace Cloud services. + $client = new Rackspace(RACKSPACE_US, array( + 'username' => 'Replace this with your Rackspace Cloud user name', + 'apiKey' => 'Replace this with your Rackspace Cloud API key' + )); + + // Create a service object to use the object store service. The sample code + // creates the object store in the 'DFW' region. + $service = $client->objectStoreService('cloudFiles', 'DFW'); + +Create container +---------------- + +To create a new container, you just need to define its name: + +.. code:: php + + $container = $service->createContainer('my_amazing_container'); + +If the response returned is ``FALSE``, there was an API error - most +likely due to the fact you have a naming collision. + +Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +List containers +--------------- + +Return a list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $containerList = $service->listContainers(); + + while ($container = $containerList->next()) { + // Do stuff; some examples below + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } + +Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite's +memcmp() function, regardless of text encoding. + +The list is limited to 10,000 containers at a time. See 1.3 for ways to +limit and navigate this list. + +Return a formatted list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, the SDK only supports JSON-formatted responses. + +Controlling a large list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may limit and control this list of results by using the ``marker`` +and ``end_marker`` parameters. The former parameter (``marker``) tells +the API where to begin the list, and the latter (``end_marker``) tells +it where to end the list. You may use either of them independently or +together. You may also use the ``limit`` parameter to fix the number of +containers returned. + +To list a set of containers between two fixed points: + +.. code:: php + + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); + +Or to return a limited set: + +.. code:: php + + $someContainers = $service->listContainers(array('limit' => 560)); + +Get container +------------- + +To retrieve a certain container, either to access its object or +metadata: + +.. code:: php + + $container = $service->getContainer('container_name'); + + echo $container->getObjectCount(); + echo $container->getBytesUsed(); + +Delete container +---------------- + +Deleting a container is easy: + +.. code:: php + + $container->delete(); + +Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +``$deleteObjects`` parameter to ``TRUE`` like so: + +.. code:: php + + $container->delete(TRUE); + +You can also do it manually: + +.. code:: php + + $container->deleteAllObjects(); + $container->delete(); + +Create or update container metadata +----------------------------------- + +.. code:: php + + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); + +Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To *append* values +to the current metadata: + +.. code:: php + + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); + +If you only want to set the metadata to the local object, and not +immediately retain these values on the API, you can use a standard +setter method - which can contribute to eventual actions like an update: + +.. code:: php + + $container->setMetadata(array('Foo' => 'Bar')); + +Container quotas +---------------- + +The container\_quotas middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check. + +To set quotas for a container: + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); + +And to retrieve them: + +.. code:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); + +Access log delivery +------------------- + +To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage). + +.. code:: php + + $container->enableLogging(); + $container->disableLogging(); + +Syncing containers +------------------ + +You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory: + +.. code:: php + + $container->uploadDirectory('/home/Jamie/blog'); + +There are four scenarios you should be aware of: + ++------------------------+-----------------------+----------------------+--------------------------------+ +| Local | Remote | Comparison | Action | ++========================+=======================+======================+================================+ +| File exists | File exists | Identical checksum | No action | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File exists | Different checksum | Local file overwrites remote | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File does not exist | - | Local file created in Swift | ++------------------------+-----------------------+----------------------+--------------------------------+ +| Files does not exist | File exists | - | Remote file deleted | ++------------------------+-----------------------+----------------------+--------------------------------+ + diff --git a/doc/services/object-store/Migrating.md.storage.rst b/doc/services/object-store/Migrating.md.storage.rst new file mode 100644 index 000000000..99738554b --- /dev/null +++ b/doc/services/object-store/Migrating.md.storage.rst @@ -0,0 +1,101 @@ +Migrating containers (across regions) +===================================== + +Introduction +------------ + +Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a ``COPY`` +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality. + +You **will** be charged for bandwidth between regions, so it's advisable +to use ServiceNet where possible (which is free). + +Requirements +------------ + +- You must install the full Guzzle package, so that the process can + take advantage of Guzzle's batching functionality (it allows parallel + requests to be batched for greater efficiency). You can do this by + running: + +.. code:: bash + + php composer.phar install --dev + +- Depending on the size and number of transfer items, you will need to + raise PHP's memory limit: + +.. code:: php + + ini_set('memory_limit', '512M'); + +- You will need to enact some kind of backoff/retry strategy for rate + limits. Guzzle comes with a convenient feature that just needs to be + added as a normal subscriber: + +.. code:: php + + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + +This tells the client to retry up to ``10`` times for failed requests +have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + +Setup +----- + +You can access all this functionality by executing: + +.. code:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); + + $iadService->migrateContainer($oldContainer, $newContainer); + +It's advisable to do this process in a Cloud Server in one of the two +regions you're migrating to/from. This allows you to use ``privateURL`` +as the third argument in the ``objectStoreService`` methods like this: + +.. code:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + +This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free. + +Options +------- + +You can pass in an array of arguments to the method: + +.. code:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); + + $iadService->migrateContainer($oldContainer, $newContainer, $options); + +Options explained +~~~~~~~~~~~~~~~~~ + ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| Name | Description | Default | ++========================+===============================================================================================================================================================================================================================================================================================================================================+===========+ +| ``read.pageLimit`` | When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional ``objectList`` method, which calls the ``PaginatedIterator``. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API) | 10,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``read.batchLimit`` | After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together. | 1,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ + diff --git a/doc/services/object-store/Object.md.cdn.rst b/doc/services/object-store/Object.md.cdn.rst new file mode 100644 index 000000000..64ac143b3 --- /dev/null +++ b/doc/services/object-store/Object.md.cdn.rst @@ -0,0 +1,25 @@ +Setup +----- + +You will need to instantiate the container object and access its CDN +functionality as `documented +here `__. + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); + diff --git a/doc/services/object-store/Object.md.storage.rst b/doc/services/object-store/Object.md.storage.rst new file mode 100644 index 000000000..16dab18f4 --- /dev/null +++ b/doc/services/object-store/Object.md.storage.rst @@ -0,0 +1,330 @@ +Setup +----- + +Conceptually, a container contains objects (also known as files). In +order to work with objects, you will need to instantiate a container +object first as `documented +here `__. + +Note on object properties +------------------------- + +Please be aware that you cannot directly access the properties of +DataObject anymore, you **must** use appropriate getter/ setter methods: + ++----------------------+------------------------+ +| Property | Method | ++======================+========================+ +| Parent container | ``getContainer`` | ++----------------------+------------------------+ +| Name | ``getName`` | ++----------------------+------------------------+ +| Body of file | ``getContent`` | ++----------------------+------------------------+ +| Size of file | ``getContentLength`` | ++----------------------+------------------------+ +| Type of file | ``getContentType`` | ++----------------------+------------------------+ +| ETag checksum | ``getEtag`` | ++----------------------+------------------------+ +| Last modified date | ``getLastModified`` | ++----------------------+------------------------+ + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + + **Note:** Unlike previous versions, you do not need to manually + specify your object's content type. The API will do this for you. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +To upload a single/basic file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $data = fopen('/path/to/sample.mp3', 'r+'); + + // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) + + $meta = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow' + ); + + $metaHeaders = DataObject::stockHeaders($meta); + $customHeaders = array(); + $allHeaders = $metaHeaders + $customHeaders; + + $container->uploadObject('sample.mp3', $data, $allHeaders); + +To upload multiple small-to-mid sized files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + +To upload large files +~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your +transfer, upon which you can execute your upload functionality. For your +convenience, the Container resource object contains a simple method to +do this heavy lifting for you: + +.. code:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/jamie/video.mov', + 'metadata' => array( + 'Author' => 'Jamie' + ), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifest is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + // ... do something + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code:: php + + $container->objectList(array('prefix' => 'logFile_')); + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code:: php + + $file = $container->getObject('summer_vacation.mp4'); + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +:: + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + +Update an existing object +------------------------- + +Updating content is easy: + +.. code:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + +Copy object +----------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code:: php + + $object->copy('/container_2/new_object_name'); + +Delete object +------------- + +.. code:: php + + $object->delete(); + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code:: php + + $container->getPartialObject('summer_vacation.mp4'); + +In order to access the metadata on a partial or complete object, use: + +.. code:: php + + $object->getMetadata(); + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code:: php + + $object->refresh(); + +You can also update to get the latest metadata: + +.. code:: php + + $object->retrieveMetadata(); + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + +Bulk delete +----------- + +Bulk delete a set of paths: + +.. code:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); + diff --git a/doc/services/object-store/README.md.rst b/doc/services/object-store/README.md.rst new file mode 100644 index 000000000..01cf26733 --- /dev/null +++ b/doc/services/object-store/README.md.rst @@ -0,0 +1,86 @@ +Object Store +============ + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Specifically, a cloud is made up of one or more regions. Each region can +have several **containers**, created by a user. Each container can +container several **objects** (sometimes referred to as files), uploaded +by the user. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choose one of the following two options: + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Object Store service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +3. Create a container for your objects (also referred to as files). +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +4. Upload an object to the container. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +There is a lot more you can do with containers and objects. See the +`complete user guide to the Object Store service `__. diff --git a/doc/services/object-store/USERGUIDE.md.rst b/doc/services/object-store/USERGUIDE.md.rst new file mode 100644 index 000000000..d9732ea36 --- /dev/null +++ b/doc/services/object-store/USERGUIDE.md.rst @@ -0,0 +1,900 @@ +The Complete User Guide to the Object Store Service +=================================================== + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the object store service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'apiKey' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Object Store Service +~~~~~~~~~~~~~~~~~~~~ + +All operations on the object store are done via an object store service +object. + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +Containers +---------- + +A **container** defines a namespace for **objects**. An object with the +same name in two different containers represents two different objects. + +For example, you may create a container called ``logos`` to hold all the +image files for your blog. + +A container may contain zero or more objects in it. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Create Container +~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Details +~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single container's details by using its name. An +instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. + +.. code:: php + + $container = $objectStoreService->getContainer('logos'); + + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + +[ `Get the executable PHP script for this +example `__ ] + +List Containers +~~~~~~~~~~~~~~~ + +You can retrieve a list of all your containers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $containers = $objectStoreService->listContainers(); + foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Set or Update Container Metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can set metadata on a container. + +.. code:: php + + $container->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a container. + +.. code:: php + + $containerMetadata = $container->getMetadata(); + +[ `Get the executable PHP script for this +example `__ ] + +Delete Container +~~~~~~~~~~~~~~~~ + +When you no longer have a need for the container, you can remove it. + +If the container is empty (that is, it has no objects in it), you can +remove it as shown below: + +.. code:: php + + $container->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +If the container is not empty (that is, it has objects in it), you have +two choices in how to remove it: + +- `Individually remove each object <#delete-object>`__ in the + container, then remove the container itself as shown above, or + +- Remove the container and all the objects within it as shown below: + + .. code:: php + + $container->delete(true); + + [ `Get the executable PHP script for this + example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in a container. + +.. code:: php + + $containerObjectCount = $container->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerObjectCount`` will contain the number +of objects in the container represented by ``$container``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by a container, in bytes. + +.. code:: php + + $containerSizeInBytes = $container->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerSizeInBytes`` will contain the space +used, in bytes, by the container represented by ``$container``. + +Container Quotas +~~~~~~~~~~~~~~~~ + +Set Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum number of objects that may be stored +in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = 25; + $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Set Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum total space (in bytes) used by +objects in a container. + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; + $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum number of objects that may be +stored in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum total space (in bytes) used +by objects in a container. + +.. code:: php + + $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Objects +------- + +An **object** (sometimes referred to as a file) is the unit of storage +in an Object Store. An object is a combination of content (data) and +metadata. + +For example, you may upload an object named ``php-elephant.jpg``, a JPEG +image file, to the ``logos`` container. Further, you may assign metadata +to this object to indicate that the author of this object was someone +named Jane Doe. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Upload Object +~~~~~~~~~~~~~ + +Once you have created a container, you can upload objects to it. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +It is also possible to upload an object and associate metadata with it. + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData, $allHeaders); + +[ `Get the executable PHP script for this +example `__ ] + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Pseudo-hierarchical Folders +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Although you cannot nest directories in an Object Store, you can +simulate a hierarchical structure within a single container by adding +forward slash characters (``/``) in the object name. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'languages/php/elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. Within that container, the filename is +``languages/php/elephant.jpg``, where ``languages/php/`` is a +pseudo-hierarchical folder hierarchy. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Upload Multiple Objects +^^^^^^^^^^^^^^^^^^^^^^^ + +You can upload more than one object at a time to a container. + +.. code:: php + + $objects = array( + array( + 'name' => 'php-elephant.jpg', + 'path' => '/path/to/local/php-elephant.jpg' + ), + array( + 'name' => 'python-snake.jpg', + 'path' => '/path/to/local/python-snake.jpg' + ), + a + ); + + $container->uploadObjects($objects); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, the contents of two files present on the local +filesystem are uploaded as objects to the container referenced by +``$container``. + +Instead of specifying the ``path`` key in an element of the ``$objects`` +array, you can specify a ``body`` key whose value is a string or a +stream representation. + +Finally, you can pass headers as the second parameter to the +``uploadObjects`` method. These headers will be applied to every object +that is uploaded. + +:: + + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $container->uploadObjects($objects, $allHeaders); + +[ `Get the executable PHP script for this +example `__ +] + +In the example above, every object referenced within the ``$objects`` +array will be uploaded with the same metadata. + +Large Objects +~~~~~~~~~~~~~ + +If you want to upload objects larger than 5GB in size, you must use a +different upload process. + +.. code:: php + + $options = array( + 'name' => 'san_diego_vacation_video.mp4', + 'path' => '/path/to/local/videos/san_diego_vacation.mp4' + ); + $objectTransfer = $container->setupObjectTransfer($options); + $objectTransfer->upload(); + +[ `Get the executable PHP script for this +example `__ ] + +The process shown above will automatically partition your large object +into small chunks and upload them concurrently to the container +represented by ``$container``. + +You can tune the parameters of this process by specifying additional +options in the ``$options`` array. Here is a complete listing of keys +that can be specified in the ``$options`` array: + ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ +| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ + +Auto-extract Archive Files +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can upload a tar archive file and have the Object Store service +automatically extract it into a container. + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $localArchiveFileName = '/path/to/local/image_files.tar.gz'; + $remotePath = 'images/'; + + $fileData = fopen($localArchiveFileName, 'r'); + $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, a local archive file named ``image_files.tar.gz`` +is uploaded to an Object Store container named ``images`` (defined by +the ``$remotePath`` variable). + +Note that while we call ``fopen`` to open a file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``bulkExtract`` call. + +The third parameter to ``bulkExtract`` is the type of the archive file +being uploaded. The acceptable values for this are: + +- ``UrlType::TAR`` for tar archive files, *or*, +- ``UrlType:TAR_GZ`` for tar archive files that are compressed with + gzip, *or* +- ``UrlType::TAR_BZ`` for tar archive file that are compressed with + bzip + +Note that the value of ``$remotePath`` could have been a +(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as +``images/blog`` as well. + +List Objects in a Container +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all the objects stored in a container. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $objects = $container->objectList(); + foreach ($objects as $object) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +You can list only those objects in the container whose names start with +a certain prefix. + +.. code:: php + + $options = array( + 'prefix' => 'php' + ); + + $objects = $container->objectList($options); + +[ `Get the executable PHP script for this +example `__ ] + +In general, the ``objectList()`` method described above takes an +optional parameter (``$options`` in the example above). This parameter +is an associative array of various options. Here is a complete listing +of keys that can be specified in the ``$options`` array: + ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++==================+=============================================================================+=============+=============+=================+=========================+ +| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ + +Retrieve Object +~~~~~~~~~~~~~~~ + +You can retrieve an object and its metadata, given the object's +container and name. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getObject($objectName); + + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + + $objectContent = $object->getContent(); + + /** @var $objectContent Guzzle\Http\EntityBody **/ + + // Write object content to file on local filesystem. + $objectContent->rewind(); + $stream = $objectContent->getStream(); + $localFilename = tempnam("/tmp", 'php-opencloud-'); + file_put_contents($localFilename, $stream); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$object`` is the object named +``php-elephant.jpg`` in the container represented by ``$container``. +Further, ``$objectContent`` represents the contents of the object. It is +of type +```Guzzle\Http\EntityBody`` `__. + +Retrieve Object Metadata +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve just an object's metadata without retrieving its +contents. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getPartialObject($objectName); + $objectMetadata = $object->getMetadata(); + + /** @var $objectMetadata \OpenCloud\Common\Metadata **/ + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, while ``$object`` is an instance of +``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only +partially populated. Specifically, only properties of the instance +relating to object metadata are populated. + +Temporary URLs +~~~~~~~~~~~~~~ + +The Temporary URL feature allows you to create limited-time Internet +addresses that allow you to grant limited access to your Object Store +account. Using this feature, you can allow others to retrieve or place +objects in your Object Store account for a specified amount of time. +Access to the temporary URL is independent of whether or not your +account is `CDN-enabled <#cdn-containers>`__. Even if you do not +CDN-enable a container, you can still grant temporary public access +through a temporary URL. + +First, you must set the temporary URL secret on your account. This is a +one-time operation; you only need to perform it the very first time you +wish to use the temporary URLs feature. + +.. code:: php + + $account->setTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation is carried out on ``$account``, which is an +instance of ``OpenCloud\ObjectStore\Resource\Account``, a class +representing `your object store account <#accounts>`__. + +The above operation will generate a random secret and set it on your +account. Instead of a random secret, if you wish to provide a secret, +you can supply it as a parameter to the ``setTempUrlSecret`` method. + +.. code:: php + + $account->setTempUrlSecret(''); + +[ `Get the executable PHP script for this +example `__ +] + +Once a temporary URL secret has been set on your account, you can +generate a temporary URL for any object in your Object Store. + +.. code:: php + + $expirationTimeInSeconds = 3600; // one hour from now + $httpMethodAllowed = 'GET'; + $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, a temporary URL for the object is generated. This +temporary URL will provide public access to the object for an hour (3600 +seconds), as specified by the ``$expirationTimeInSeconds`` variable. +Further, only GET HTTP methods will be allowed on this URL, as specified +by the ``$httpMethodAllowed`` variable. The other value allowed for the +``$httpMethodAllowed`` variable would be ``PUT``. + +You can also retrieve the temporary URL secret that has been set on your +account. + +.. code:: php + + $tempUrlSecret = $account->getTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Update Object +~~~~~~~~~~~~~ + +You can update an object's contents (as opposed to `updating its +metadata <#update-object-metadata>`__) by simply re-\ `uploading the +object <#upload-object>`__ to its container using the same object name +as before. + +Update Object Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can update an object's metadata after it has been uploaded to a +container. + +.. code:: php + + $object->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Copy Object +~~~~~~~~~~~ + +You can copy an object from one container to another, provided the +destination container already exists. + +.. code:: php + + $object->copy('logos_copy/php.jpg'); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, both the name of the destination container +(``logos_copy``)and the name of the destination object (``php.jpg``) +have to be specified, separated by a ``/``. + +Delete Object +~~~~~~~~~~~~~ + +When you no longer need an object, you can delete it. + +.. code:: php + + $object->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Bulk Delete +~~~~~~~~~~~ + +While you can delete individual objects as shown above, you can also +delete objects and empty containers in bulk. + +.. code:: php + + $objectStoreService->bulkDelete(array( + 'logos/php-elephant.png', + 'logos/python-snakes.png', + 'some_empty_container' + )); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, two objects (``some_container/object_a.png``, +``some_other_container/object_z.png``) and one empty container +(``some_empty_container``) are all being deleted in bulk via a single +command. + +CDN Containers +-------------- + +Note: The functionality described in this section is available only on +the Rackspace cloud. It will not work as described when working with a +vanilla OpenStack cloud. + +Any container can be converted to a CDN-enabled container. When this is +done, the objects within the container can be accessed from anywhere on +the Internet via a URL. + +Enable CDN Container +~~~~~~~~~~~~~~~~~~~~ + +To take advantage of CDN capabilities for a container and its objects, +you must CDN-enable that container. + +.. code:: php + + $container->enableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Public URLs +~~~~~~~~~~~ + +Once you have CDN-enabled a container, you can retrieve a +publicly-accessible URL for any of its objects. There are four types of +publicly-accessible URLs for each object. Each type of URL is meant for +a different purpose. The sections below describe each of these URL types +and how to retrieve them. + +HTTP URL +^^^^^^^^ + +You can use this type of URL to access the object over HTTP. + +:: + + $httpUrl = $object->getPublicUrl(); + +[ `Get the executable PHP script for this +example `__ ] + +Secure HTTP URL +^^^^^^^^^^^^^^^ + +You can use this type of URL to access the object over HTTP + TLS/SSL. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $httpsUrl = $object->getPublicUrl(UrlType::SSL); + +[ `Get the executable PHP script for this +example `__ ] + +Streaming URL +^^^^^^^^^^^^^ + +You can use this type of URL to stream a video or audio object using +Adobe's HTTP Dynamic Streaming. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +IOS Streaming URL +^^^^^^^^^^^^^^^^^ + +You can use this type of URL to stream an audio or video object to an +iOS device. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +Update CDN Container TTL +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update the TTL of a CDN-enabled container. + +.. code:: php + + $cdnContainer = $container->getCdn(); + $cdnContainer->setTtl(); + +[ `Get the executable PHP script for this +example `__ ] + +Disable CDN Container +~~~~~~~~~~~~~~~~~~~~~ + +If you no longer need CDN capabilities for a container, you can disable +them. + +.. code:: php + + $container->disableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Accounts +-------- + +An **account** defines a namespace for **containers**. An account can +have zero or more containers in it. + +Retrieve Account +~~~~~~~~~~~~~~~~ + +You must retrieve the account before performing any operations on it. + +.. code:: php + + $account = $objectStoreService->getAccount(); + +Get Container Count +~~~~~~~~~~~~~~~~~~~ + +You can quickly find out how many containers are in your account. + +.. code:: php + + $accountContainerCount = $account->getContainerCount(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in your account. + +.. code:: php + + $accountObjectCount = $account->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountObjectCount`` will contain the number of +objects in the account represented by ``$account``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by your account, in bytes. + +.. code:: php + + $accountSizeInBytes = $account->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountSizeInBytes`` will contain the space +used, in bytes, by the account represented by ``$account``. diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/orchestration/README.md.rst b/doc/services/orchestration/README.md.rst new file mode 100644 index 000000000..1a983d187 --- /dev/null +++ b/doc/services/orchestration/README.md.rst @@ -0,0 +1,98 @@ +Orchestration +============= + +**Orchestration** is a service that can be used to create and manage +cloud resources. Examples of such resources are databases, load +balancers, servers and software installed on them. + +Concepts +-------- + +To use the Orchestration service effectively, you should understand +several key concepts: + +- **Template**: An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + +- **Resource**: A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + +- **Stack**: A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Orchestration service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object as shown in the following example: + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +3. Create a stack from a template. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a stack, there is more you can do with it. See +`complete user guide for orchestration `__. diff --git a/doc/services/orchestration/USERGUIDE.md.rst b/doc/services/orchestration/USERGUIDE.md.rst new file mode 100644 index 000000000..98bd51db7 --- /dev/null +++ b/doc/services/orchestration/USERGUIDE.md.rst @@ -0,0 +1,672 @@ +Complete User Guide for the Orchestration Service +================================================= + +Orchestration is a service that you can use to create and manage cloud +resources such as databases, load balancers, and servers, and the +software installed on servers. + +Table of Contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ +- `Client <#client>`__ +- `Orchestration service <#orchestration-service>`__ +- `Templates <#templates>`__ +- `Validate template <#validate-template>`__ + + - `Validate a template from a + file <#validate-a-template-from-a-file>`__ + - `Validate Template from URL <#validate-template-from-url>`__ + +- `Stacks <#stacks>`__ +- `Preview stack <#preview-stack>`__ + + - `Preview a stack from a template + file <#preview-a-stack-from-a-template-file>`__ + - `Preview a stack from a template + URL <#preview-a-stack-from-a-template-url>`__ + +- `Create stack <#create-stack>`__ + + - `Create a stack from a template + file <#create-a-stack-from-a-template-file>`__ + - `Create a stack from a template + URL <#create-a-stack-from-a-template-url>`__ + +- `List stacks <#list-stacks>`__ +- `Get stack <#get-stack>`__ +- `Get stack template <#get-stack-template>`__ +- `Update stack <#update-stack>`__ + + - `Update a stack from a template + file <#update-a-stack-from-a-template-file>`__ + - `Update Stack from Template + URL <#update-stack-from-template-url>`__ + +- `Delete stack <#delete-stack>`__ +- `Abandon Stack <#abandon-stack>`__ +- `Adopt stack <#adopt-stack>`__ +- `Stack resources <#stack-resources>`__ +- `List stack resources <#list-stack-resources>`__ +- `Get stack resource <#get-stack-resource>`__ +- `Get stack resource metadata <#get-stack-resource-metadata>`__ +- `Stack resource events <#stack-resource-events>`__ +- `List stack events <#list-stack-events>`__ +- `List stack resource events <#list-stack-resource-events>`__ +- `Get stack resource event <#get-stack-resource-event>`__ +- `Resource types <#resource-types>`__ +- `List resource types <#list-resource-types>`__ +- `Get resource type <#get-resource-type>`__ +- `Get resource type template <#get-resource-type-template>`__ +- `Build info <#build-info>`__ +- `Get build info <#get-build-info>`__ + +Concepts +-------- + +To use the Orchestration service effectively, you should understand the +following key concepts: + +- **Template**: A JSON or YAML document that describes how a set of + resources should be assembled to produce a working deployment. The + template specifies the resources to use, the attributes of these + resources that are parameterized and the information that is sent to + the user when a template is instantiated. + +- **Resource**: Some component of your architecture (a cloud server, a + group of scaled cloud servers, a load balancer, some configuration + management system, and so on) that is defined in a template. + +- **Stack**: A running instance of a template. When a stack is created, + the resources specified in the template are created. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+==========================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +Templates +--------- + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validate template +~~~~~~~~~~~~~~~~~ + +Before you use a template to create a stack, you might want to validate +it. + +Validate a template from a file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Validate Template from URL +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Stacks +------ + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + +Preview stack +~~~~~~~~~~~~~ + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Preview a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create stack +~~~~~~~~~~~~ + +You can create a stack from a template. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +List stacks +~~~~~~~~~~~ + +You can list all the stacks that you have created as shown in the +following example: + +.. code:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack +~~~~~~~~~ + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code:: php + + $stack = $orchestrationService->getStack('simple-lamp-setup'); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack template +~~~~~~~~~~~~~~~~~~ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code:: php + + $stackTemplate = $stack->getTemplate(); + /** @var $stackTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update stack +~~~~~~~~~~~~ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + +Update a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code:: php + + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Update Stack from Template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code:: php + + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Delete stack +~~~~~~~~~~~~ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code:: php + + $stack->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Abandon Stack +~~~~~~~~~~~~~ + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code:: php + + $abandonStackData = $stack->abandon(); + /** @var $abandonStackData string **/ + + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation returns data about the abandoned stack as a +string. You can use this data to recreate the stack by using the `adopt +stack <#adopt-stack>`__ operation. + +Adopt stack +~~~~~~~~~~~ + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code:: php + + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resources +--------------- + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + +List stack resources +~~~~~~~~~~~~~~~~~~~~ + +You can list all the resources for a stack as shown in the following +example: + +.. code:: php + + $resources = $stack->listResources(); + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource +~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code:: php + + $resource = $stack->getResource('load-balancer'); + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceMetadata = $resource->getMetadata(); + /** @var $resourceMetadata \stdClass **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resource events +--------------------- + +Operations on resources within a stack (such as the creation of a +resource) produce events. + +List stack events +~~~~~~~~~~~~~~~~~ + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code:: php + + $stackEvents = $stack->listEvents(); + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List stack resource events +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceEvents = $resource->listEvents(); + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource event +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code:: php + + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + +[ `Get the executable PHP script for this +example `__ ] + +Resource types +-------------- + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +~~~~~~~~~~~~~~~~~~~ + +You can list all supported resource types as shown in the following +example: + +.. code:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type +~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code:: php + + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type template +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code:: php + + $resourceTypeTemplate = $resourceType->getTemplate(); + /** @var $resourceTypeTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Build info +---------- + +Get build info +~~~~~~~~~~~~~~ + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code:: php + + $buildInfo = $orchestrationService->getBuildInfo(); + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/queues/Claim.md.rst b/doc/services/queues/Claim.md.rst new file mode 100644 index 000000000..42161536d --- /dev/null +++ b/doc/services/queues/Claim.md.rst @@ -0,0 +1,164 @@ +1. Introduction +--------------- + +A **Claim** is the process of a worker checking out a message to perform +a task. Claiming a message prevents other workers from attempting to +process the same messages. + +2. Setup +-------- + +A Claim is initialized on its parent object, a Queue: + +.. code:: php + + // To initialize an empty object: + $claim = $queue->getClaim(); + + // or retrieve a specific claim: + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +3. Claim messages +----------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +``204 No Content`` message. + +When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it. + +The age given for a claim is relative to the server's clock. The claim's +age is useful for determining how quickly messages are getting processed +and whether a given message's claim is about to expire. + +When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message. + +3.2 Attributes +~~~~~~~~~~~~~~ + +The ``ttl`` attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request. + +The ``grace`` attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted. + +The ``limit`` attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional. + +3.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); + +4. Query claim +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation queries the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +4.2 Attributes +~~~~~~~~~~~~~~ + +Claim ID. + +4.3 Code +~~~~~~~~ + +.. code:: php + + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +5. Update claim +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific **Claim** and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL. + +5.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +5.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); + +6. Release claim +---------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored. + +This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers. + +6.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +6.3 Code +~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/services/queues/Message.md.rst b/doc/services/queues/Message.md.rst new file mode 100644 index 000000000..ca67e22bb --- /dev/null +++ b/doc/services/queues/Message.md.rst @@ -0,0 +1,257 @@ +1. Introduction +--------------- + +A **Message** is a task, a notification, or any meaningful data that a +producer or publisher sends to the queue. A message exists until it is +deleted by a recipient or automatically by the system based on a TTL +(time-to-live) value. + +2. Setup +-------- + +A message is initialized from its parent object, a Queue: + +.. code:: php + + // Setup an empty object + $message = $queue->getMessage(); + + // or retrieve an existing one + $message = $queue->getMessage(''); + +3. Post message +--------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation posts the specified message or messages. You can submit +up to 10 messages in a single request. + +When posting new messages, you specify only the ``body`` and ``ttl`` for +the message. The API will insert metadata, such as ID and age. + +3.2 Parameters +~~~~~~~~~~~~~~ + +How you pass through the array structure depends on whether you are +executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the +same. + +The ``body`` attribute specifies an arbitrary document that constitutes +the body of the message being sent. The size of this body is limited to +256 KB, excluding whitespace. + +The ``ttl`` attribute specifies how long the server waits before marking +the message as expired and removing it from the queue. The value of ttl +must be between 60 and 1209600 seconds (14 days). Note that the server +might not actually delete the message until its age has reached up to +(ttl + 60) seconds, to allow for flexibility in storage implementations. + +3.3 Code samples +~~~~~~~~~~~~~~~~ + +3.3.1 Posting a single message +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + use OpenCloud\Common\Constants\Datetime; + + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013 + ), + 'ttl' => 2 * Datetime::DAY + )); + +3.3.2 Post a batch of messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Please note that the list of messages will be truncated at 10. For more, +please execute another method call. + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); + + $queue->createMessages($messages); + +4. Get messages +--------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the message or messages in the specified queue. + +Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations. + +Results are ordered by age, oldest message first. + +4.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++====================+=========+============+============================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ +| marker | Query | String | Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +5. Get a set of messages by ID +------------------------------ + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format +multiple message ID values by separating them with commas +(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies +the claim ID with which the message is associated. Optional.\| +\|----\|-----\|----\|-----------\| + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $messages = $queue->listMessages(array('ids' => $ids)); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +6. Delete a set of messages by ID +--------------------------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted. + +6.2 Parameters +~~~~~~~~~~~~~~ + +An array of IDs. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $response = $queue->deleteMessages($ids); + +7. Get a specific message +------------------------- + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the specified message from the specified queue. + +7.2 Parameters +~~~~~~~~~~~~~~ + +Message ID. + +7.3 Object properties +~~~~~~~~~~~~~~~~~~~~~ + +``href`` is an opaque relative URI that the client can use to uniquely +identify a message resource and interact with it. + +``ttl`` is the TTL that was set on the message when it was posted. The +message expires after (ttl - age) seconds. + +``age`` is the number of seconds relative to the server's clock. + +``body`` is the arbitrary document that was submitted with the original +request to post the message. + +7.4 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message = $queue->getMessage('51db6f78c508f17ddc924357'); + +8. Delete message +----------------- + +8.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified message. + +8.2 Parameters +~~~~~~~~~~~~~~ + +None. + +8.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/services/queues/Queue.md.rst b/doc/services/queues/Queue.md.rst new file mode 100644 index 000000000..1fe9f7036 --- /dev/null +++ b/doc/services/queues/Queue.md.rst @@ -0,0 +1,197 @@ +1. Introduction +--------------- + +A Queue is an entity that holds messages. Ideally, a queue is created +per work type. For example, if you want to compress files, you would +create a queue dedicated to this job. Any application that reads from +this queue would only compress files. + +2. Setup +-------- + +.. code:: php + + $service = $client->queuesService('cloudQueues', 'ORD'); + +3. Client IDs +------------- + +With most of Marconi's operation, you must specify a **Client ID** which +will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + +4. List queues +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation lists queues for the project. The queues are sorted +alphabetically by name. + +4.2 Parameters +~~~~~~~~~~~~~~ + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|marker\|Query\|​String\|Specifies the name of the last queue received +in a previous request, or none to get the first page of results. +Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to +return. The default value for the number of queues returned is 10. If +you do not specify this parameter, the default number of queues is +returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether +queue metadata is included in the response. The default value for this +parameter is false, which excludes the metadata. Optional.\| +\|----\|-----\|----\|-----------\| + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queues = $service->listQueues(); + + while ($queue = $queues->next()) { + echo $queue->getName() . PHP_EOL; + } + +5. Create queue +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation creates a new queue. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A string representation of the name for your new Queue. The name must +not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->createQueue('new_queue'); + +6. Retrieve queue +----------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +Returns a ``Queue`` object for use. + +6.2 Parameters +~~~~~~~~~~~~~~ + +Queue name. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->getQueue('new_queue'); + +7. Check queue existence +------------------------ + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +7.2 Parameters +~~~~~~~~~~~~~~ + +7.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + +8. Update queue metadata (permanently to the API) +------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +4.2 Parameters +~~~~~~~~~~~~~~ + +Hash of key pairs. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + +9. Retrieve the queue metadata (fresh from the API) +--------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns metadata, such as message TTL, for the queue. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadata = $queue->retrieveMetadata(); + + print_r($metadata->toArray()); + +10. Get queue stats +------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->getStats(); + diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst new file mode 100644 index 000000000..e69de29bb From 5890418e1a14ebfdd4caf57b36044196eed4224c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:47 +0100 Subject: [PATCH 567/835] Add remaining top-level things --- doc/caching-creds.rst | 44 +++++++++++ doc/debugging.rst | 99 +++++++++++++++++++++++ doc/index.rst | 23 ++++++ doc/iterators.rst | 178 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+) create mode 100644 doc/caching-creds.rst create mode 100644 doc/debugging.rst create mode 100644 doc/index.rst create mode 100644 doc/iterators.rst diff --git a/doc/caching-creds.rst b/doc/caching-creds.rst new file mode 100644 index 000000000..3c3e8df23 --- /dev/null +++ b/doc/caching-creds.rst @@ -0,0 +1,44 @@ +Caching credentials +=================== + +You can speed up your API operations by caching your credentials in a +(semi-)permanent location, such as your DB or local filesystem. This +enable subsequent requests to access a shared resource, instead of +repetitively having to re-authenticate on every thread of execution. + +Tokens are valid for 24 hours, so you can effectively re-use the same +cached value for that period. If you try to use a cached version that +has expired, an authentication request will be made. + +Filesystem example +------------------ + +In this example, credentials will be saved to a file in the local +filesystem. Be sure to exclude it from your VCS. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); + + $cacheFile = __DIR__ . '/.opencloud_token'; + + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } + + $token = $client->getTokenObject(); + + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } + +In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/debugging.rst b/doc/debugging.rst new file mode 100644 index 000000000..445915284 --- /dev/null +++ b/doc/debugging.rst @@ -0,0 +1,99 @@ +Debugging +========= + +There are two important debugging strategies to use when encountering +problems with HTTP transactions. + +Strategy 1: Meaningful exception handling +----------------------------------------- + +If the API returns a ``4xx`` or ``5xx`` status code, it indicates that +there was an error with the sent request, meaning that the transaction +cannot be adequately completed. + +The Guzzle HTTP component, which forms the basis of our SDK's transport +layer, utilizes `numerous exception +classes `__ +to handle this error logic. + +The two most common exception classes are: + +- ``Guzzle\Http\Exception\ClientErrorResponseException``, which is + thrown when a ``4xx`` response occurs + +- ``Guzzle\Http\Exception\ServerErrorResponseException``, which is + thrown when a ``5xx`` response occurs + +Both of these classes extend the base ``BadResponseException`` class. + +This provides you with the granularity you need to debug and handle +exceptions. + +An example with Swift +~~~~~~~~~~~~~~~~~~~~~ + +If you're trying to retrieve a Swift resource, such as a Data Object, +and you're not completely certain that it exists, it makes sense to wrap +your call in a try/catch block: + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + // Okay, the resource probably does not exist + return false; + } catch (\Exception $e) { + // Some other exception was thrown, probably critical + $this->logException($e); + $this->alertDevs(); + } + +Both ``ClientErrorResponseException`` and +``ServerErrorResponseException`` have two methods that allow you to +access the HTTP transaction: + +.. code:: php + + // Find out the faulty request + $request = $e->getRequest(); + + // Display everything by casting as string + echo (string) $request; + + // Find out the HTTP response + $response = $e->getResponse(); + + // Output that too + echo (string) $response; + +Strategy 2: Wire logging +------------------------ + +Guzzle provides a `Log +plugin `__ +that allows you to log everything over the wire, which is useful if you +don't know what's going on. + +Here's how you enable it: + +Install the plugin +^^^^^^^^^^^^^^^^^^ + +.. code:: bash + + php composer.phar require guzzle/plugin-log:~3.8 + +Add to your client +^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use Guzzle\Plugin\Log\LogPlugin; + + $client->addSubscriber(LogPlugin::getDebugPlugin()); + +The above will add a generic logging subscriber to your client, which +will be notified every time a relevant HTTP event is fired off. diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 000000000..45c453a40 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,23 @@ +.. php-opencloud documentation master file, created by + sphinx-quickstart on Tue Mar 3 12:28:19 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to php-opencloud's documentation! +========================================= + +Contents: + +.. toctree:: + :glob: + :maxdepth: 2 + + services/**/index + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/iterators.rst b/doc/iterators.rst new file mode 100644 index 000000000..e5d3c198d --- /dev/null +++ b/doc/iterators.rst @@ -0,0 +1,178 @@ +Iterators +========= + +Intro +----- + +Iterators allow you to traverse over collections of your resources in an +efficient and easy way. Currently there are two Iterators provided by +the SDK: + +- **ResourceIterator**. The standard iterator class that implements + SPL's standard + `Iterator `__, + `ArrayAccess `__ + and `Countable `__ + interfaces. In short, this allows you to traverse this object (using + ``foreach``), count its internal elements like an array (using + ``count`` or ``sizeof``), and access its internal elements like an + array (using ``$iterator[1]``). + +- **PaginatedIterator**. This is a child of ResourceIterator, and as + such inherits all of its functionality. The difference however is + that when it reaches the end of the current collection, it attempts + to construct a URL to access the API based on predictive paginated + collection templates. + +Common behaviour +---------------- + +.. code:: php + + $iterator = $computeService->flavorList(); + +There are two ways to traverse an iterator. The first is the longer, +more traditional way: + +.. code:: php + + while ($iterator->valid()) { + $flavor = $iterator->current(); + + // do stuff.. + echo $flavor->id; + + $iterator->next(); + } + +There is also a shorter and more intuitive version: + +.. code:: php + + foreach ($iterator as $flavor) { + // do stuff... + echo $flavor->id; + } + +Because the iterator implements PHP's native ``Iterator`` interface, it +can inherit all the native functionality of traversible data structures +with ``foreach``. + +Very important note +------------------- + +Until now, users have been expected to do this: + +.. code:: php + + while ($flavor = $iterator->next()) { + // ... + } + +which is **incorrect**. The single responsibility of ``next`` is to move +the internal pointer forward. It is the job of ``current`` to retrieve +the current element. + +For your convenience, these two Iterator classes are fully backward +compatible: they exhibit all the functionality you'd expect from a +correctly implemented iterator, but they also allow previous behaviour. + +Using paginated collections +--------------------------- + +For large collections, such as retrieving DataObjects from +CloudFiles/Swift, you need to use pagination. Each resource will have a +different limit per page; so once that page is traversed, there needs to +be another API call to retrieve to *next* page's resources. + +There are two key concepts: + +- **limit** is the amount of resources returned per page +- **marker** is the way you define a starting point. It is some form of + identifier that allows the collection to begin from a specific + resource + +Resource classes +~~~~~~~~~~~~~~~~ + +When the iterator returns a current element in the internal list, it +populates the relevant resource class with all the data returned to the +API. In most cases, a ``stdClass`` object will become an instance of +``OpenCloud\Common\PersistentObject``. + +In order for this instantiation to happen, the ``resourceClass`` option +must correspond to some method in the parent class that creates the +resource. For example, if we specify 'ScalingPolicy' as the +``resourceClass``, the parent object (in this case +``OpenCloud\Autoscale\Group``, needs to have some method will allows the +iterator to instantiate the child resource class. These are all valid: + +1. ``Group::scalingGroup($data);`` + +2. ``Group::getScalingGroup($data);`` + +3. ``Group::resource('ScalingGroup', $data);`` + +where ``$data`` is the standard object. This list runs in order of +precedence. + +Setting up a PaginatedIterator +------------------------------ + +.. code:: php + + use OpenCloud\Common\Collection\PaginatedIterator; + + $service = $client->computeService(); + + $flavors = PaginatedIterator::factory($service, array( + 'resourceClass' => 'Flavor', + 'baseUrl' => $service->getUrl('flavors') + 'limit.total' => 350, + 'limit.page' => 100, + 'key.collection' => 'flavors' + )); + + foreach ($flavors as $flavor) { + echo $flavor->getId(); + } + +As you can see, there are a lot of configuration parameters to pass in - +and getting it right can be quite fiddly, involving a lot of API +research. For this reason, using the convenience methods like +``flavorList`` is recommended because it hides the complexity. + +PaginatedIterator options +~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are certain configuration options that the paginated iterator +needs to work. These are: + ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| Name | Description | Type | Required | Default | ++=========================+===================================================================================================================================================================================================================================================+==============================+============+===============+ +| resourceClass | The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator. | string | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| baseUrl | The base URL that is used for making new calls to the API for new pages | ``Guzzle\Http\Url`` | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.total | The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list | int | No | 10000 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.page | The amount of resources each page contains | int | No | 100 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.links | Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is ``images_links``. | string | No | links | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collection | The top-level key for the array of resources. For example, servers are returned with this data structure: ``{"servers": [...]}``. The **key.collection** value in this case would be ``servers``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collectionElement | Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: ``{"keypairs": [ {"keypair": {...}} ] }``. So in this case the collectionElement key would be ``keypair``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.marker | The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is ``id`` or ``name``. | string | No | name | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.method | The HTTP method used when making API calls for new pages | string | No | GET | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.headers | The HTTP headers to send when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.body | The HTTP entity body to send when making API calls for new pages | ``Guzzle\Http\EntityBody`` | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ + From 7340498c50ee3dea474e72a7104cf64c67f73bd4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:49:42 +0100 Subject: [PATCH 568/835] Shuffle structure --- doc/services/autoscale/group-config.rst | 6 +----- doc/services/autoscale/groups.rst | 9 --------- doc/services/autoscale/index.rst | 18 +++++++++++++++++ doc/services/autoscale/policies.rst | 4 +--- doc/services/autoscale/service.sample.rst | 7 ------- doc/services/autoscale/webhooks.rst | 4 +--- doc/services/common/service-args.rst | 16 +++++++++++++++ doc/services/compute/flavors.rst | 6 ------ doc/services/compute/images.rst | 5 ----- doc/services/compute/index.rst | 18 +++++++++++++++++ doc/services/compute/keypairs.rst | 6 ------ doc/services/compute/servers.rst | 5 ----- doc/services/compute/service.sample.rst | 24 ----------------------- 13 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 doc/services/autoscale/service.sample.rst create mode 100644 doc/services/common/service-args.rst delete mode 100644 doc/services/compute/service.sample.rst diff --git a/doc/services/autoscale/group-config.rst b/doc/services/autoscale/group-config.rst index a0d7c98f7..5dcfb6ce4 100644 --- a/doc/services/autoscale/group-config.rst +++ b/doc/services/autoscale/group-config.rst @@ -1,14 +1,10 @@ Group configurations ==================== -.. contents:: - Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with the functionality of a group's configuration, +In order to interact with the functionality of a group's configuration, you must first retrieve the details of the group itself. To do this, you must substitute `{groupId}` for your group's ID: diff --git a/doc/services/autoscale/groups.rst b/doc/services/autoscale/groups.rst index 8e781a2aa..a6caf3f9d 100644 --- a/doc/services/autoscale/groups.rst +++ b/doc/services/autoscale/groups.rst @@ -1,15 +1,6 @@ Groups ====== -.. contents:: - - -Setup ------ - -.. include:: service.sample.rst - - List all groups --------------- diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst index 3077a0a2c..d6ec512ac 100644 --- a/doc/services/autoscale/index.rst +++ b/doc/services/autoscale/index.rst @@ -1,6 +1,23 @@ Auto Scale v2 ============= +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); + +.. include:: ../common/service-args.rst + + +Operations +---------- + .. toctree:: groups @@ -8,6 +25,7 @@ Auto Scale v2 policies webhooks + Glossary -------- diff --git a/doc/services/autoscale/policies.rst b/doc/services/autoscale/policies.rst index f5d2605f4..e103d328e 100644 --- a/doc/services/autoscale/policies.rst +++ b/doc/services/autoscale/policies.rst @@ -4,9 +4,7 @@ Scaling Policies Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with the functionality of a group's scaling +In order to interact with the functionality of a group's scaling policies, you must first retrieve the details of the group itself. To do this, you must substitute `{groupId}` for your group's ID: diff --git a/doc/services/autoscale/service.sample.rst b/doc/services/autoscale/service.sample.rst deleted file mode 100644 index 7e5d166ab..000000000 --- a/doc/services/autoscale/service.sample.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. include:: ../common/rs-client.sample.rst - -Now, set up the Auto Scale service: - -.. code-block:: php - - $service = $client->autoscaleService(); diff --git a/doc/services/autoscale/webhooks.rst b/doc/services/autoscale/webhooks.rst index 68b16b0dc..9133aa9ed 100644 --- a/doc/services/autoscale/webhooks.rst +++ b/doc/services/autoscale/webhooks.rst @@ -4,9 +4,7 @@ Webhooks Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with webhooks, you must first retrieve the +In order to interact with webhooks, you must first retrieve the details of the group and scaling policy you want to execute: .. code-block:: php diff --git a/doc/services/common/service-args.rst b/doc/services/common/service-args.rst new file mode 100644 index 000000000..696f32f2a --- /dev/null +++ b/doc/services/common/service-args.rst @@ -0,0 +1,16 @@ +``{catalogName}`` is the **name** of the service, as it appears in the service +catalog. For Rackspace users, a default will be provided if you pass ``null`` +in for this argument. For OpenStack users, you cannot do this: you must instead +set your own value since it can depend on your environment setup. + +``{region}`` is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the `supported regions page +`_. + +``{urlType}`` is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either ``internalURL`` or +``publicURL``. The former will execute HTTP transactions over the internal +network configured for your service, possibly reducing latency and the overall +bandwidth cost - the caveat is that all of your resources must be in same region. +``publicURL``, however, which is the default, will operate over the public +Internet and is to be used for multi-region installations. diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst index f79caa171..eeb447d3c 100644 --- a/doc/services/compute/flavors.rst +++ b/doc/services/compute/flavors.rst @@ -1,12 +1,6 @@ Flavors ======= -Setup ------ - -.. include:: service.sample.rst - - Get a flavor ------------ diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst index b2aae4a65..fb68af7f7 100644 --- a/doc/services/compute/images.rst +++ b/doc/services/compute/images.rst @@ -9,11 +9,6 @@ Images Cloud Backup or Cloud Block Storage to ensure availability in case you need to rebuild or restore a server. -Setup ------ - -.. include:: service.sample.rst - List images ----------- diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 94c41979f..207151d50 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -6,6 +6,23 @@ Compute v2 This is a joint service that supports both Rackspace Cloud Servers v2 API, and OpenStack Nova v2 API. +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Compute service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + .. toctree:: images @@ -13,6 +30,7 @@ Compute v2 servers keypairs + Glossary -------- diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst index 4040c0e65..f7e67b113 100644 --- a/doc/services/compute/keypairs.rst +++ b/doc/services/compute/keypairs.rst @@ -1,12 +1,6 @@ Keypairs ======== -Setup ------ - -.. include:: service.sample.rst - - Generate a new keypair ---------------------- diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 4d2f2363c..229e8e4ff 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -1,11 +1,6 @@ Servers ======= -Setup ------ - -.. include:: service.sample.rst - Get server ---------- diff --git a/doc/services/compute/service.sample.rst b/doc/services/compute/service.sample.rst deleted file mode 100644 index c684caf9a..000000000 --- a/doc/services/compute/service.sample.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. include:: ../common/rs-client.sample.rst - -Now, set up the Auto Scale service: - -.. code-block:: php - - $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); - - -``{catalogName}`` is the **name** of the service, as it appears in the service -catalog. For Rackspace users, this will default to `cloudServersOpenStack`; for -OpenStack users, you must set your own value since it can depend on your -environment setup. - -``{region}`` is the Compute region the service will operate in. For Rackspace -users, you can select one of the following from the `supported regions page`. - -``{urlType}`` is the type of URL to use, depending on what endpoints your -catalog provides. For Rackspace, you may use either `internalURL` or `publicURL`. -The former will execute HTTP transactions over the internal Rackspace network, -reducing latency and the overall bandwidth cost - the caveat is that all of your -resources must be in same region. `publicURL`, however, which is the default, -will operate over the public Internet and is to be used for multi-region -installations. From 1647471543be0aff7a202c3e5a21e856a9c2a9ad Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:03 +0100 Subject: [PATCH 569/835] Turn on extension --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 7f9e29414..f49381d42 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -23,7 +23,7 @@ lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) primary_domain = 'php' -extensions = [] +extensions = ['sphinxcontrib.phpdomain'] templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' From b59ba87199414cde05cad4cef63fd54f4e3d57b6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:07 +0100 Subject: [PATCH 570/835] Add regions --- doc/regions.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 doc/regions.rst diff --git a/doc/regions.rst b/doc/regions.rst new file mode 100644 index 000000000..82edf28ec --- /dev/null +++ b/doc/regions.rst @@ -0,0 +1,15 @@ +Rackspace regions +================= + +Below are the supported regions on the Rackspace network: + ++======+===========+ +| code | location | ++======+===========+ +| IAD | Virginia | +| ORD | Chicago | +| DFW | Dallas | +| LON | London | +| SYD | Sydney | +| HKG | Hong Kong | ++------+-----------+ From 3b4329239c705116b65dee2e3749f222db98d336 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:13 +0100 Subject: [PATCH 571/835] Add DNS docs --- doc/services/dns/Domains.md.rst | 290 ------------------ doc/services/dns/Records.md.rst | 111 ------- doc/services/dns/Service.md.rst | 13 - doc/services/dns/domains.rst | 288 +++++++++++++++++ doc/services/dns/index.rst | 51 +++ .../dns/{Limits.md.rst => limits.rst} | 37 +-- doc/services/dns/records.rst | 113 +++++++ .../{Reverse-DNS.md.rst => reverse-dns.rst} | 24 +- 8 files changed, 474 insertions(+), 453 deletions(-) delete mode 100644 doc/services/dns/Domains.md.rst delete mode 100644 doc/services/dns/Records.md.rst delete mode 100644 doc/services/dns/Service.md.rst create mode 100644 doc/services/dns/domains.rst rename doc/services/dns/{Limits.md.rst => limits.rst} (59%) create mode 100644 doc/services/dns/records.rst rename doc/services/dns/{Reverse-DNS.md.rst => reverse-dns.rst} (82%) diff --git a/doc/services/dns/Domains.md.rst b/doc/services/dns/Domains.md.rst deleted file mode 100644 index 824c05099..000000000 --- a/doc/services/dns/Domains.md.rst +++ /dev/null @@ -1,290 +0,0 @@ -Domains -======= - -A domain is an entity/container of all DNS-related information -containing one or more records. - -Setup ------ - -Limit methods will be called on the DNS service, an instance of -``OpenCloud\DNS\Service``. Please see the `DNS service `__ -documentation for setup instructions. - -Get domain ----------- - -To retrieve a specific domain, you will need the domain's **id**, not -its domain name. - -.. code:: php - - $domain = $service->domain(12345); - -If you are having trouble remembering or accessing the domain ID, you -can do a domain list search for your domain and then access its ID. - -List domains ------------- - -These calls provide a list of all DNS domains manageable by a given -account. The resulting list is flat, and does not break the domains down -hierarchically by subdomain. All representative domains are included in -the list, even if a domain is conceptually a subdomain of another domain -in the list. - -.. code:: php - - $domains = $service->domainList(); - - # Return detailed information for each domain - $domains = $service->domainList(true); - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Filter parameters -~~~~~~~~~~~~~~~~~ - -You can filter the aforementioned search by using the ``name`` parameter -in a key/value array supplied as a method argument. For example, -providing ``array('name' => 'hoola.com')`` will return hoola.com and -similar names such as main.hoola.com and sub.hoola.com. - -.. code:: php - - $hoolaDomains = $service->domainList(array( - 'name' => 'hoola.com' - )); - -Filter criteria may consist of: - -- Any letter (A-Za-z) -- Numbers (0-9) -- Hyphen ("-") -- 1 to 63 characters - -Filter criteria should not include any of the following characters: - - ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò - -Finding a domain ID -~~~~~~~~~~~~~~~~~~~ - -If you know a domain's name, but not its unique identifier, you can do -this: - -.. code:: php - - $domains = $service->domainList(array( - 'name' => 'foo.com' - )); - - foreach ($domains as $domain) { - $id = $domain->id; - } - -List domain changes -------------------- - -This call shows all changes to the specified domain since the specified -date/time. The since parameter is optional and defaults to midnight of -the current day. - -.. code:: php - - $changes = $domain->changes(); - - # Changes since last week - $since = date('c', strtotime('last week')); - $changes = $domain->changes($since); - - foreach ($changes->changes as $change) { - printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); - - foreach ($change->changeDetails as $detail) { - printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); - } - } - -Export domain -------------- - -This call provides the BIND (Berkeley Internet Name Domain) 9 formatted -contents of the requested domain. This call is for a single domain only, -and as such, does not traverse up or down the domain hierarchy for -details (that is, no subdomain information is provided). - -.. code:: php - - $asyncResponse = $domain->export(); - $body = $asyncResponse->waitFor('COMPLETED'); - echo $body['contents']; - -Create domain -------------- - -A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` -records) and an optional list of sub-domains. You will need to specify -these before creating the domain itself: - -.. code:: php - - // get empty object - $domain = $service->domain(); - - // add A record - $aRecord = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 - )); - $domain->addRecord($aRecord); - - // add optional C record - $cRecord = $domain->record(array( - 'type' => 'CNAME', - 'name' => 'www.example.com', - 'data' => 'example.com', - 'ttl' => 3600 - )); - $domain->addRecord($cRecord); - - // add optional MX record - $mxRecord = $domain->record(array( - 'type' => 'MX', - 'data' => 'mail.example.com', - 'name' => 'example.com', - 'ttl' => 3600, - 'priority' => 5 - )); - $domain->addRecord($mxRecord); - - // add optional NS records - $nsRecord1 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns1.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 - )); - $domain->addRecord($nsRecord1); - - $nsRecord2 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns2.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 - )); - $domain->addRecord($nsRecord2); - - // add optional subdomains - $sub1 = $domain->subdomain(array( - 'emailAddress' => 'foo@example.com', - 'name' => 'dev.example.com', - 'comment' => 'Dev portal' - )); - $domain->addSubdomain($sub1); - - // send to API - $domain->create(array( - 'emailAddress' => 'webmaster@example.com', - 'ttl' => 3600, - 'name' => 'example.com', - 'comment' => 'Optional comment' - )); - -Clone domain ------------- - -This call will duplicate a single existing domain configuration with a -new domain name for the specified Cloud account. By default, all records -and, optionally, subdomain(s) are duplicated as well. - -The method signature you will need to use is: - -.. code:: php - - cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) - -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Name | Data type | Default | Description | -+======================+==============+============+====================================================================================================================================================================================+ -| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -For example: - -.. code:: php - - $asyncResponse = $domain->cloneDomain('new-name.com', true); - -Import domain -------------- - -This call provisions a new DNS domain under the account specified by the -BIND 9 formatted file configuration contents defined in the request -object. - -You will need to ensure that the BIND 9 formatted file configuration -contents are valid by adhering to the following rules: - -- Each record starts on a new line and on the first column. If a record - will not fit on one line, use the BIND\_9 line continuation - convention where you put a left parenthesis and continue the one - record on the next line and put a right parenthesis when the record - ends. For example, - - example2.net. 3600 IN SOA dns1.stabletransit.com. ( - sample@rackspace.com. 1308874739 3600 3600 3600 3600) - -- The attribute values of a record must be separated by a single blank - or tab. No other white space characters. - -- If there are any NS records, the data field should not be - dns1.stabletransit.com or dns2.stabletransit.com. They will result in - "duplicate record" errors. - -For example: - -.. code:: php - - $bind9Data = <<import($bind9Data); - -Modify domain -------------- - -This call modifies DNS domain(s) attributes only. Only the TTL, email -address and comment attributes of a domain can be modified. Records -cannot be added, modified, or removed through this API operation - you -will need to use the `add -records `__, `modify -records `__ or `remove -records `__ operations -respectively. - -.. code:: php - - $domain->update(array( - 'ttl' => ($domain->ttl + 100), - 'emailAddress' => 'new_dev@foo.com' - )); - -Remove domain -------------- - -.. code:: php - - $domain->delete(); - diff --git a/doc/services/dns/Records.md.rst b/doc/services/dns/Records.md.rst deleted file mode 100644 index 4e492e8ef..000000000 --- a/doc/services/dns/Records.md.rst +++ /dev/null @@ -1,111 +0,0 @@ -Records -======= - -A DNS record belongs to a particular domain and is used to specify -information about the domain. - -There are several types of DNS records. Examples include mail exchange -(MX) records, which specify the mail server for a particular domain, and -name server (NS) records, which specify the authoritative name servers -for a domain. - -It is represented by the ``OpenCloud\DNS\Resource\Record`` class. -Records belong to a `Domain `__. - -Get record ----------- - -In order to retrieve details for a specific DNS record, you will need -its **id**: - -.. code:: php - - $record = $domain->record('NS-1234567'); - -If you do not have this ID at your disposal, you can traverse the record -collection and do a string comparison (detailed below). - -List records ------------- - -This call lists all records configured for the specified domain. - -.. code:: php - - $records = $domain->recordList(); - - foreach ($records as $record) { - printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); - } - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Query parameters -~~~~~~~~~~~~~~~~ - -You can pass in an array of query parameters for greater control over -your search: - -+------------+--------------+------------------------+---------------+ -| Name | Data type | Default | Description | -+============+==============+========================+===============+ -| ``type`` | ``string`` | The record type | -+------------+--------------+------------------------+---------------+ -| ``name`` | ``string`` | The record name | -+------------+--------------+------------------------+---------------+ -| ``data`` | ``string`` | Data for this record | -+------------+--------------+------------------------+---------------+ - -Find a record ID from its name -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For example: - -.. code:: php - - $records = $domain->recordList(array( - 'name' => 'imap.example.com', - 'type' => 'MX' - )); - - foreach ($records as $record) { - $recordId = $record->id; - } - -Add record ----------- - -This call adds a new record to the specified domain: - -.. code:: php - - $record = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 - )); - - $record->create(); - -Please be aware that records that are added with a different hostname -than the parent domain might fail silently. - -Modify record -------------- - -.. code:: php - - $record = $domain->record(123456); - $record->ttl -= 100; - $record->update(); - -Delete record -------------- - -.. code:: php - - $record->delete(); - diff --git a/doc/services/dns/Service.md.rst b/doc/services/dns/Service.md.rst deleted file mode 100644 index 29ea79193..000000000 --- a/doc/services/dns/Service.md.rst +++ /dev/null @@ -1,13 +0,0 @@ -DNS Service -=========== - -To instantiate a Compute service object, you first need to setup a -Rackspace/OpenStack client. To do this, or for more information, please -consult the `Clients documentation <../Clients.md>`__. - -You will then need to run: - -.. code:: php - - $service = $client->dnsService(); - diff --git a/doc/services/dns/domains.rst b/doc/services/dns/domains.rst new file mode 100644 index 000000000..0a7b83b96 --- /dev/null +++ b/doc/services/dns/domains.rst @@ -0,0 +1,288 @@ +Domains +======= + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code-block:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the search by using the ``name`` parameter in a key/value array +supplied as a method argument. For example, to retrieve domains named ``foo.com``, +along with any subdomains like ``bar.foo.com``: + +.. code-block:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'foo.com' + )); + +Filter criteria may consist of: + +* Any letter (A-Za-z) +* Numbers (0-9) +* Hyphen ("-") +* 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +Once you have a list of domains, to retrieve a domain's ID: + +.. code-block:: php + + foreach ($domains as $domain) { + $id = $domain->id; + } + + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. To list all available changes for a domain for the current day: + +.. code-block:: php + + $changes = $domain->changes(); + + +For more granular control, you can manually define the ``since`` parameter like +so: + +.. code-block:: php + + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + +Once you have a set of changes, you can iterate over them like so: + +.. code-block:: php + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + + +Create domain +------------- + +The first thing you will need to do is instantiate a new object and set the +primary ``A`` record for the DNS domain, like so: + +.. code-block:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $domain->addRecord($aRecord); + +You also have the option of adding more types of DNS records such as ``CNAME``, +``MX`` and ``NS`` records. This step is completely optional and depends on +your requirements: + +.. code-block:: php + + // add CNAME record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add NS record + $nsRecord = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord); + +You can also add sub-domains to your new DNS domain. Again, this is completely +optional: + +.. code-block:: php + + $subdomain = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($subdomain); + +Once you've finished configuring how your DNS domain will work, you're ready +to specify the essential details and send it to the API for creation: + +.. code-block:: php + + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + + +Clone domain +------------ + +This call will duplicate an existing domain under a new name. By default, all +records and, optionally, subdomains are duplicated as well. + +The method signature you will need to use is: + +.. function:: cloneDomain( $newDomainName[, $subdomains[, $comments[, $email[, $records]]]] ) + + Clone a domain + + :param string $newDomainName: The name of the new domain entry + :param bool $subdomains: Set to ``true`` to clone all the subdomains for this domain + :param bool $comments: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. + :param bool $email: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + :param bool $records: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + + +For example: + +.. code-block:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true, false, true, false); + + +Export domain +------------- + +This call provides access to the `BIND `_ +(Berkeley Internet Name Domain) 9 for the requested domain. This call is for a +single domain only, and as such, does not traverse up or down the domain +hierarchy for details: + +.. code-block:: php + + $asyncResponse = $domain->export(); + + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + + +Import domain +------------- + +This operation will create a new DNS domain according to a `BIND `_ +(Berkeley Internet Name Domain) 9 formatted value. + +In order for the BIND value to be considered valid, it needs to adhere to the +following rules: + +* Each record starts on a new line and on the first column. If a record will + not fit on one line, use the BIND\_9 line continuation convention where you put + a left parenthesis and continue the one record on the next line and put a right + parenthesis when the record ends. For example: + + example2.net. 3600 IN SOA dns1.stabletransit.com. (sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +* The attribute values of a record must be separated by a single blank or tab. + No other white space characters. + +* If there are any NS records, the data field should not be + ``dns1.stabletransit.com`` or ``dns2.stabletransit.com``. They will result in + "duplicate record" errors. + +For example: + +.. code-block:: php + + $bind9Data = <<import($bind9Data); + + +Modify domain +------------- + +Only the TTL, email address and comment attributes of a domain can be modified. +Records cannot be added, modified, or removed through this API operation - you +will need to use the `add records `__, `modify records +`__ or `remove records `__ +operations respectively. + +.. code-block:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + + +Delete domain +------------- + +.. code-block:: php + + $domain->delete(); diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index e69de29bb..4caca9fe5 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -0,0 +1,51 @@ +DNS v1 +====== + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the DNS service: + +.. code-block:: php + + $service = $client->dnsService(); + + +Operations +---------- + +.. toctree:: + + records + domains + limits + reverse-dns + + +Glossary +-------- + + domain + A domain is an entity/container of all DNS-related information containing + one or more records. + + record + A DNS record belongs to a particular domain and is used to specify + information about the domain. There are several types of DNS records. Each + record type contains particular information used to describe that record's + purpose. Examples include mail exchange (MX) records, which specify the + mail server for a particular domain, and name server (NS) records, which + specify the authoritative name servers for a domain. + + subdomain + Subdomains are domains within a parent domain, and subdomains cannot be + registered. Subdomains allow you to delegate domains. Subdomains can + themselves have subdomains, so third-level, fourth-level, fifth-level, and + deeper levels of nesting are possible. + + pointer records + DNS usually determines an IP address associated with a domain name. + Reverse DNS is the opposite process: resolving a domain name from an IP + address. This is usually achieved with a domain name pointer. diff --git a/doc/services/dns/Limits.md.rst b/doc/services/dns/limits.rst similarity index 59% rename from doc/services/dns/Limits.md.rst rename to doc/services/dns/limits.rst index 72f8219a0..289c3888d 100644 --- a/doc/services/dns/Limits.md.rst +++ b/doc/services/dns/limits.rst @@ -1,22 +1,15 @@ Limits ====== -Setup ------ - -Limit methods will be called on the DNS service, an instance of -``OpenCloud\DNS\Service``. Please see the `DNS service `__ -documentation for setup instructions. - List all limits --------------- -This call provides a list of all applicable limits for the specified -account. +This call provides a list of all applicable limits for the specified account. .. code:: php - $limits = $service->limits(); + $limits = $service->limits(); + Absolute limits ~~~~~~~~~~~~~~~ @@ -29,12 +22,13 @@ domain: $absoluteLimits = $limits->absolute; - # Domain limit + // Domain limit echo $absoluteLimits->domains; - # Record limit per domain + // Record limit per domain echo $absoluteLimits->{'records per domain'}; + List limit types ---------------- @@ -42,20 +36,17 @@ To find out the different limit types you can query, run: .. code:: php - $limitTypes = $service->limitTypes(); + $limitTypes = $service->limitTypes(); will return: :: - array(3) { - [0] => - string(10) "RATE_LIMIT" - [1] => - string(12) "DOMAIN_LIMIT" - [2] => - string(19) "DOMAIN_RECORD_LIMIT" - } + array(3) { + [0] => string(10) "RATE_LIMIT" + [1] => string(12) "DOMAIN_LIMIT" + [2] => string(19) "DOMAIN_RECORD_LIMIT" + } Query a specific limit ---------------------- @@ -63,8 +54,4 @@ Query a specific limit .. code:: php $limit = $service->limits('DOMAIN_LIMIT'); - echo $limit->absolute->limits->value; - - >>> 500 - diff --git a/doc/services/dns/records.rst b/doc/services/dns/records.rst new file mode 100644 index 000000000..26e17ee5e --- /dev/null +++ b/doc/services/dns/records.rst @@ -0,0 +1,113 @@ +Records +======= + +Setup +----- + +In order to interact with the functionality of records, you must first +retrieve the details of the domain itself. To do this, you must substitute +`{domainId}` for your domain's ID: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('{recordId}'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+ +| Name | Data type | Description | ++============+==============+========================+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+ + + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record('{recordId}'); + $record->ttl -= 100; + $record->update(); + + +Delete record +------------- + +.. code:: php + + $record->delete(); diff --git a/doc/services/dns/Reverse-DNS.md.rst b/doc/services/dns/reverse-dns.rst similarity index 82% rename from doc/services/dns/Reverse-DNS.md.rst rename to doc/services/dns/reverse-dns.rst index b8a5c0e76..4d0eb671c 100644 --- a/doc/services/dns/Reverse-DNS.md.rst +++ b/doc/services/dns/reverse-dns.rst @@ -1,9 +1,6 @@ Reverse DNS =========== -DNS usually determines an IP address associated with a domain name. -Reverse DNS is the opposite process: resolving a domain name from an IP -address. This is usually achieved with a domain name pointer. Get PTR record -------------- @@ -20,14 +17,15 @@ formed resource object in order to retrieve either one's PTR record: 'parent' => $parent )); -So, in the above example, a ``$parent`` could be an instance of +So, in the above example, the ``$parent`` object could be an instance of ``OpenCloud\Compute\Resource\Server`` or ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please -consult the `server documentation <../Compute/Server.md>`__ and `load -balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +consult the `server documentation <../compute>`__ and `load +balancer documentation <../load-balancer>`__ for more detailed usage instructions. + List PTR records ---------------- @@ -41,9 +39,6 @@ List PTR records } -Please consult the `iterator -documentation `__ for more information -about iterators. Add PTR record -------------- @@ -78,19 +73,20 @@ Here is a table that explains the above attributes: | comment | If included, its length must be less than or equal to 160 characters. | No | +-----------+------------------------------------------------------------------------------------+------------+ + Modify PTR record ----------------- .. code:: php - $ptr->update(array( - 'ttl' => $ptr->ttl * 2 - )); + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + Delete PTR record ----------------- .. code:: php - $ptr->delete(); - + $ptr->delete(); From 9b5dfa58af8a4ec1f89cc790254b7a33e9d970d4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:23 +0100 Subject: [PATCH 572/835] Add DB docs --- doc/services/database/README.md.rst | 125 ------------------ doc/services/database/configurations.rst | 127 +++++++++++++++++++ doc/services/database/databases.rst | 60 +++++++++ doc/services/database/datastores.rst | 59 +++++++++ doc/services/database/index.rst | 69 ++++++++++ doc/services/database/instances.rst | 155 +++++++++++++++++++++++ doc/services/database/users.rst | 67 ++++++++++ 7 files changed, 537 insertions(+), 125 deletions(-) delete mode 100644 doc/services/database/README.md.rst create mode 100644 doc/services/database/configurations.rst create mode 100644 doc/services/database/databases.rst create mode 100644 doc/services/database/datastores.rst create mode 100644 doc/services/database/instances.rst create mode 100644 doc/services/database/users.rst diff --git a/doc/services/database/README.md.rst b/doc/services/database/README.md.rst deleted file mode 100644 index 3f6bdd3c2..000000000 --- a/doc/services/database/README.md.rst +++ /dev/null @@ -1,125 +0,0 @@ -Databases -========= - -A **cloud database** is a MySQL relational database service that allows -customers to programatically provision database instances of varying -virtual resource sizes without the need to maintain and/or update MySQL. - -Getting started ---------------- - -1. Instantiate a Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - use OpenCloud\Rackspace; - use OpenCloud\Common\Constants\State; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Create a database server instance. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $databaseService = $client->databaseService('cloudDatabases', 'DFW'); - - $twoGbFlavor = $databaseService->flavor(3); - - $dbInstance = $databaseService->instance(); - $dbInstance->name = 'Demo database instance'; - $dbInstance->volume = new stdClass(); - $dbInstance->volume->size = 20; // GB - $dbInstance->flavor = $twoGbFlavor; - $dbInstance->create(); - - $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { - - printf("Database instance build status: %s\n", $dbInstance->status); - - }); - -The example above creates a database server instance with 20GB of disk -space and 2GB of memory, then waits for it to become ACTIVE. - -3. Create a database on the database server instance. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $db = $dbInstance->database(); - $db->name = 'demo_db'; - - $db->create(); - -The example above creates a database named ``demo_db`` on the database -server instance created in the previous step. - -4. Create database user and give it access to database. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $user = $dbInstance->user(); - $user->name = 'demo_user'; - $user->password = 'h@X0r!'; - $user->databases = array('demo_db'); - - $user->create(); - -The example above creates a database user named ``demo_user``, sets its -password and gives it access to the ``demo_db`` database created in the -previous step. - -5. Optional step: Create a load balancer to allow access to the database from the Internet. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The database created in the previous step can only be accessed from the -Rackspace private network (aka ``SERVICENET``). If you have a cloud -server instance in the same region as the database server instance, you -will be able to connect to the database from that cloud server instance. - -If, however, you would like to access the database from the Internet, -you will need to create a load balancer with an IP address that is -routable from the Internet and attach the database server instance as a -back-end node of this load balancer. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $loadBalancer->name = 'Load balancer - DB'; - $loadBalancer->addNode($dbInstance->hostname, 3306); - $loadBalancer->port = 3306; - $loadBalancer->protocol = 'MYSQL'; - $loadBalancer->addVirtualIp('PUBLIC'); - - $loadBalancer->create(); - - $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { - printf("Load balancer build status: %s\n", $lb->status); - }); - - foreach ($loadBalancer->virtualIps as $vip) { - if ($vip->type == 'PUBLIC') { - printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); - } - } - -In the example above, a load balancer is created with the database -server instance as its only back-end node. Further, this load balancer -is configured to listen for MySQL connections on port 3306. Finally a -virtual IP address (VIP) is configured in the ``PUBLIC`` network address -space so that this load balancer may receive connections from the -Internet. - -Once the load balancer is created and becomes ``ACTIVE``, it's -Internet-accessible IP addresses are printed out. If you connect to any -of these IP addresses on port 3306 using the MySQL protocol, you will be -connected to the database created in step 3. diff --git a/doc/services/database/configurations.rst b/doc/services/database/configurations.rst new file mode 100644 index 000000000..edfb9dc89 --- /dev/null +++ b/doc/services/database/configurations.rst @@ -0,0 +1,127 @@ +Configurations +============== + +Creating a configuration +------------------------ + +.. code-block:: php + + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + $configuration = $service->configuration(); + + $configuration->create(array( + 'name' => 'example-configuration-name', + 'description' => 'An example configuration', + 'values' => array( + 'collation_server' => 'latin1_swedish_ci', + 'connect_timeout' => 120 + ), + 'datastore' => array( + 'type' => '10000000-0000-0000-0000-000000000001', + 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' + ) + )); + +`Get the executable PHP script for this example `__ + + +Listing configurations +---------------------- + +You can list out all the configurations you have created as shown below: + +.. code-block:: php + + $configurations = $service->configurationList(); + foreach ($configurations as $configuration) { + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a configuration +-------------------------- + +You can retrieve a specific configuration, using its ID, as shown below: + +.. code-block:: php + + $configuration = $service->configuration('{configId}'); + /** @var OpenCloud\Database\Resource\Configuration **/ + +`Get the executable PHP script for this example `__ + + +Updating a configuration +------------------------ + +You have two choices when updating a configuration: + +* you can `patch a configuration <#patching-a-configuration>`__ to change only +some configuration parameters +* you can `entirely replace a configuration <#replacing-a-configuration>`__ to +replace all configuration parameters with new ones + + +Patching a configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can patch a configuration as shown below: + +.. code-block:: php + + $configuration->patch(array( + 'values' => array( + 'connect_timeout' => 30 + ) + )); + +`Get the executable PHP script for this example `__ + + +Replacing a configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can replace a configuration as shown below: + +.. code-block:: php + + $configuration->update(array( + 'values' => array( + 'collation_server' => 'utf8_general_ci', + 'connect_timeout' => 60 + ) + )); + +`Get the executable PHP script for this example `__ + + +Deleting a configuration +------------------------ + +.. code-block:: php + + $configuration->delete(); + +`Get the executable PHP script for this example `__ + +.. note:: + + You cannot delete a configuration if it is in use by a running instance. + + +Listing instances using a configuration +--------------------------------------- + +You can list all instances using a specific configuration, using its ID, +as shown below: + +.. code-block:: php + + $instances = $configuration->instanceList(); + foreach ($instances as $instance) { + /** @var $instance OpenCloud\Database\Resource\Instance **/ + } + +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/databases.rst b/doc/services/database/databases.rst new file mode 100644 index 000000000..83d07c5e2 --- /dev/null +++ b/doc/services/database/databases.rst @@ -0,0 +1,60 @@ +Databases +========= + +Setup +----- + +In order to interact with the functionality of databases, you must first +retrieve the details of the instance itself. To do this, you must substitute +`{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating a new database +----------------------- + +To create a new database, you must supply it with a name; you can +optionally specify its character set and collating sequence: + +.. code-block:: php + + // Create an empty object + $database = $instance->database(); + + // Send to API + $database->create(array( + 'name' => 'production', + 'character_set' => 'utf8', + 'collate' => 'utf8_general_ci' + )); + +You can find values for ``character_set`` and ``collate`` at `the MySQL +website `__. + + +Deleting a database +------------------- + +.. code-block:: php + + $database->delete(); + +.. note:: + + This is a destructive operation: all your data will be wiped away and will + not be retrievable. + + +Listing databases +----------------- + +.. code-block:: php + + $databases = $service->databaseList(); + + foreach ($databases as $database) { + /** @param $database OpenCloud\Database\Resource\Database */ + } diff --git a/doc/services/database/datastores.rst b/doc/services/database/datastores.rst new file mode 100644 index 000000000..6ca45daed --- /dev/null +++ b/doc/services/database/datastores.rst @@ -0,0 +1,59 @@ +Datastores +========== + +Listing datastores +------------------ + +You can list out all the datastores available as shown below: + +.. code-block:: php + + $datastores = $service->datastoreList(); + foreach ($datastores as $datastore) { + /** @var $datastore OpenCloud\Database\Resource\Datastore **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore +---------------------- + +You can retrieve a specific datastore's information, using its ID, as +shown below: + +.. code-block:: php + + /** @var OpenCloud\Database\Resource\Datastore **/ + $datastore = $service->datastore('{datastoreId}'); + +`Get the executable PHP script for this example `__ + + +Listing datastore versions +-------------------------- + +You can list out all the versions available for a specific datastore, as +shown below: + +.. code-block:: php + + $versions = $datastore->versionList(); + foreach ($versions as $version) { + /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore version +------------------------------ + +You a retrieve a specific datastore version, using its ID, as shown +below: + +.. code-block:: php + + $datastoreVersion = $datastore->version('{versionId}'); + +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index e69de29bb..850e56c8a 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -0,0 +1,69 @@ +Databases v1 +============ + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Database service: + +.. code-block:: php + + $service = $client->databaseService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + instances + databases + users + datastores + + +Glossary +-------- + +.. glossary:: + + configuration group + A configuration group is a collection of key/value pairs which configure a + database instance. Some directives are capable of being applied dynamically, + while other directives require a server restart to take effect. The + configuration group can be applied to an instance at creation or applied to + an existing instance to modify the behavior of the running datastore on the + instance. + + flavor + A flavor is an available hardware configuration for a database instance. + Each flavor has a unique combination of memory capacity and priority for + CPU time. + + instance + A database instance is an isolated MySQL instance in a single tenant + environment on a shared physical host machine. Also referred to as + instance. + + database + A database is a local MySQL database running on an instance. + + user + A user is a local MySQL user that can access a database running on an + instance. + + datastore + The database engine running on your instance. Currently, there is support + for MySQL 5.6, MySQL 5.1, Percona 5.6 and MariaDB 10. + + volume + A volume is user-specified storage that contains the database engine data + directory. Volumes are automatically provisioned on shared Internet Small + Computer System Interface (iSCSI) storage area networks (SAN) that provide + for increased performance, scalability, availability and manageability. + Applications with high I/O demands are performance optimized and data is + protected through both local and network RAID-10. diff --git a/doc/services/database/instances.rst b/doc/services/database/instances.rst new file mode 100644 index 000000000..082e56c05 --- /dev/null +++ b/doc/services/database/instances.rst @@ -0,0 +1,155 @@ +Instances +========= + +Create a new instance +--------------------- + +.. code-block:: php + + // Create an empty object + $instance = $service->instance(); + + // Send to the API + $instance->create(array( + 'name' => '{name}', + 'flavor' => $service->flavor('{flavorId}'), + 'volume' => array('size' => 4) // 4GB of volume disk + )); + +`Get the executable PHP script for this sample `__ + + +Waiting for the instance to build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The SDK provides a blocking operation that will wait until your instance resource +has transitioned into an ``ACTIVE`` state. During this period, it will +continuously poll the API and break the loop when the state has been achieved: + +.. code-block:: php + + $instance->waitFor('ACTIVE', null, function ($instance) { + // This will be executed continuously + printf("Database instance build status: %s\n", $instance->status); + }); + + +Connecting an instance to a load balancer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The instance created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code-block:: php + + $lbService = $client->loadBalancerService(null, '{region}'); + + // Create empty object + $loadBalancer = $lbService->loadBalancer(); + + // Associate this LB with the instance as a "node" + $loadBalancer->addNode($instance->hostname, 3306); + $loadBalancer->addVirtualIp('PUBLIC'); + + // Configure other parameters and send to the API + $loadBalancer->create(array( + 'name' => 'DB Load Balancer', + 'port' => 3306, + 'protocol' => 'MYSQL', + )); + + // Wait for the resource to create + $loadBalancer->waitFor('ACTIVE', null, function ($loadBalancer) { + printf("Load balancer build status: %s\n", $loadBalancer->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. + + +Retrieving an instance +---------------------- + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + +`Get the executable PHP script for this example `__ + + +Updating an instance +-------------------- + +An instance can be updated to use a specific `configuration `__ as shown below. + +.. code-block:: php + + $instance->update(array( + 'configuration' => '{configurationId}' + )); + +.. note:: + + If any parameters in the associated configuration require a restart, then you + will need to `restart the instance <#restarting-an-instance>`__ after the update. + + +Deleting an instance +-------------------- + +.. code-block:: php + + $instance->delete(); + + +Restarting an instance +---------------------- + +.. code-block:: php + + $instance->restart(); + + +Resizing an instance's RAM +-------------------------- + +To change the amount of RAM allocated to the instance: + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + $instance->resize($flavor); + + +Resizing an instance's volume +----------------------------- + +You can also independently change the volume size to increase the disk +space: + +.. code-block:: php + + // Increase to 8GB disk + $instance->resizeVolume(8); diff --git a/doc/services/database/users.rst b/doc/services/database/users.rst new file mode 100644 index 000000000..0343c4904 --- /dev/null +++ b/doc/services/database/users.rst @@ -0,0 +1,67 @@ +Users +===== + +Setup +----- + +Finally, in order to interact with the functionality of databases, you must +first retrieve the details of the instance itself. To do this, you must +substitute `{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating users +-------------- + +Database users exist at the ``Instance`` level, but can be associated +with a specific ``Database``. They are represented by the +``OpenCloud\Database\Resource\User`` class. + +.. code-block:: php + + // New instance of OpenCloud\Database\Resource\User + $user = $instance->user(); + + // Send to API + $user->create(array( + 'name' => 'Alice', + 'password' => 'fooBar' + 'databases' => array('production') + )); + + +Deleting a user +--------------- + +.. code-block:: php + + $user->delete(); + + +The root user +------------- + +By default, Cloud Databases does not enable the root user. In most +cases, the root user is not needed, and having one can leave you open to +security violations. However, if you do want to enable access to the root user: + +.. code-block:: php + + $rootUser = $instance->enableRootUser(); + + +This returns a regular ``User`` object with the ``name`` attribute set +to ``root`` and the ``password`` attribute set to an auto-generated +password. + + +Check if root user is enabled +----------------------------- + +.. code-block:: php + + // true for yes, false for no + $instance->isRootEnabled(); From fb165638ed2cd5b11e0696509794c983d0eff903 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 15:16:07 +0100 Subject: [PATCH 573/835] Identity docs --- doc/services/identity/Service.md.rst | 35 ----- doc/services/identity/Tenants.md.rst | 29 ---- doc/services/identity/Tokens.md.rst | 105 ------------- doc/services/identity/index.rst | 46 ++++++ .../identity/{Roles.md.rst => roles.rst} | 0 doc/services/identity/tenants.rst | 26 ++++ doc/services/identity/tokens.rst | 101 +++++++++++++ .../identity/{Users.md.rst => users.rst} | 139 ++++++++---------- 8 files changed, 238 insertions(+), 243 deletions(-) delete mode 100644 doc/services/identity/Service.md.rst delete mode 100644 doc/services/identity/Tenants.md.rst delete mode 100644 doc/services/identity/Tokens.md.rst rename doc/services/identity/{Roles.md.rst => roles.rst} (100%) create mode 100644 doc/services/identity/tenants.rst create mode 100644 doc/services/identity/tokens.rst rename doc/services/identity/{Users.md.rst => users.rst} (79%) diff --git a/doc/services/identity/Service.md.rst b/doc/services/identity/Service.md.rst deleted file mode 100644 index f7e8e12ba..000000000 --- a/doc/services/identity/Service.md.rst +++ /dev/null @@ -1,35 +0,0 @@ -Identity service -================ - -Intro ------ - -The Identity service is regionless, so you do not need to specify a -region when instantiating the service object. Although this was -primarily based on Rackspace's implementation of Cloud Identity, it -should also work for OpenStack Keystone. - -A note on object creation -------------------------- - -Normally, when services are created the client handles authenticates -automatically. But because Keystone/Identity is fundamental to the -authentication process itself, it proves difficult to do this procedure -as its normally done. For this reason, you have two options when -creating the service object: - -1: Use the client's factory method - -.. code:: php - - $identity = $client->identityService(); - -2: Authenticate manually - -.. code:: php - - use OpenCloud\Identity\Service as IdentityService; - - $identity = IdentityService::factory($client); - $identity->getClient()->authenticate(); - diff --git a/doc/services/identity/Tenants.md.rst b/doc/services/identity/Tenants.md.rst deleted file mode 100644 index 9b58efd1a..000000000 --- a/doc/services/identity/Tenants.md.rst +++ /dev/null @@ -1,29 +0,0 @@ -Tenants -======= - -Intro ------ - -A tenant is a container used to group or isolate resources and/or -identity objects. Depending on the service operator, a tenant may map to -a customer, account, organization, or project. - -Setup ------ - -Tenant objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -List tenants ------------- - -.. code:: php - - $tenants = $service->getTenants(); - - foreach ($tenants as $tenant) { - // ... - } - -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Tokens.md.rst b/doc/services/identity/Tokens.md.rst deleted file mode 100644 index c42ce1573..000000000 --- a/doc/services/identity/Tokens.md.rst +++ /dev/null @@ -1,105 +0,0 @@ -Tokens -====== - -Intro ------ - -A token is an opaque string that represents an authorization to access -cloud resources. Tokens may be revoked at any time and are valid for a -finite duration. - -Setup ------ - -Token objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -Useful object properties/methods --------------------------------- - -+------------+-------------------------------------------+----------------------------------------+--------------------+ -| Property | Description | Getter | Setter | -+============+===========================================+========================================+====================+ -| id | The unique ID of the token | ``getId()`` | ``setId()`` | -+------------+-------------------------------------------+----------------------------------------+--------------------+ -| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | -+------------+-------------------------------------------+----------------------------------------+--------------------+ - -Create token (authenticate) ---------------------------- - -In order to generate a token, you must pass in the JSON template that is -sent to the API. This is because Rackspace's operation expects a -slightly different entity body than OpenStack Keystone. - -Request body for Rackspace's generate token operation: - -.. code:: json - - { - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "foo", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1100111" - } - } - -Request body for Keystone's generate token operation: - -.. code:: json - - { - "auth": { - "passwordCredentials":{ - "username":"demoauthor", - "password":"theUsersPassword" - }, - "tenantId": "12345678" - } - } - -The only real differences you'll notice is the name of the object key -(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret -(``apiKey``/``password``). The ``tenantId`` property in both templates -are optional. You can also add ``tenantName`` too. - -.. code:: php - - use OpenCloud\Common\Http\Message\Formatter; - - $template = sprintf( - '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', - 'my_username', - 'my_api_key' - ); - - $response = $service->generateToken($template); - - $body = Formatter::decode($response); - - // service catalog - $catalog = $body->access->serviceCatalog; - - // token - $token = $body->access->token; - - // user - $user = $body->access->user; - -As you will notice, these variables will be stdClass objects - for fully -fledged functionality, let the client authenticate by itself because it -ends up stocking the necessary models for you. - -To see the response body structure, consult the `official -docs `__. - -Revoke token (destroy session) ------------------------------- - -.. code:: php - - $tokenId = '1234567'; - $service->revokeToken($tokenId); - diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index e69de29bb..35c27131b 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -0,0 +1,46 @@ +Identity v2 +=========== + +Setup +----- + +.. include:: ../common/rs-client.sample + +.. code-block:: php + + $service = $client->identityService(); + + +Operations +---------- + +.. toctree:: + + tokens + users + tenants + +Glossary +-------- + +.. glossary:: + + token + A token is an opaque string that represents an authorization to access + cloud resources. Tokens may be revoked at any time and are valid for a + finite duration. + + tenant + A tenant is a container used to group or isolate resources and/or + identity objects. Depending on the service operator, a tenant may map to + a customer, account, organization, or project. + + user + A user is a digital representation of a person, system, or service who + consumes cloud services. Users have credentials and may be assigned + tokens; based on these credentials and tokens, the authentication + service validates that incoming requests are being made by the user who + claims to be making the request, and that the user has the right to + access the requested resources. Users may be directly assigned to a + particular tenant and behave as if they are contained within that + tenant. diff --git a/doc/services/identity/Roles.md.rst b/doc/services/identity/roles.rst similarity index 100% rename from doc/services/identity/Roles.md.rst rename to doc/services/identity/roles.rst diff --git a/doc/services/identity/tenants.rst b/doc/services/identity/tenants.rst new file mode 100644 index 000000000..a27417cca --- /dev/null +++ b/doc/services/identity/tenants.rst @@ -0,0 +1,26 @@ +Tenants +======= + +List tenants +------------ + +.. code-block:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +Tenant object properties and methods +------------------------------------ + +Once you have a ``OpenCloud\Identity\Resource\Tenant`` object, you can retrieve +information like so: + +.. code-block:: php + + $tenant->getId(); + $tenant->getName(); + $tenant->getDescription(); + $tenant->isEnabled(); diff --git a/doc/services/identity/tokens.rst b/doc/services/identity/tokens.rst new file mode 100644 index 000000000..f49484105 --- /dev/null +++ b/doc/services/identity/tokens.rst @@ -0,0 +1,101 @@ +Tokens +====== + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +To do this, and then generate a token: + +.. code-block:: php + + $json = $client->getCredentials(); + + /** @var $response Guzzle\Http\Message\Response */ + $response = $service->generateToken($json); + $jsonBody = $response->json(); + +When a token is generated by the API, there are a few things returned: + +* a `service catalog `_ + outlining all of the services you can interact with, + including their names, service types, and endpoint URLs. Which services + make up your catalog, and how your catalog is structured, will depend on + your service provider. + +* details about your token, such as its ID, created and expiration date + +* details about your user account + +* details about your tenant + +Interacting with the service catalog +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have the ``$jsonBody``, you can construct a ``Catalog`` object for +easier interaction: + +.. code-block:: php + + $data = $jsonBody->access->serviceCatalog; + $catalog = OpenCloud\Common\Service\Catalog::factory($data); + + foreach ($catalog->getItems() as $service) { + /** @param $service OpenCloud\Common\Service\CatalogItem */ + printf("Catalog item: Name [%s] Type [%s]\n", $service->getName(), $service->getType()); + + foreach ($service->getEndpoints() as $endpoint) { + printf(" Endpoint provided: Region [%s] PublicURL [%s] PrivateURL [%s]\n", + $endpoint->getRegion(), $endpoint->getPublicUrl(), $endpoint->getPrivateUrl()); + } + } + +Interacting with tokens +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->token; + $token = $service->resource('Token', $data); + + printf("Token ID: %s - Token expiry %s", $token->getId(), $token->getExpires()); + + if ($token->hasExpired()) { + // ... + } + +Interacting with users +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->user; + $user = $service->resource('User', $data); + +To see which methods you can call on ``$user`` (which implements +``OpenCloud\Identity\Resource\User``), see our `user documentation `_ +which accompanies this guide. + + +Interacting with tenants +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->tenant; + $tenant = $service->resource('Tenant', $data); + +To see which methods you can call on ``$tenant`` (which implements +``OpenCloud\Identity\Resource\Tenant``), see our `user documentation `_ +which accompanies this guide. + + +Revoke token (destroy session) +------------------------------ + +.. code-block:: php + + $service->revokeToken('{tokenId}'); diff --git a/doc/services/identity/Users.md.rst b/doc/services/identity/users.rst similarity index 79% rename from doc/services/identity/Users.md.rst rename to doc/services/identity/users.rst index e8c2e6d63..b55236a74 100644 --- a/doc/services/identity/Users.md.rst +++ b/doc/services/identity/users.rst @@ -1,26 +1,9 @@ Users ===== -Intro ------ -A user is a digital representation of a person, system, or service who -consumes cloud services. Users have credentials and may be assigned -tokens; based on these credentials and tokens, the authentication -service validates that incoming requests are being made by the user who -claims to be making the request, and that the user has the right to -access the requested resources. Users may be directly assigned to a -particular tenant and behave as if they are contained within that -tenant. - -Setup ------ - -User objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -Useful object properties/methods --------------------------------- +Object properties/methods +------------------------- +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ | Property | Description | Getter | Setter | @@ -43,74 +26,78 @@ Useful object properties/methods List users ---------- -.. code:: php +.. code-block:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } - $users = $service->getUsers(); - foreach ($users as $user) { - // ... - } +Retrieve a user by username +--------------------------- -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. +.. code-block:: php -Get user --------- + $user = $service->getUser('jamie'); -There are various ways to get a specific user: by name, ID and email -address. -.. code:: php +Retrieve a user by user ID +-------------------------- - use OpenCloud\Identity\Constants\User as UserConst; +.. code-block:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{userId}', UserConst::MODE_ID); + + +Retrieve a user by email address +-------------------------------- - // Get user by name - $user1 = $service->getUser('jamie'); +.. code-block:: php - // Get user by ID - $user2 = $service->getUser(123456, UserConst::MODE_ID); + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); - // Get user by email - $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); Create user ----------- -There are a few things to remember when creating a user: +There are a few things to bear in mind when creating a user: -- This operation is available only to users who hold the +* This operation is available only to users who hold the ``identity:user-admin`` role. This admin can create a user who holds the ``identity:default`` user role. -- The created user **will** have access to APIs but **will not** have +* The created user **will** have access to APIs but **will not** have access to the Cloud Control Panel. -- Within an account, a maximum of 100 account users can be added. +* A maximum of 100 account users can be added per account. -- If you attempt to add a user who already exists, an HTTP error 409 +* If you attempt to add a user who already exists, an HTTP error 409 results. + The ``username`` and ``email`` properties are required for creating a user. Providing a ``password`` is optional; if omitted, one will be automatically generated and provided in the response. -.. code:: php - use Guzzle\Http\Exception\ClientErrorResponseException; +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; - try { - // execute operation - $user = $service->createUser(array( - 'username' => 'newUser', - 'email' => 'foo@bar.com' - )); - } catch (ClientErrorResponseException $e) { - // catch 4xx HTTP errors - echo $e->getResponse()->toString(); - } + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + + // show generated password + echo $user->getPassword(); - // show generated password - echo $user->getPassword(); Update user ----------- @@ -118,27 +105,30 @@ Update user When updating a user, specify which attribute/property you want to update: -.. code:: php +.. code-block:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); - $user->update(array( - 'email' => 'new_email@bar.com' - )); Updating a user password -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ Updating a user password requires calling a distinct method: -.. code:: php +.. code-block:: php + + $user->updatePassword('password123'); - $user->updatePassword('password123'); Delete user ----------- -.. code:: php +.. code-block:: php + + $user->delete(); - $user->delete(); List credentials ---------------- @@ -146,16 +136,18 @@ List credentials This operation allows you to see your non-password credential types for all authentication methods available. -.. code:: php +.. code-block:: php + + $creds = $user->getOtherCredentials(); - $creds = $user->getOtherCredentials(); Get user API key ---------------- -.. code:: php +.. code-block:: php + + echo $user->getApiKey(); - echo $user->getApiKey(); Reset user API key ------------------ @@ -163,8 +155,7 @@ Reset user API key When resetting an API key, a new one will be automatically generated for you: -.. code:: php - - $user->resetApiKey(); - echo $user->getApiKey(); +.. code-block:: php + $user->resetApiKey(); + echo $user->getApiKey(); From 11f0f608bc9dde6889e412afdfd16a612dc62028 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 05:04:24 -0800 Subject: [PATCH 574/835] Adding missing inline documentation in OpenCloud\ObjectStore classes. --- lib/OpenCloud/ObjectStore/AbstractService.php | 4 +- lib/OpenCloud/ObjectStore/CDNService.php | 14 ++++- .../Resource/AbstractContainer.php | 31 +++++++++- .../ObjectStore/Resource/AbstractResource.php | 62 +++++++++++++------ lib/OpenCloud/ObjectStore/Service.php | 55 +++++++++------- 5 files changed, 121 insertions(+), 45 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/AbstractService.php b/lib/OpenCloud/ObjectStore/AbstractService.php index 8f92874f8..6094d7780 100644 --- a/lib/OpenCloud/ObjectStore/AbstractService.php +++ b/lib/OpenCloud/ObjectStore/AbstractService.php @@ -30,7 +30,9 @@ abstract class AbstractService extends CatalogService const MAX_OBJECT_SIZE = 5102410241025; /** - * @return Resource\Account + * Returns the Object Store account associated with the service. + * + * @return Resource\Account Object Store account */ public function getAccount() { diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index d45fc494d..0092941e0 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -31,8 +31,12 @@ class CDNService extends AbstractService /** * List CDN-enabled containers. * - * @param array $filter - * @return \OpenCloud\Common\Collection\PaginatedIterator + * @param array $filter Array of filter options such as: + * + * * `limit`: number of results to limit the list to. Optional. + * * `marker`: name of container after which to start the list. Optional. + * * `end_marker`: name of container before which to end the list. Optional. + * @return \OpenCloud\Common\Collection\PaginatedIterator Iterator to list of CDN-enabled containers */ public function listContainers(array $filter = array()) { @@ -40,6 +44,12 @@ public function listContainers(array $filter = array()) return $this->resourceList('CDNContainer', $this->getUrl(null, $filter), $this); } + /** + * Return an existing CDN-enabled container. + * + * @param \stdClass $data Data to initialize container. + * @return CDNContainer CDN-enabled Container + */ public function cdnContainer($data) { $container = new CDNContainer($this, $data); diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php b/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php index a36320652..56cd1c2fa 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractContainer.php @@ -50,13 +50,28 @@ public function __construct(ServiceInterface $service, $data = null) $this->populate($data); } + /** + * Return the transaction ID for an HTTP API operation. Useful for debugging. + * + * @return string Transaction ID + */ public function getTransId() { return $this->metadata->getProperty(HeaderConst::TRANS_ID); } + /** + * Returns whether this container is CDN-enabled or not. + * + * @return boolean true if this container is CDN-enabled; false, otherwise. + */ abstract public function isCdnEnabled(); + /** + * Returns whether this container has log retention enabled or not. + * + * @return boolean true if this container has log retention enabled; false, otherwise. + */ public function hasLogRetention() { if ($this instanceof CDNContainer) { @@ -66,11 +81,23 @@ public function hasLogRetention() } } + /** + * For internal use only + * + * @return string Name of the primary key field for this resource + */ public function primaryKeyField() { return 'name'; } + /** + * For internal use only + * + * @param string $path Path to add to URL. Optional. + * @param array $params Query parameters to add to URL. Optional. + * @return Url URL of this container + path + query parameters. + */ public function getUrl($path = null, array $params = array()) { if (strlen($this->getName()) == 0) { @@ -91,7 +118,7 @@ protected function createRefreshRequest() * This method will enable your CDN-enabled container to serve out HTML content like a website. * * @param $indexPage The data object name (i.e. a .html file) that will serve as the main index page. - * @return \Guzzle\Http\Message\Response + * @return \Guzzle\Http\Message\Response The HTTP response for this API operation. */ public function setStaticIndexPage($page) { @@ -110,7 +137,7 @@ public function setStaticIndexPage($page) * Set the default error page for your static site. * * @param $name The data object name (i.e. a .html file) that will serve as the main error page. - * @return \Guzzle\Http\Message\Response + * @return \Guzzle\Http\Message\Response The HTTP response for this operation. */ public function setStaticErrorPage($page) { diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php index e2d494ede..8e16b30b3 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php @@ -45,16 +45,31 @@ public function __construct(ServiceInterface $service) $this->metadata = new $this->metadataClass; } + /** + * For internal use only. + * + * @return Service The ObjectStore service associated with this ObjectStore resource. + */ public function getService() { return $this->service; } + /** + * For internal use only. + * + * @return Service The CDN version of the ObjectStore service associated with this ObjectStore resource. + */ public function getCdnService() { return $this->service->getCDNService(); } + /** + * For internal use only. + * + * @return Client The HTTP client associated with the associated ObjectStore service. + */ public function getClient() { return $this->service->getClient(); @@ -62,10 +77,12 @@ public function getClient() /** * Factory method that allows for easy instantiation from a Response object. + * + * For internal use only. * - * @param Response $response - * @param ServiceInterface $service - * @return static + * @param Response $response HTTP response from an API operation. + * @param ServiceInterface $service The ObjectStore service to associate with this ObjectStore resource object. + * @return AbstractResource A concrete sub-class of `AbstractResource`. */ public static function fromResponse(Response $response, ServiceInterface $service) { @@ -81,8 +98,10 @@ public static function fromResponse(Response $response, ServiceInterface $servic /** * Trim headers of their resource-specific prefixes. * - * @param $headers - * @return array + * For internal use only. + * + * @param array $headers Headers as returned from an HTTP response + * @return array Trimmed headers */ public static function trimHeaders($headers) { @@ -121,8 +140,8 @@ protected static function stripPrefix($header) /** * Prepend/stock the header names with a resource-specific prefix. * - * @param array $headers - * @return array + * @param array $headers Headers to use on ObjectStore resource. + * @return array Headers returned with appropriate prefix as expected by ObjectStore service. */ public static function stockHeaders(array $headers) { @@ -147,11 +166,12 @@ public static function stockHeaders(array $headers) } /** - * Set the metadata (local-only) for this object. + * Set the metadata (local-only) for this object. You must call saveMetadata + * to actually persist the metadata using the ObjectStore service. * - * @param $data - * @param bool $constructFromResponse - * @return $this + * @param array $data Object/container metadata key/value pair array. + * @param bool $constructFromResponse Whether the metadata key/value pairs were obtiained from an HTTP response of an ObjectStore API operation. + * @return AbstractResource This object, with metadata set. */ public function setMetadata($data, $constructFromResponse = false) { @@ -167,7 +187,9 @@ public function setMetadata($data, $constructFromResponse = false) } /** - * @return \OpenCloud\Common\Metadata + * Returns metadata for this object. + * + * @return \OpenCloud\Common\Metadata Metadata set on this object. */ public function getMetadata() { @@ -180,7 +202,7 @@ public function getMetadata() * @param array $metadata The array of values you want to set as metadata * @param bool $stockPrefix Whether to prepend each array key with the metadata-specific prefix. For objects, this * would be X-Object-Meta-Foo => Bar - * @return mixed + * @return Response HTTP response from API operation. */ public function saveMetadata(array $metadata, $stockPrefix = true) { @@ -192,7 +214,7 @@ public function saveMetadata(array $metadata, $stockPrefix = true) /** * Retrieve metadata from the API. This method will then set and return this value. * - * @return \OpenCloud\Common\Metadata + * @return \OpenCloud\Common\Metadata Metadata returned from the ObjectStore service for this object/container. */ public function retrieveMetadata() { @@ -208,8 +230,8 @@ public function retrieveMetadata() /** * To delete or unset a particular metadata item. * - * @param $key - * @return mixed + * @param $key Metadata key to unset + * @return Response HTTP response returned from API operation to unset metadata item. */ public function unsetMetadataItem($key) { @@ -224,10 +246,12 @@ public function unsetMetadataItem($key) } /** - * Append a particular array of values to the existing metadata. Analogous to a merge. + * Append a particular array of values to the existing metadata. Analogous + * to a merge. You must call to actually persist the metadata using the + * ObjectStore service. * - * @param array $values - * @return array + * @param array $values The array of values you want to append to metadata. + * @return array Metadata, after `$values` are appended. */ public function appendToMetadata(array $values) { diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index f0e1c3dc9..babd764dd 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -59,7 +59,9 @@ public function __construct(Client $client, $type = null, $name = null, $region } /** - * @return CDNService + * Return the CDN version of the ObjectStore service. + * + * @return CDNService CDN version of the ObjectStore service */ public function getCdnService() { @@ -69,8 +71,12 @@ public function getCdnService() /** * List all available containers. * - * @param array $filter - * @return \OpenCloud\Common\Collection\PaginatedIterator + * @param array $filter Array of filter options such as: + * + * * `limit`: number of results to limit the list to. Optional. + * * `marker`: name of container after which to start the list. Optional. + * * `end_marker`: name of container before which to end the list. Optional. + * @return \OpenCloud\Common\Collection\PaginatedIterator Iterator to list of containers */ public function listContainers(array $filter = array()) { @@ -79,8 +85,10 @@ public function listContainers(array $filter = array()) } /** - * @param $data - * @return Container + * Return a new or existing (if name is specified) container. + * + * @param \stdClass $data Data to initialize container. Optional. + * @return Container Container */ public function getContainer($data = null) { @@ -90,9 +98,9 @@ public function getContainer($data = null) /** * Create a container for this service. * - * @param $name The name of the container + * @param string $name The name of the container * @param array $metadata Additional (optional) metadata to associate with the container - * @return bool|static + * @return bool|Container Newly-created Container upon success; false, otherwise */ public function createContainer($name, array $metadata = array()) { @@ -114,9 +122,9 @@ public function createContainer($name, array $metadata = array()) /** * Check the validity of a potential container name. * - * @param $name - * @return bool - * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError + * @param string $name Name of container + * @return bool True if container name is valid + * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError if container name is invalid */ public function checkContainerName($name) { @@ -145,12 +153,12 @@ public function checkContainerName($name) * will be ignored. You can create up to 1,000 new containers per extraction request. Also note that only regular * files will be uploaded. Empty directories, symlinks, and so on, will not be uploaded. * - * @param $path The path to the archive being extracted - * @param $archive The contents of the archive (either string or stream) + * @param string $path The path to the archive being extracted + * @param string|stream $archive The contents of the archive (either string or stream) * @param string $archiveType The type of archive you're using {@see \OpenCloud\ObjectStore\Constants\UrlType} - * @return \Guzzle\Http\Message\Response - * @throws Exception\BulkOperationException - * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError + * @return \Guzzle\Http\Message\Response HTTP response from API + * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError if specifed `$archiveType` is invalid + * @throws Exception\BulkOperationException if there are errors with the bulk extract */ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ) { @@ -199,17 +207,17 @@ public function bulkDelete(array $paths) * and sent as individual requests. * * @param array $paths The objects you want to delete. Each path needs - * be formatted as /{containerName}/{objectName}. If - * you are deleting object_1 and object_2 from the - * photos_container, the array will be: + * be formatted as `/{containerName}/{objectName}`. If + * you are deleting `object_1` and `object_2` from the + * `photos_container`, the array will be: * * array( * '/photos_container/object_1', * '/photos_container/object_2' * ) * - * @return array The array of responses from the API - * @throws Exception\BulkOperationException + * @return array[Guzzle\Http\Message\Response] HTTP responses from the API + * @throws Exception\BulkOperationException if the bulk delete operation fails */ public function batchDelete(array $paths) { @@ -257,7 +265,12 @@ private function executeBatchDeleteRequest(array $paths) * * @param Container $old Where you're moving files from * @param Container $new Where you're moving files to - * @return array Of PUT responses + * @param array $options Options to configure the migration. Optional. Available options are: + * + * * `read.batchLimit`: Number of files to read at a time from `$old` container. Optional; default = 1000. + * * `write.batchLimit`: Number of files to write at a time to `$new` container. Optional; default = 1000. + * * `read.pageLimit`: Number of filenames to read at a time from `$old` container. Optional; default = 10000. + * @return array[Guzzle\Http\Message\Response] HTTP responses from the API */ public function migrateContainer(Container $old, Container $new, array $options = array()) { From 9ee57c7d95993e939c9a9ae67f88b77a2e82ca7a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 22:43:10 -0800 Subject: [PATCH 575/835] Adding unit tests + implementation for Neutron Security Groups and Security Group Rules. --- lib/OpenCloud/Networking/Service.php | 100 ++++++++++++++++++ .../Tests/Networking/NetworkingTestCase.php | 10 ++ .../Tests/Networking/ServiceTest.php | 46 ++++++++ 3 files changed, 156 insertions(+) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 3c1f879e2..12bb7477f 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -22,6 +22,8 @@ use OpenCloud\Networking\Resource\Network; use OpenCloud\Networking\Resource\Subnet; use OpenCloud\Networking\Resource\Port; +use OpenCloud\Networking\Resource\SecurityGroup; +use OpenCloud\Networking\Resource\SecurityGroupRule; /** * The Networking class represents the OpenNetwork Neutron service. @@ -290,6 +292,104 @@ public function listPorts(array $params = array()) return $this->resourceList('Port', $url); } + /** + * Returns a SecurityGroup object associated with this Networking service + * + * @param string $id ID of security group to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroup object + */ + public function securityGroup($id = null) + { + return $this->resource('SecurityGroup', $id); + } + + /** + * Creates a new SecurityGroup and returns it. + * + * @param array $params SecurityGroup creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group + * @return \OpenCloud\Networking\Resource\SecurityGroup Object representing created security group + */ + public function createSecurityGroup(array $params = array()) + { + $securityGroup = $this->securityGroup(); + $securityGroup->create($params); + return $securityGroup; + } + + /** + * Returns a SecurityGroup object associated with this Networking service + * + * @param string $id ID of security group to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroup object + */ + public function getSecurityGroup($id) + { + return $this->securityGroup($id); + } + + /** + * Returns a list of security groups you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSecurityGroups(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(SecurityGroup::resourceName())->setQuery($params); + + return $this->resourceList('SecurityGroup', $url); + } + + /** + * Returns a SecurityGroupRule object associated with this Networking service + * + * @param string $id ID of security group rule to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroupRule object + */ + public function securityGroupRule($id = null) + { + return $this->resource('SecurityGroupRule', $id); + } + + /** + * Creates a new SecurityGroupRule and returns it. + * + * @param array $params SecurityGroupRule creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule + * @return \OpenCloud\Networking\Resource\SecurityGroupRule Object representing created security group rule + */ + public function createSecurityGroupRule(array $params = array()) + { + $securityGroupRule = $this->securityGroupRule(); + $securityGroupRule->create($params); + return $securityGroupRule; + } + + /** + * Returns a SecurityGroupRule object associated with this Networking service + * + * @param string $id ID of security group rule to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroupRule object + */ + public function getSecurityGroupRule($id) + { + return $this->securityGroupRule($id); + } + + /** + * Returns a list of security group rules you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSecurityGroupRules(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(SecurityGroupRule::resourceName())->setQuery($params); + + return $this->resourceList('SecurityGroupRule', $url); + } + /** * Return namespaces. * diff --git a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php index be6b304c4..399094b6e 100644 --- a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php +++ b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php @@ -48,4 +48,14 @@ protected function assertIsPort($object) { $this->assertInstanceOf('OpenCloud\Networking\Resource\Port', $object); } + + protected function assertIsSecurityGroup($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\SecurityGroup', $object); + } + + protected function assertIsSecurityGroupRule($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\SecurityGroupRule', $object); + } } diff --git a/tests/OpenCloud/Tests/Networking/ServiceTest.php b/tests/OpenCloud/Tests/Networking/ServiceTest.php index 92b793e19..f2ab2cbfe 100644 --- a/tests/OpenCloud/Tests/Networking/ServiceTest.php +++ b/tests/OpenCloud/Tests/Networking/ServiceTest.php @@ -146,4 +146,50 @@ public function testGetPort() $this->assertIsPort($port); $this->assertEquals('port1', $port->getName()); } + + public function testCreateSecurityGroup() + { + $this->assertIsSecurityGroup($this->service->createSecurityGroup()); + } + + public function testListSecurityGroups() + { + $this->addMockSubscriber($this->makeResponse('{"security_groups":[{"description":"default","id":"85cc3048-abc3-43cc-89b3-377341426ac5","name":"default","security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}],"tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}]}')); + + $securityGroups = $this->service->listSecurityGroups(); + $this->isCollection($securityGroups); + $this->assertIsSecurityGroup($securityGroups->getElement(0)); + } + + public function testGetSecurityGroup() + { + $this->addMockSubscriber($this->makeResponse('{"security_group":{"description":"default","id":"85cc3048-abc3-43cc-89b3-377341426ac5","name":"default","security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}],"tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}}')); + + $securityGroup = $this->service->getSecurityGroup('85cc3048-abc3-43cc-89b3-377341426ac5'); + $this->assertIsSecurityGroup($securityGroup); + $this->assertEquals('default', $securityGroup->getName()); + } + + public function testCreateSecurityGroupRule() + { + $this->assertIsSecurityGroupRule($this->service->createSecurityGroupRule()); + } + + public function testListSecurityGroupRules() + { + $this->addMockSubscriber($this->makeResponse('{"security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}]}')); + + $securityGroupRules = $this->service->listSecurityGroupRules(); + $this->isCollection($securityGroupRules); + $this->assertIsSecurityGroupRule($securityGroupRules->getElement(0)); + } + + public function testGetSecurityGroupRule() + { + $this->addMockSubscriber($this->makeResponse('{"security_group_rule":{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}}')); + + $securityGroupRule = $this->service->getSecurityGroupRule('3c0e45ff-adaf-4124-b083-bf390e5482ff'); + $this->assertIsSecurityGroupRule($securityGroupRule); + $this->assertEquals('egress', $securityGroupRule->getDirection()); + } } From afd17c237bcb478c69b1a87125eeeba6843a213a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:09:09 -0800 Subject: [PATCH 576/835] Adding userguide documentation for security groups and security group rules. --- docs/userguide/Networking/USERGUIDE.md | 147 +++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 74e713b8a..bfa1a217c 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -33,6 +33,16 @@ these entities. * [Get a port](#get-a-port) * [Update a port](#update-a-port) * [Delete a port](#delete-a-port) + * [Security Groups](#security-groups) + * [Create a security group](#create-a-security-group) + * [List security groups](#list-security-groups) + * [Get a security group](#get-a-security-group) + * [Delete a security group](#delete-a-security-group) + * [Security group rule Rules](#security-group-rules) + * [Create a security group rule](#create-a-security-group-rule) + * [List security group rules](#list-security-group-rules) + * [Get a security group rule](#get-a-security-group-rule) + * [Delete a security group rule](#delete-a-security-group-rule) ## Concepts @@ -55,6 +65,10 @@ be assigned to the interfaces plugged into them. When IP addresses are associated with a port, this also implies the port is associated with a subnet because the IP address is taken from the allocation pool for a specific subnet. +* **Security Group**: TODO + +* **Security Group Rule**: TODO + ## Prerequisites ### Client @@ -468,3 +482,136 @@ $port->delete(); ``` [ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] + +## Security Groups + +TODO: Add description + +### Create a security group + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | `new-webservers` | +| `description` | Description of the security group. | String | No | `null` | `security group for webservers` | + +You can create a security group as shown in the following example: + +```php +$securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' +)); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-security-group.php) ] + +### List security groups + +You can list all the security groups to which you have access as shown in the following +example: + +```php +$securityGroups = $networkingService->listSecurityGroups(); +foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-security-groups.php) ] + +### Get a security group + +You can retrieve a specific security group by using that security group's ID, as shown in the +following example: + +```php +$securityGroup = $networkingService->getSecurityGroup('2076db17-a522-4506-91de-c6dd8e837028'); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-security-group.php) ] + +### Delete a security group + +You can delete a security group as shown in the following example: + +```php +$securityGroup->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group.php) ] + +## Security Group Rules + +TODO: Add description + +### Create a security group rule + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `securityGroupId` | The security group ID to associate with this security group rule. | String | Yes | - | `2076db17-a522-4506-91de-c6dd8e837028` | +| `direction` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (`ingress` or `egress`) | Yes | - | `ingress` | +| `ethertype` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (`IPv4` or `IPv6`) | No | `IPv4` | `IPv6` | +| `portRangeMin` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the `portRangeMax` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | +| `portRangeMax` | The maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | +| `protocol` | The protocol that is matched by the security group rule. | String (`tcp`, `udp`, `icmp`) | No | `null` | `tcp` | +| `remoteGroupId` | The remote group ID to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `85cc3048-abc3-43cc-89b3-377341426ac5` | +| `remoteIpPrefix` | The remote IP prefix to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `192.168.5.0` | + +You can create a security group rule as shown in the following example: + +```php +$securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' +)); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-security-group-rule.php) ] + +### List security group rules + +You can list all the security group rules to which you have access as shown in the following +example: + +```php +$securityGroupRules = $networkingService->listSecurityGroupRules(); +foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-security-group-rules.php) ] + +### Get a security group rule + +You can retrieve a specific security group rule by using that security group rule's ID, as shown in the +following example: + +```php +$securityGroupRule = $networkingService->getSecurityGroupRule(''); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-security-group-rule.php) ] + +### Delete a security group rule + +You can delete a security group rule as shown in the following example: + +```php +$securityGroupRule->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group-rule.php) ] From 4bfdc0185d974b651b82760bc3b77272bfc21a61 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:24:17 -0800 Subject: [PATCH 577/835] Adding downloadable code samples for Security Groups and Security Group Rules. --- .../Networking/create-security-group-rule.php | 42 +++++++++++++++++++ samples/Networking/create-security-group.php | 37 ++++++++++++++++ .../Networking/delete-security-group-rule.php | 36 ++++++++++++++++ samples/Networking/delete-security-group.php | 36 ++++++++++++++++ .../Networking/get-security-group-rule.php | 34 +++++++++++++++ samples/Networking/get-security-group.php | 34 +++++++++++++++ .../Networking/list-security-group-rules.php | 36 ++++++++++++++++ samples/Networking/list-security-groups.php | 36 ++++++++++++++++ 8 files changed, 291 insertions(+) create mode 100644 samples/Networking/create-security-group-rule.php create mode 100644 samples/Networking/create-security-group.php create mode 100644 samples/Networking/delete-security-group-rule.php create mode 100644 samples/Networking/delete-security-group.php create mode 100644 samples/Networking/get-security-group-rule.php create mode 100644 samples/Networking/get-security-group.php create mode 100644 samples/Networking/list-security-group-rules.php create mode 100644 samples/Networking/list-security-groups.php diff --git a/samples/Networking/create-security-group-rule.php b/samples/Networking/create-security-group-rule.php new file mode 100644 index 000000000..e60059774 --- /dev/null +++ b/samples/Networking/create-security-group-rule.php @@ -0,0 +1,42 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Create a security group rule. +$securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' +)); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ diff --git a/samples/Networking/create-security-group.php b/samples/Networking/create-security-group.php new file mode 100644 index 000000000..bf69ad044 --- /dev/null +++ b/samples/Networking/create-security-group.php @@ -0,0 +1,37 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Create a security group. +$securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' +)); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ diff --git a/samples/Networking/delete-security-group-rule.php b/samples/Networking/delete-security-group-rule.php new file mode 100644 index 000000000..d7cf988dd --- /dev/null +++ b/samples/Networking/delete-security-group-rule.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group rule. +$securityGroupRule = $networkingService->getSecurityGroupRule('{securityGroupRuleId}'); + +// 4. Delete security group rule. +$securityGroupRule->delete(); diff --git a/samples/Networking/delete-security-group.php b/samples/Networking/delete-security-group.php new file mode 100644 index 000000000..758da3ae8 --- /dev/null +++ b/samples/Networking/delete-security-group.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group. +$securityGroup = $networkingService->getSecurityGroup('{securityGroupId}'); + +// 4. Delete security group. +$securityGroup->delete(); diff --git a/samples/Networking/get-security-group-rule.php b/samples/Networking/get-security-group-rule.php new file mode 100644 index 000000000..f6ea2bc25 --- /dev/null +++ b/samples/Networking/get-security-group-rule.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group rule. +$securityGroupRule = $networkingService->getSecurityGroupRule('{securityGroupRuleId}'); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ diff --git a/samples/Networking/get-security-group.php b/samples/Networking/get-security-group.php new file mode 100644 index 000000000..7573aa1f9 --- /dev/null +++ b/samples/Networking/get-security-group.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group. +$securityGroup = $networkingService->getSecurityGroup('{securityGroupId}'); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ diff --git a/samples/Networking/list-security-group-rules.php b/samples/Networking/list-security-group-rules.php new file mode 100644 index 000000000..1adba50d7 --- /dev/null +++ b/samples/Networking/list-security-group-rules.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List security group rules +$securityGroupRules = $networkingService->listSecurityGroupRules(); +foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +} diff --git a/samples/Networking/list-security-groups.php b/samples/Networking/list-security-groups.php new file mode 100644 index 000000000..82ffd0931 --- /dev/null +++ b/samples/Networking/list-security-groups.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List security groups +$securityGroups = $networkingService->listSecurityGroups(); +foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +} From efbed881ac696196e05889c2c40bc68a06f8b749 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:40:40 -0800 Subject: [PATCH 578/835] Adding smoke tests for Security Groups and Security Group Rules. --- tests/OpenCloud/Smoke/Unit/Networking.php | 80 ++++++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 74f39fac7..b6ab4592e 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -26,9 +26,11 @@ */ class Networking extends AbstractUnit implements UnitInterface { - protected $cleanupNetworkIds = array(); - protected $cleanupSubnetIds = array(); - protected $cleanupPortIds = array(); + protected $cleanupNetworkIds = array(); + protected $cleanupSubnetIds = array(); + protected $cleanupPortIds = array(); + protected $cleanupSecurityGroupIds = array(); + protected $cleanupSecurityGroupRuleIds = array(); public function setupService() { @@ -40,6 +42,8 @@ public function main() $this->testNetworkOperations(); $this->testSubnetOperations(); $this->testPortOperations(); + $this->testSecurityGroupOperations(); + $this->testSecurityGroupRuleOperations(); } protected function testNetworkOperations() @@ -251,8 +255,78 @@ protected function testPortOperations() )); } + protected function testSecurityGroupOperations() + { + $this->step('Create security group'); + $security group = $this->getService()->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); + $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + $this->cleanupSecurityGroupIds[] = $securityGroup->getId(); + + $this->step('List security groups'); + $securityGroups = $this->getService()->listSecurityGroups(); + $this->stepInfo('%-40s | %s', 'Security Group ID', 'Security Group name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($securityGroups as $securityGroup) { + $this->stepInfo('%-40s | %s', $securityGroup->getId(), $securityGroup->getName()); + } + + $this->step('Get security group'); + $security group = $this->getService()->getSecurityGroup($securityGroup->getId()); + $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); + $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + } + + protected function testSecurityGroupRuleOperations() + { + $securityGroup1 = $this->getService()->createSecurityGroup(array( + 'name' => 'test_security_group_for_test_security_group_rule' + )); + $this->cleanupSecurityGroupIds[] = $securityGroup1->getId(); + + $this->step('Create security group rule'); + $security group rule = $this->getService()->createSecurityGroupRule(array( + 'securityGroupId' => $securityGroup1->getId(), + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); + $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); + $this->cleanupSecurityGroupRuleIds[] = $securityGroupRule->getId(); + + $this->step('List security group rules'); + $securityGroupRules = $this->getService()->listSecurityGroupRules(); + $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($securityGroupRules as $securityGroupRule) { + $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getName()); + } + + $this->step('Get security group rule'); + $security group rule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); + $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); + $this->stepInfo('Security Group Rule Name: ' . $securityGroupRule->getName()); + } + public function teardown() { + foreach ($this->cleanupSecurityGroupRuleIds as $securityGroupRuleId) { + $securityGroupRule = $this->getService()->getSecurityGroupRule($securityGroupRuleId); + $securityGroupRule->delete(); + } + + foreach ($this->cleanupSecurityGroupIds as $securityGroupId) { + $securityGroup = $this->getService()->getSecurityGroup($securityGroupId); + $securityGroup->delete(); + } + foreach ($this->cleanupPortIds as $portId) { $port = $this->getService()->getPort($portId); $port->delete(); From 9ad0ee0cb06f495425ac3dd8468636757b537d65 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:41:48 -0800 Subject: [PATCH 579/835] Forgot to check-in resource classes. --- .../Networking/Resource/SecurityGroup.php | 67 +++++++++++++++ .../Networking/Resource/SecurityGroupRule.php | 82 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 lib/OpenCloud/Networking/Resource/SecurityGroup.php create mode 100644 lib/OpenCloud/Networking/Resource/SecurityGroupRule.php diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php new file mode 100644 index 000000000..105fc22a1 --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -0,0 +1,67 @@ + 'securityGroupRules', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'name', + 'description' + ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as {@see OpenCloud\Networking\Service}. + */ + public function createJson() + { + return parent::createJson(); + } + + /** + * {@inheritDoc} + */ + public function update($params = array()) + { + return $this->noUpdate(); + } +} diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php new file mode 100644 index 000000000..3659d08ac --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -0,0 +1,82 @@ + 'portRangeMin', + 'port_range_max' => 'portRangeMax', + 'remote_group_id' => 'remoteGroupId', + 'remote_ip_prefix' => 'remoteIpPrefix', + 'security_group_id' => 'securityGroupId', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'direction', + 'ethertype', + 'securityGroupId', + 'portRangeMin', + 'portRangeMax', + 'protocol', + 'remoteGroupId', + 'remoteIpPrefix' + ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as {@see OpenCloud\Networking\Service}. + */ + public function createJson() + { + return parent::createJson(); + } + + /** + * {@inheritDoc} + */ + public function update($params = array()) + { + return $this->noUpdate(); + } +} From 61efb9cc58fd3cb505bad30ebfc19e94c8a994c4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:11 +0100 Subject: [PATCH 580/835] Add docs for Images --- doc/services/image/Images.md.rst | 107 ------------------ doc/services/image/Tags.md.rst | 28 ----- doc/services/image/images.rst | 84 ++++++++++++++ doc/services/image/index.rst | 41 +++++++ .../image/{Schemas.md.rst => schemas.rst} | 29 ++--- .../image/{Sharing.md.rst => sharing.rst} | 50 ++++---- doc/services/image/tags.rst | 29 +++++ 7 files changed, 189 insertions(+), 179 deletions(-) delete mode 100644 doc/services/image/Images.md.rst delete mode 100644 doc/services/image/Tags.md.rst create mode 100644 doc/services/image/images.rst rename doc/services/image/{Schemas.md.rst => schemas.rst} (87%) rename doc/services/image/{Sharing.md.rst => sharing.rst} (75%) create mode 100644 doc/services/image/tags.rst diff --git a/doc/services/image/Images.md.rst b/doc/services/image/Images.md.rst deleted file mode 100644 index ef187f083..000000000 --- a/doc/services/image/Images.md.rst +++ /dev/null @@ -1,107 +0,0 @@ -Images -====== - -A virtual machine image is a single file which contains a virtual disk -that has an installed bootable operating system. In the Cloud Images -API, an image is represented by a JSON-encoded data structure (the image -schema) and its raw binary data (the image file). - -An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. - -Setup ------ - -You instantiate an Image object from its ``OpenCloud\Image\Service`` -class, which is available from the OpenStack/Rackspace client: - -.. code:: php - - $service = $client->imageService('cloudImages', 'IAD'); - -View the guides for more information about `clients <../Clients.md>`__ -or `services <../Services.md>`__. - -List images ------------ - -.. code:: php - - $images = $service->listImages(); - - foreach ($images as $image) { - /** @param $image OpenCloud\Image\Resource\Image */ - } - -For more information about working with iterators, please see the -`iterators documentation <../Iterators.md>`__. - -Get image details ------------------ - -.. code:: php - - /** @param $image OpenCloud\Image\Resource\Image */ - $image = $service->getImage(''); - -A note on schema classes -~~~~~~~~~~~~~~~~~~~~~~~~ - -Both ``OpenCloud\Image\Resource\Image`` and -``OpenCloud\Image\Resource\Member`` extend the -``AbstractSchemaResource`` abstract class, which offers some unique -functionality. - -Because these resources are inherently dynamic - i.e. they are modelled -on dynamic JSON schema - you need to access their state in a way -different than conventional getter/setter methods, and even class -properties. For this reason, they implement SPL's native -```ArrayAccess`` `__ -interface which allows you to access their state as a conventional -array: - -.. code:: php - - $image = $service->getImage(''); - - $id = $image['id']; - $tags = $image['tags']; - -Update image ------------- - -You can only update your own custom images - you cannot update or delete -base images. The way in which you may update your image is dictated by -its `schema `__. - -Although you should be able to add new and replace existing properties, -always prepare yourself for a situation where it might be forbidden: - -.. code:: php - - use OpenCloud\Common\Exceptions\ForbiddenOperationException; - - try { - $image->update(array( - 'name' => 'foo', - 'newProperty' => 'bar' - )); - } catch (ForbiddenOperationException $e) { - // A 403 Forbidden was returned - } - -There are three operations that can take place for each Image property: - -- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation - will occur, removing the property from the JSON document -- If a non-false value is provided and the property does not exist, an - ``ADD`` operation will add it to the document -- If a non-false value is provided and the property does exist, a - ``REPLACE`` operation will modify the property in the document - -Delete image ------------- - -.. code:: php - - $image->delete(); - diff --git a/doc/services/image/Tags.md.rst b/doc/services/image/Tags.md.rst deleted file mode 100644 index af5baf157..000000000 --- a/doc/services/image/Tags.md.rst +++ /dev/null @@ -1,28 +0,0 @@ -Image tags -========== - -An image tag is a string of characters used to identify a specific image -or images. - -Setup ------ - -All member operations are executed against an `Image `__, so -you will need to set this up first. - -Add image tag -------------- - -.. code:: php - - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->addTag('jamie_dev'); - -Delete image tag ----------------- - -.. code:: php - - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->deleteTag('jamie_dev'); - diff --git a/doc/services/image/images.rst b/doc/services/image/images.rst new file mode 100644 index 000000000..ec1a17c34 --- /dev/null +++ b/doc/services/image/images.rst @@ -0,0 +1,84 @@ +Images +====== + +List images +----------- + +.. code-block:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + + +Get image details +----------------- + +.. code-block:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage('{imageId}'); + + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and ``OpenCloud\Image\Resource\Member`` +extend the ``AbstractSchemaResource`` class, which offers some unique functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a different way +than conventional getter/setter methods, and even class properties. For this +reason, they implement SPL's native +`ArrayAccess `_ +interface which allows you to access their state as a conventional +array: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + $id = $image['id']; + $tags = $image['tags']; + + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +* If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +* If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +* If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code-block:: php + + $image->delete(); diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index e69de29bb..d54b09ad0 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -0,0 +1,41 @@ +Images v1 +========= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +.. code-block:: php + + $service = $client->imageService(null, '{region}'); + + +Operations +---------- + +.. toctree:: + + images + schemas + sharing + tags + + +Glossary +-------- + + image + A virtual machine image is a single file which contains a virtual disk + that has an installed bootable operating system. In the Cloud Images + API, an image is represented by a JSON-encoded data structure (the image + schema) and its raw binary data (the image file). + + schema + The Cloud Images API supplies JSON documents describing the JSON-encoded + data structures that represent domain objects, so that a client knows + exactly what to expect in an API response. + + tag + An image tag is a string of characters used to identify a specific image + or images. diff --git a/doc/services/image/Schemas.md.rst b/doc/services/image/schemas.rst similarity index 87% rename from doc/services/image/Schemas.md.rst rename to doc/services/image/schemas.rst index 8f792c4c0..c1b85b8de 100644 --- a/doc/services/image/Schemas.md.rst +++ b/doc/services/image/schemas.rst @@ -1,13 +1,6 @@ JSON schemas ============ -The Cloud Images API supplies json documents describing the JSON-encoded -data structures that represent domain objects, so that a client knows -exactly what to expect in an API response. - -A JSON Schema is represented by the -``OpenCloud\Image\Resource\Schema\Schema`` class. - Schema types ------------ @@ -19,7 +12,7 @@ Example response from the API A sample response from the API, for an Images schema might be: -.. code:: json +.. code-block:: json { "name": "images", @@ -70,7 +63,7 @@ links and a properties object. Inside this properties object we see the structure of this top-level ``images`` object. So we know that it will take this form: -.. code:: json +.. code-block:: json { "images": [something...] @@ -80,7 +73,7 @@ Within this object, we can see that it contains an array of anonymous objects, each of which is called ``image`` and has its own set of nested properties: -.. code:: json +.. code-block:: json { "images": [ @@ -101,7 +94,7 @@ i.e. a *subschema*. We know that each object has an ID property (string), a name property (string), a visibility property (can either be ``private`` or ``public``), etc. -.. code:: json +.. code-block:: json { "images": [ @@ -147,21 +140,21 @@ Requests need to use the In order for the operation to occur, the request entity body needs to contain a very particular structure: -:: +.. code-block:: json [ {"op": "replace", "path": "/name", "value": "Fedora 17"}, {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} ] -The ``op`` key refers to the type of Operation (see -``OpenCloud\Image\Enum\OperationType`` for a full list). +* The ``op`` key refers to the type of Operation (see + ``OpenCloud\Image\Enum\OperationType`` for a full list). -The ``path`` key is a JSON pointer to the document property you want to -modify or insert. JSON pointers are defined in `RFC -6901 `__. +* The ``path`` key is a JSON pointer to the document property you want to + modify or insert. JSON pointers are defined in `RFC + 6901 `__. -The ``value`` key is the value. +* The ``value`` key is the value. Because this is all handled for you behind the scenes, we will not go into exhaustive depth about how this operation is handled. You can diff --git a/doc/services/image/Sharing.md.rst b/doc/services/image/sharing.rst similarity index 75% rename from doc/services/image/Sharing.md.rst rename to doc/services/image/sharing.rst index b1a1ea382..5edf63ab4 100644 --- a/doc/services/image/Sharing.md.rst +++ b/doc/services/image/sharing.rst @@ -63,66 +63,64 @@ Additional notes Setup ----- -All member operations are executed against an `Image `__, so -you will need to set this up first. +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + List image members ------------------ This operation is available for both producers and consumers. -.. code:: php +.. code-block:: php - $members = $image->listMembers(); + $members = $image->listMembers(); - foreach ($members as $member) { - /** @param $member OpenCloud\Image\Resource\Member */ - } + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } -For more information about working with iterators, please see the -`iterators documentation <../Iterators.md>`__. Create image member ------------------- This operation is only available for producers. -.. code:: php +.. code-block:: php - $tenantId = 12345; + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember('{tenantId}'); - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->createMember($tenantId); Delete image member ------------------- This operation is only available for producers. -.. code:: php +.. code-block:: php - $tenantId = 12345; + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); + $member->delete(); - /** @param $member OpenCloud\Image\Resource\Member */ - $member = $image->getMember($tenantId); - - $member->delete(); Update image member status -------------------------- This operation is only available for consumers. -.. code:: php - - use OpenCloud\Images\Enum\MemberStatus; +.. code-block:: php - $tenantId = 12345; + use OpenCloud\Images\Enum\MemberStatus; - /** @param $member OpenCloud\Image\Resource\Member */ - $member = $image->getMember($tenantId); + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); - $member->updateStatus(MemberStatus::ACCEPTED); + $member->updateStatus(MemberStatus::ACCEPTED); The acceptable states you may pass in are made available to you through the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` diff --git a/doc/services/image/tags.rst b/doc/services/image/tags.rst new file mode 100644 index 000000000..0a376e6de --- /dev/null +++ b/doc/services/image/tags.rst @@ -0,0 +1,29 @@ +Image tags +========== + +Setup +----- + +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + +Add image tag +------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); From f96f3c6afa361d0fad3ff31f5d7567c291f9425c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:21 +0100 Subject: [PATCH 581/835] Add LB docs --- doc/services/load-balancer/README.md.rst | 92 -- doc/services/load-balancer/USERGUIDE.md.rst | 840 ------------------ doc/services/load-balancer/access.rst | 79 ++ doc/services/load-balancer/caching.rst | 35 + doc/services/load-balancer/errors.rst | 44 + doc/services/load-balancer/index.rst | 93 ++ .../load-balancer/lb-setup.sample.rst | 9 + doc/services/load-balancer/load-balancer.rst | 165 ++++ doc/services/load-balancer/logging.rst | 33 + doc/services/load-balancer/metadata.rst | 46 + doc/services/load-balancer/monitors.rst | 43 + doc/services/load-balancer/nodes.rst | 124 +++ doc/services/load-balancer/sessions.rst | 53 ++ doc/services/load-balancer/ssl.rst | 52 ++ doc/services/load-balancer/stats.rst | 59 ++ doc/services/load-balancer/virtual-ips.rst | 74 ++ 16 files changed, 909 insertions(+), 932 deletions(-) delete mode 100644 doc/services/load-balancer/README.md.rst delete mode 100644 doc/services/load-balancer/USERGUIDE.md.rst create mode 100644 doc/services/load-balancer/access.rst create mode 100644 doc/services/load-balancer/caching.rst create mode 100644 doc/services/load-balancer/errors.rst create mode 100644 doc/services/load-balancer/lb-setup.sample.rst create mode 100644 doc/services/load-balancer/load-balancer.rst create mode 100644 doc/services/load-balancer/logging.rst create mode 100644 doc/services/load-balancer/metadata.rst create mode 100644 doc/services/load-balancer/monitors.rst create mode 100644 doc/services/load-balancer/nodes.rst create mode 100644 doc/services/load-balancer/sessions.rst create mode 100644 doc/services/load-balancer/ssl.rst create mode 100644 doc/services/load-balancer/stats.rst create mode 100644 doc/services/load-balancer/virtual-ips.rst diff --git a/doc/services/load-balancer/README.md.rst b/doc/services/load-balancer/README.md.rst deleted file mode 100644 index 862cf20d2..000000000 --- a/doc/services/load-balancer/README.md.rst +++ /dev/null @@ -1,92 +0,0 @@ -Load Balancers -============== - -A **load balancer** is a device that distributes incoming network -traffic amongst multiple back-end systems. These back-end systems are -called the **nodes** of the load balancer. - -Getting started ---------------- - -1. Instantiate a Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Retrieve the server instances you want to add as nodes of the load balancer. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); - - $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); - $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); - -The example above uses two server instances that have already been -created. It retrieves the server instances using their IDs. See also: -`creating server instances <>`__. - -3. Obtain a Load Balancer service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This object will be used to first define the load balancer nodes and -later create the load balancer itself. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -4. Define a load balancer node for each server. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $serverOneNode = $loadBalancer->node(); - $serverOneNode->address = $serverOne->addresses->private[0]->addr; - $serverOneNode->port = 8080; - $serverOneNode->condition = 'ENABLED'; - - $serverTwoNode = $loadBalancer->node(); - $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; - $serverTwoNode->port = 8080; - $serverTwoNode->condition = 'ENABLED'; - -In the example above, each node runs a service that listens on port -8080. Further, each node will start out as ``ENABLED``, which means it -will be ready to receive network traffic from the load balancer as soon -as it is created. - -5. Create the load balancer with the two nodes. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->addVirtualIp('PUBLIC'); - $loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) - )); - -In the example above, the load balancer will have a virtual IP address -accessible from the public Internet. Also notice that the port the load -balancer listens on (80) does not need to match the ports of its nodes -(8080). - -Next steps ----------- - -Once you have created a load balancer, there is a lot you can do with -it. See the `complete user guide for load balancers `__. diff --git a/doc/services/load-balancer/USERGUIDE.md.rst b/doc/services/load-balancer/USERGUIDE.md.rst deleted file mode 100644 index 788651f2b..000000000 --- a/doc/services/load-balancer/USERGUIDE.md.rst +++ /dev/null @@ -1,840 +0,0 @@ -The Complete User Guide to Load Balancers -========================================= - -Prerequisites -------------- - -Client -~~~~~~ - -To use the load balancers service, you must first instantiate a -``Rackspace`` client object. - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Load Balancer Service -~~~~~~~~~~~~~~~~~~~~~ - -All operations on load balancers are done via a load balancer service -object. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -Cloud Servers -~~~~~~~~~~~~~ - -Many of the examples in this document use two cloud servers as nodes for -the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer -to these two cloud servers. - -Load Balancers --------------- - -A **load balancer** is a device that distributes incoming network -traffic amongst multiple back-end systems. These back-end systems are -called the **nodes** of the load balancer. - -Create Load Balancer -~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $serverOneNode = $loadBalancer->node(); - $serverOneNode->address = $serverOne->addresses->private[0]->addr; - $serverOneNode->port = 8080; - $serverOneNode->condition = 'ENABLED'; - - $serverTwoNode = $loadBalancer->node(); - $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; - $serverTwoNode->port = 8080; - $serverTwoNode->condition = 'ENABLED'; - - $loadBalancer->addVirtualIp('PUBLIC'); - $loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) - )); - -List Load Balancer Details -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a single load balancer's details by using its ID. - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer('254889'); - - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - -List Load Balancers -~~~~~~~~~~~~~~~~~~~ - -You can retrieve a list of all your load balancers. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $loadBalancers = $loadBalancerService->loadBalancerList(); - foreach ($loadBalancers as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - } - -Update Load Balancer Attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can update one or more of the following load balancer attributes: - -- ``name``: The name of the load balancer -- ``algorithm``: The algorithm used by the load balancer to distribute - traffic amongst its nodes. See also: `Load balancing - algorithms <#algorithms>`__. -- ``protocol``: The network protocol used by traffic coming in to the - load balancer. See also: `Protocols <#protocols>`__. -- ``port``: The network port on which the load balancer listens for - incoming traffic. -- ``halfClosed``: Enable or Disable Half-Closed support for the load - balancer. -- ``timeout``: The timeout value for the load balancer to communicate - with its nodes. -- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for - the load balancer. When enabled, any HTTP request will return status - code 301 (Moved Permanently), and the requestor will be redirected to - the requested URL via the HTTPS protocol on port 443. For example, - http://example.com/page.html would be redirected to https:// - example.com/page.html. Only available for HTTPS protocol (``port`` = - 443), or HTTP Protocol with a properly configured SSL Termination - (\`secureTrafficOnly=true, securePort=443). See also: `SSL - Termination <#ssl-termination>`__. - -Updating a single attribute of a load balancer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->update(array( - 'name' => 'New name' - )); - -Updating multiple attributes of a load balancer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->update(array( - 'name' => 'New name', - 'algorithm' => 'ROUND_ROBIN' - )); - -Remove Load Balancer -~~~~~~~~~~~~~~~~~~~~ - -When you no longer have a need for the load balancer, you can remove it. - -.. code:: php - - $loadBalancer->delete(); - -Nodes ------ - -A **node** is a backend device that provides a service on specified IP -and port. An example of a load balancer node might be a web server -serving HTTP traffic on port 8080. - -A load balancer typically has multiple nodes attached to it so it can -distribute incoming network traffic amongst them. - -List Nodes -~~~~~~~~~~ - -You can list the nodes attached to a load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $nodes = $loadBalancer->nodeList(); - foreach ($nodes as $node) { - /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ - } - -Add Nodes -~~~~~~~~~ - -You can attach additional nodes to a load balancer. Assume -``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` -and ``$serverTwo`` - and you want to attach a third node to it, say -``$serverThree``, which provides a service on port 8080. - -**Important:** Remember to call ``$loadBalancer->addNodes()`` after all -the calls to ``$loadBalancer->addNode()`` as shown below. - -.. code:: php - - $address = $serverThree->addresses->private[0]->addr; - $loadBalancer->addNode($address, 8080); - $loadBalancer->addNodes(); - -The ``addNode`` method accepts three more optional parameters, in -addition to the two shown above: - -| Position \| Description \| Data type \| Required? \| Default value \| -| ----------- \| --------------- \| --------------\| -------------- \| ------------------ \| -|  1 \| IP address of node \| String \| Yes \| - \| -|  2 \| Port used by node's service \| Integer \| Yes \| - \| -|  3 \| Starting condition of node: -|  4 \| Type of node to add: -|  5 \| Weight, between 1 and 100, given to node when distributing -traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the -``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| -No \| 1 \| - -Modify Nodes -~~~~~~~~~~~~ - -You can modify one or more of the following node attributes: - -- ``condition``: The condition of the load balancer: - - - ``ENABLED`` – Node is ready to receive traffic from the load - balancer. - - ``DISABLED`` – Node should not receive traffic from the load - balancer. - - ``DRAINING`` – Node should process any traffic it is already - receiving but should not receive any further traffic from the load - balancer. - -- ``type``: The type of the node: - - - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation - to receive traffic from the load balancer. - - ``SECONDARY`` – Nodes defined as SECONDARY are only in the - rotation to receive traffic from the load balancer when all the - primary nodes fail. - -- ``weight``: The weight, between 1 and 100, given to node when - distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the - ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. - -Modifying a single attribute of a node -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\NodeCondition; - - $node->update(array( - 'condition' => NodeCondition::DISABLED - )); - -Modifying multiple attributes of a node -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\NodeCondition; - use OpenCloud\LoadBalancer\Enum\NodeType; - - $node->update(array( - 'condition' => NodeCondition::DISABLED, - 'type' => NodeType::SECONDARY - )); - -Remove Nodes -~~~~~~~~~~~~ - -There are two ways to remove a node. - -Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $node->delete(); - -Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->removeNode(490639); - -The ``removeNode`` method, as shown above, accepts the following -arguments: - -+------------+---------------+-------------+-------------+-----------------+ -| Position | Description | Data type | Required? | Default value | -+============+===============+=============+=============+=================+ -| 1 | ID of node | Integer | Yes | - | -+------------+---------------+-------------+-------------+-----------------+ - -View Node Service Events -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can view events associated with the activity between a node and a -load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $nodeEvents = $loadBalancer->nodeEventList(); - foreach ($nodeEvents as $nodeEvent) { - /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ - } - -Virtual IPs ------------ - -A **virtual IP (VIP)** makes a load balancer accessible by clients. The -load balancing service supports either a public VIP address -(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP -address (``SERVICENET``), routable only within the region in which the -load balancer resides. - -List Virtual IPs -~~~~~~~~~~~~~~~~ - -You can list the VIPs associated with a load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $vips = $loadBalancer->virtualIpList(); - foreach ($vips as $vip) { - /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ - } - -Add Virtual IPv6 -~~~~~~~~~~~~~~~~ - -You can add additional IPv6 VIPs to a load balancer. - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\IpType; - - $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); - -The ``addVirtualIp`` method, as shown above, accepts the following -arguments: - -| Position \| Description \| Data type \| Required? \| Default value \| -| ----------- \| --------------- \| -------------- \|-------------- \| ------------------ \| -|  1 \| Type of VIP: -|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| - -Remove Virtual IPs -~~~~~~~~~~~~~~~~~~ - -You can remove a VIP from a load balancer. - -.. code:: php - - $vip->remove(); - -Please note that a load balancer must have at least one VIP associated -with it. If you try to remove a load balancer's last VIP, a -``ClientErrorResponseException`` will be thrown. - -Algorithms ----------- - -Load balancers use an **algorithm** to determine how incoming traffic is -distributed amongst the back-end nodes. - -List Load Balancing Algorithms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all supported load balancing algorithms using a load -balancer service object. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $algorithms = $loadBalancerService->algorithmList(); - foreach ($algorithms as $algorithm) { - /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ - } - -Protocols ---------- - -When a load balancer is created a network protocol must be specified. -This network protocol should be based on the network protocol of the -back-end service being load balanced. - -List Load Balancing Protocols -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all supported network protocols using a load balancer -service object. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $protocols = $loadBalancerService->protocolList(); - foreach ($protocols as $protocol) { - /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ - } - -Session Persistence -------------------- - -**Session persistence** is a feature of the load balancing service that -forces multiple requests, of the same protocol, from clients to be -directed to the same node. This is common with many web applications -that do not inherently share application state between back-end servers. - -There are two types (or modes) of session persistence: - -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Name | Description | -+===================+===================================================================================================================================================================================================================================+ -| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -List Session Persistence Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistenceType = $sessionPersistence->persistenceType; - - /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ - -In the example above: - -- If session persistence is enabled, the value of - ``$sessionPersistenceType`` is the type of session persistence: - either ``HTTP_COOKIE`` or ``SOURCE_IP``. -- If session persistence is disabled, the value of - ``$sessionPersistenceType`` is ``null``. - -Enable Session Persistence -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistence->update(array( - 'persistenceType' => 'HTTP_COOKIE' - )); - -Disable Session Persistence -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistence->delete(); - -Connection Logging ------------------- - -The **connection logging** feature allows logs to be delivered to a -Cloud Files account every hour. For HTTP-based protocol traffic, these -are Apache-style access logs. For all other traffic, this is connection -and transfer logging. - -Check Logging Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $connectionLogging bool **/ - - $connectionLogging = $loadBalancer->hasConnectionLogging(); - -In the example above: - -- If connection logging is enabled, the value of ``$connectionLogging`` - is ``true``. -- If connection logging is disabled, the value of - ``$connectionLogging`` is ``false``. - -Enable Connection Logging -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableConnectionLogging(true); - -Disable Connection Logging -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableConnectionLogging(false); - -Error Page ----------- - -An **error page** is the html file that is shown to the end user when an -error in the service has been thrown. By default every virtual server is -provided with the default error file. It is also possible to set a -custom error page for a load balancer. - -View Error Page Content -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPageContent = $errorPage->content; - - /** @var $errorPageContent string **/ - -In the example above the value of ``$errorPageContent`` is the HTML for -that page. This could either be the HTML of the default error page or of -your custom error page. - -Set Custom Error Page -~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPage->update(array( - 'content' => '' - )); - -Delete Custom Error Page -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPage->delete(); - -Allowed Domains ---------------- - -**Allowed domains** are a restricted set of domain names that are -allowed to add load balancer nodes. - -List Allowed Domains -~~~~~~~~~~~~~~~~~~~~ - -You can list all allowed domains using a load balancer service object. -An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is -returned. - -.. code:: php - - $allowedDomains = $loadBalancerService->allowedDomainList(); - foreach ($allowedDomains as $allowedDomain) { - /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ - } - -Access Lists ------------- - -**Access Lists** allow fine-grained network access to a load balancer's -VIP. Using access lists, network traffic to a load balancer's VIP can be -allowed or denied from a single IP address, multiple IP addresses or -entire network subnets. - -Note that ``ALLOW`` network items will take precedence over ``DENY`` -network items in an access list. - -To reject traffic from all network items except those with the ``ALLOW`` -type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. - -View Access List -~~~~~~~~~~~~~~~~ - -You can view a load balancer's access list. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $accessList = $loadBalancer->accessList(); - foreach ($accessList as $networkItem) { - /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ - } - -Add Network Items To Access List -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can add network items to a load balancer's access list very easily: - -.. code:: php - - $loadBalancer->createAccessList(array( - (object) array( - 'type' => 'ALLOW', - 'address' => '206.160.165.1/24' - ), - (object) array( - 'type' => 'DENY', - 'address' => '0.0.0.0/0' - ) - )); - -In the above example, we allowed access for 1 IP address, and used the -"0.0.0.0" wildcard to blacklist all other traffic. - -Remove Network Item From Access List -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You an remove a network item from a load balancer's access list. - -.. code:: php - - $networkItem->delete(); - -Content Caching ---------------- - -When **content caching** is enabled on a load balancer, -recently-accessed files are stored on the load balancer for easy -retrieval by web clients. Requests to the load balancer for these files -are serviced by the load balancer itself, which reduces load off its -back-end nodes and improves response times as well. - -Check Content Caching Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $contentCaching bool **/ - - $contentCaching = $loadBalancer->hasContentCaching(); - -In the example above: - -- If content caching is enabled, the value of ``$contentCaching`` is - ``true``. -- If content caching is disabled, the value of ``$contentCaching`` is - ``false``. - -Enable Content Caching -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableContentCaching(true); - -Disable Content Caching -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableContentCaching(false); - -SSL Termination ---------------- - -The SSL Termination feature allows a load balancer user to terminate SSL -traffic at the load balancer layer versus at the web server layer. A -user may choose to configure SSL Termination using a key and an SSL -certificate or an (Intermediate) SSL certificate. - -When SSL Termination is configured on a load balancer, a secure shadow -server is created that listens only for secure traffic on a -user-specified port. This shadow server is only visible to and -manageable by the system. Existing or updated attributes on a load -balancer with SSL Termination will also apply to its shadow server. For -example, if Connection Logging is enabled on an SSL load balancer, it -will also be enabled on the shadow server and Cloud Files logs will -contain log files for both. - -View current SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ - - $sslConfig = $loadBalancer->SSLTermination(); - -Update SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sslConfig->update(array( - 'enabled' => true, - 'securePort' => 443, - 'privateKey' => $key, - 'certificate' => $cert - )); - -For a full list, with explanations, of required and optional attributes, -please consult the `official -documentation `__ - -Delete SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sslConfig->delete(); - -Metadata --------- - -Metadata can be associated with each load balancer and each node for the -client's personal use. It is defined using key-value pairs where the key -and value consist of alphanumeric characters. A key is unique per load -balancer. - -List metadata -~~~~~~~~~~~~~ - -.. code:: php - - $metadataList = $loadBalancer->metadataList(); - - foreach ($metadataList as $metadataItem) { - printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); - } - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Add metadata -~~~~~~~~~~~~ - -.. code:: php - - $metadataItem = $loadBalancer->metadata(); - $metadataItem->create(array( - 'key' => 'foo', - 'value' => 'bar' - )); - -Modify metadata -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadataItem = $loadBalancer->metadata('foo'); - $metadataItem->update(array( - 'value' => 'baz' - )); - -Remove metadata -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadataItem->delete(); - -Monitors --------- - -The load balancing service includes a health monitoring operation which -periodically checks your back-end nodes to ensure they are responding -correctly. If a node is not responding, it is removed from rotation -until the health monitor determines that the node is functional. In -addition to being performed periodically, the health check also is -performed against every node that is added to ensure that the node is -operating properly before allowing it to service traffic. Only one -health monitor is allowed to be enabled on a load balancer at a time. - -.. code:: php - - /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ - - $healthMonitor = $loadBalancer->healthMonitor(); - - printf( - "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", - $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout - ); - -For a full list, with explanations, of required and optional attributes, -please consult the `official -documentation `__ - -Update or delete -~~~~~~~~~~~~~~~~ - -.. code:: php - - // Update - $healthMonitor->update(array( - 'delay' => 120, - 'timeout' => 60, - 'type' => 'CONNECT' - 'attemptsBeforeDeactivation' => 3 - )); - - // Delete - $healthMonitor->delete(); - -Statistics ----------- - -You can retrieve detailed stats about your load balancer, including the -following information: - -- ``connectTimeOut`` – Connections closed by this load balancer because - the 'connect\_timeout' interval was exceeded. -- ``connectError`` – Number of transaction or protocol errors in this - load balancer. -- ``connectFailure`` – Number of connection failures in this load - balancer. -- ``dataTimedOut`` – Connections closed by this load balancer because - the 'timeout' interval was exceeded. -- ``keepAliveTimedOut`` – Connections closed by this load balancer - because the 'keepalive\_timeout' interval was exceeded. -- ``maxConn`` – Maximum number of simultaneous TCP connections this - load balancer has processed at any one time. - -.. code:: php - - /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ - - $stats = $loadBalancer->stats(); - -Usage Reports -------------- - -The load balancer usage reports provide a view of all transfer activity, -average number of connections, and number of virtual IPs associated with -the load balancing service. Current usage represents all usage recorded -within the preceding 24 hours. Values for both incomingTransfer and -outgoingTransfer are expressed in bytes transferred. - -The optional startTime and endTime parameters can be used to filter all -usage. If the startTime parameter is supplied but the endTime parameter -is not, then all usage beginning with the startTime will be provided. -Likewise, if the endTime parameter is supplied but the startTime -parameter is not, then all usage will be returned up to the endTime -specified. - -.. code:: php - - # View billable LBs - $billable = $service->billableLoadBalancerList(); - - foreach ($billable as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - - # View usage - /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ - $usage = $loadBalancer->usage(); - - echo $usage->averageNumConnections, PHP_EOL; - } - diff --git a/doc/services/load-balancer/access.rst b/doc/services/load-balancer/access.rst new file mode 100644 index 000000000..4387dd8de --- /dev/null +++ b/doc/services/load-balancer/access.rst @@ -0,0 +1,79 @@ +Allowed Domains +=============== + +List Allowed Domains +-------------------- + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code-block:: php + + $allowedDomains = $service->allowedDomainList(); + + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + + +Access Lists +============ + +Access Lists allow fine-grained network access to a load balancer's VIP. Using +access lists, network traffic to a load balancer's VIP can be allowed or denied +from a single IP address, multiple IP addresses or entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` network +items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +.. include:: lb-setup.sample.rst + + +View Access List +---------------- + +You can view a load balancer's access list: + +.. code-block:: php + + $accessList = $loadBalancer->accessList(); + + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + + +Add Network Items To Access List +-------------------------------- + +You can add network items to a load balancer's access list very easily: + +.. code-block:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + + +Remove Network Item From Access List +------------------------------------ + +You an remove a network item from a load balancer's access list: + +.. code-block:: php + + $networkItem->delete(); diff --git a/doc/services/load-balancer/caching.rst b/doc/services/load-balancer/caching.rst new file mode 100644 index 000000000..6678e048a --- /dev/null +++ b/doc/services/load-balancer/caching.rst @@ -0,0 +1,35 @@ +Content Caching +=============== + +When content caching is enabled on a load balancer, recently-accessed files are +stored on the load balancer for easy retrieval by web clients. Requests to the +load balancer for these files are serviced by the load balancer itself, which +reduces load off its back-end nodes and improves response times as well. + + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $contentCaching = $loadBalancer->hasContentCaching(); + + +Enable Content Caching +---------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(true); + + +Disable Content Caching +----------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(false); diff --git a/doc/services/load-balancer/errors.rst b/doc/services/load-balancer/errors.rst new file mode 100644 index 000000000..041c19d45 --- /dev/null +++ b/doc/services/load-balancer/errors.rst @@ -0,0 +1,44 @@ +Error Pages +=========== + +.. include:: lb-setup.sample.rst + +An error page is the html file that is shown to the end user when an error in +the service has been thrown. By default every virtual server is provided with +the default error file. It is also possible to set a custom error page for a +load balancer. + + +View Error Page Content +----------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + + +Set Custom Error Page +--------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + + +Delete Custom Error Page +------------------------ + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index e69de29bb..bf2b6dd0c 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -0,0 +1,93 @@ +Load Balancer v1 +================ + +.. note:: + + This service is only available for Rackspace users. + + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Load Balancer service: + +.. code-block:: php + + $service = $client->loadBalancerService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + load-balancers + nodes + virtual-ips + access + caching + errors + logging + monitors + metadata + sessions + ssl + stats + + +Glossary +-------- + + allowed domain + Allowed domains are a restricted set of domain names that are allowed to add + load balancer nodes. + + content caching + When content caching is enabled on a load balancer, recently-accessed files + are stored on the load balancer for easy retrieval by web clients. Requests to + the load balancer for these files are serviced by the load balancer itself, + which reduces load off its back-end nodes and improves response times as well. + + health monitor + The load balancing service includes a health monitoring operation which + periodically checks your back-end nodes to ensure they are responding + correctly. If a node is not responding, it is removed from rotation until the + health monitor determines that the node is functional. In addition to being + performed periodically, the health check also is performed against every node + that is added to ensure that the node is operating properly before allowing it + to service traffic. Only one health monitor is allowed to be enabled on a load + balancer at a time. + + load balancer + A load balancer is a device that distributes incoming network + traffic amongst multiple back-end systems. These back-end systems are + called the nodes of the load balancer. + + metadata + Metadata can be associated with each load balancer and each node for the + client's personal use. It is defined using key-value pairs where the key + and value consist of alphanumeric characters. A key is unique per load + balancer. + + node + A node is a backend device that provides a service on specified IP and port. + An example of a load balancer node might be a web server serving HTTP + traffic on port 8080. A load balancer typically has multiple nodes attached + to it so it can distribute incoming network traffic amongst them. + + session persistence + Session persistence is a feature of the load balancing service that forces + multiple requests, of the same protocol, from clients to be directed to the + same node. This is common with many web applications that do not inherently + share application state between back-end servers. + + virtual IP + A virtual IP (VIP) makes a load balancer accessible by clients. The + load balancing service supports either a public VIP address + (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP + address (``SERVICENET``), routable only within the region in which the + load balancer resides. diff --git a/doc/services/load-balancer/lb-setup.sample.rst b/doc/services/load-balancer/lb-setup.sample.rst new file mode 100644 index 000000000..96e228b8d --- /dev/null +++ b/doc/services/load-balancer/lb-setup.sample.rst @@ -0,0 +1,9 @@ +Setup +----- + +In order to interact with this feature you must first retrieve a particular +load balancer, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer('{id}'); diff --git a/doc/services/load-balancer/load-balancer.rst b/doc/services/load-balancer/load-balancer.rst new file mode 100644 index 000000000..7071794df --- /dev/null +++ b/doc/services/load-balancer/load-balancer.rst @@ -0,0 +1,165 @@ +Load Balancer +============= + +.. note:: + + Many of the examples in this document use two cloud servers as nodes for + the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer + to these two cloud servers. + + +Create Load Balancer +-------------------- + +The first step is to instantiate an empty object, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer(); + +In essence, all a load balancer does is evenly distribute traffic between +various back-end nodes - which can be Compute or Database instances. So we will +need to add a few when creating our load balancer: + +.. code-block:: php + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + +All that remains is apply final configuration touches, such as name and the +port number, before submitting to the API: + +.. code-block:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode), + 'algorithm' => 'ROUND_ROBIN', + )); + +For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ +please see the sections below. + + +List Load Balancer Details +-------------------------- + +You can retrieve a single load balancer's details by using its ID: + +.. code-block:: php + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + $loadBalancer = $service->loadBalancer('{loadBalancerId}'); + + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers: + +.. code-block:: php + + $loadBalancers = $service->loadBalancerList(); + + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + + +Update a Load Balancer +---------------------- + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +.. code-block:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it: + +.. code-block:: php + + $loadBalancer->delete(); + + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. Common protocols are ``HTTP``, ``HTTPS`` +and ``MYSQL``. A full list is available `here `_. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols like so: + +.. code-block:: php + + $protocols = $service->protocolList(); + + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. A full list is available `here +`_. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can programmatically list all supported load balancing algorithms: + +.. code-block:: php + + $algorithms = $service->algorithmList(); + + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } diff --git a/doc/services/load-balancer/logging.rst b/doc/services/load-balancer/logging.rst new file mode 100644 index 000000000..29c37aee2 --- /dev/null +++ b/doc/services/load-balancer/logging.rst @@ -0,0 +1,33 @@ +Connection Logging +================== + +The connection logging feature allows logs to be delivered to a Cloud Files +account every hour. For HTTP-based protocol traffic, these are Apache-style +access logs. For all other traffic, this is connection and transfer logging. + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $connectionLogging = $loadBalancer->hasConnectionLogging(); + + +Enable Connection Logging +------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(true); + + +Disable Connection Logging +-------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(false); diff --git a/doc/services/load-balancer/metadata.rst b/doc/services/load-balancer/metadata.rst new file mode 100644 index 000000000..2b38de129 --- /dev/null +++ b/doc/services/load-balancer/metadata.rst @@ -0,0 +1,46 @@ +Metadata +======== + +.. include:: lb-setup.sample.rst + +List metadata +------------- + +.. code-block:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + + +Add metadata +------------ + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + + +Modify metadata +--------------- + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + + +Remove metadata +--------------- + +.. code-block:: php + + $metadataItem->delete(); diff --git a/doc/services/load-balancer/monitors.rst b/doc/services/load-balancer/monitors.rst new file mode 100644 index 000000000..9c463f270 --- /dev/null +++ b/doc/services/load-balancer/monitors.rst @@ -0,0 +1,43 @@ +Health Monitors +=============== + +.. include:: lb-setup.sample.rst + +Retrieve monitor details +------------------------ + +.. code-block:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Update monitor +-------------- + +.. code-block:: php + + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + +Delete monitor +-------------- + +.. code-block:: php + + $healthMonitor->delete(); diff --git a/doc/services/load-balancer/nodes.rst b/doc/services/load-balancer/nodes.rst new file mode 100644 index 000000000..dfaadc404 --- /dev/null +++ b/doc/services/load-balancer/nodes.rst @@ -0,0 +1,124 @@ +Nodes +===== + +.. include:: lb-setup.sample.rst + +List Nodes +---------- + +You can list the nodes attached to a load balancer: + +.. code-block:: php + + $nodes = $loadBalancer->nodeList(); + + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + + +Add Nodes +--------- + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code-block:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + + +The signature for ``addNodes`` is as follows: + +.. function:: addNodes($address, $port[, $condition = 'ENABLED'[, $type = null[, $weight = null]]]) + + Add a node to a load balancer + + :param string $address: the IP address of the node + :param integer $port: the port number of the node + :param string $condition: the initial condition of the code. Defaults to ``ENABLED`` + :param string $type: either ``PRIMARY`` or ``SECONDARY`` + :param integer $weight: the node weight (for round-robin algorithm) + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +Modify Nodes +------------ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + + +Remove Nodes +------------ + +There are two ways to remove a node. The first way is on an +``OpenCloud\LoadBalancer\Resource\Node`` instance, like so: + + +.. code-block:: php + + $node->delete(); + +The second is with an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` +instance and the node's ID, like so: + +.. code-block:: php + + $loadBalancer->removeNode('{nodeId}'); + +where '{nodeId}' is the integer ID of the node itself - this is a required value. + + +View Node Service Events +------------------------ + +You can view events associated with the activity between a node and a +load balancer: + +.. code-block:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } diff --git a/doc/services/load-balancer/sessions.rst b/doc/services/load-balancer/sessions.rst new file mode 100644 index 000000000..3d7f71cfe --- /dev/null +++ b/doc/services/load-balancer/sessions.rst @@ -0,0 +1,53 @@ +Session Persistence +=================== + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. include:: lb-setup.sample.rst + + +List Session Persistence Configuration +-------------------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + $sessionPersistenceType = $sessionPersistence->persistenceType; + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + + +Enable Session Persistence +-------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + + +Disable Session Persistence +--------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); diff --git a/doc/services/load-balancer/ssl.rst b/doc/services/load-balancer/ssl.rst new file mode 100644 index 000000000..12b28a6af --- /dev/null +++ b/doc/services/load-balancer/ssl.rst @@ -0,0 +1,52 @@ +SSL Termination +=============== + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +.. include:: lb-setup.sample.rst + + +View configuration +------------------ + +.. code-block:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + $sslConfig = $loadBalancer->SSLTermination(); + + +Update configuration +-------------------- + +.. code-block:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Delete configuration +-------------------- + +.. code-block:: php + + $sslConfig->delete(); diff --git a/doc/services/load-balancer/stats.rst b/doc/services/load-balancer/stats.rst new file mode 100644 index 000000000..65077643f --- /dev/null +++ b/doc/services/load-balancer/stats.rst @@ -0,0 +1,59 @@ +Statistics and Usage Reports +============================ + +.. include:: lb-setup.sample.rst + +Retrieve LB stats +----------------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code-block:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + $stats = $loadBalancer->stats(); + + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code-block:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } diff --git a/doc/services/load-balancer/virtual-ips.rst b/doc/services/load-balancer/virtual-ips.rst new file mode 100644 index 000000000..fa0add613 --- /dev/null +++ b/doc/services/load-balancer/virtual-ips.rst @@ -0,0 +1,74 @@ +Virtual IPs +=========== + +.. include:: lb-setup.sample.rst + + +List Virtual IPs +---------------- + +You can list the VIPs associated with a load balancer like so: + +.. code-block:: php + + $vips = $loadBalancer->virtualIpList(); + + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + + +Get existing VIP +---------------- + +To retrieve the details of an existing VIP on a load balancer, you will need +its ID: + +.. code-block:: + + $vip = $loadBalancer->virtualIp('{virtualIpId}'); + + +Add Virtual IPv6 +---------------- + +You can add additional IPv6 VIPs to a load balancer using the following method: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +the first argument is the type of network your IP address will server traffic in +- and can either be ``PUBLIC`` or ``PRIVATE``. The second argument is the version +of IP address, either ``4`` or ``6``. + + +Add Virtual IPv4 +---------------- + +Similar to above: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 4); + + +Remove Virtual IP +----------------- + +You can remove a VIP from a load balancer. + +.. code-block:: php + + $vip->remove(); + + +.. note:: + + A load balancer must have at least one VIP associated with it. If you try to + remove a load balancer's last VIP, a ``ClientErrorResponseException`` will be + thrown. From 1757f9df620238cfa19e47c3f9c5cab6026771f2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:32 +0100 Subject: [PATCH 582/835] Add monitoring docs --- doc/services/monitoring/Agents.md.rst | 202 -------------- doc/services/monitoring/Entities.md.rst | 77 ------ doc/services/monitoring/Service.md.rst | 23 -- doc/services/monitoring/agents.rst | 188 +++++++++++++ .../monitoring/{Alarms.md.rst => alarms.rst} | 84 +++--- .../{Changelogs.md.rst => changelogs.rst} | 17 +- .../monitoring/{Checks.md.rst => checks.rst} | 189 +++++++------ doc/services/monitoring/entities.rst | 77 ++++++ doc/services/monitoring/index.rst | 34 +++ .../{Metrics.md.rst => metrics.rst} | 54 ++-- ...Notifications.md.rst => notifications.rst} | 252 ++++++++++-------- .../monitoring/{Views.md.rst => views.rst} | 20 +- .../monitoring/{Zones.md.rst => zones.rst} | 42 ++- 13 files changed, 661 insertions(+), 598 deletions(-) delete mode 100644 doc/services/monitoring/Agents.md.rst delete mode 100644 doc/services/monitoring/Entities.md.rst delete mode 100644 doc/services/monitoring/Service.md.rst create mode 100644 doc/services/monitoring/agents.rst rename doc/services/monitoring/{Alarms.md.rst => alarms.rst} (66%) rename doc/services/monitoring/{Changelogs.md.rst => changelogs.rst} (52%) rename doc/services/monitoring/{Checks.md.rst => checks.rst} (80%) create mode 100644 doc/services/monitoring/entities.rst rename doc/services/monitoring/{Metrics.md.rst => metrics.rst} (83%) rename doc/services/monitoring/{Notifications.md.rst => notifications.rst} (64%) rename doc/services/monitoring/{Views.md.rst => views.rst} (58%) rename doc/services/monitoring/{Zones.md.rst => zones.rst} (71%) diff --git a/doc/services/monitoring/Agents.md.rst b/doc/services/monitoring/Agents.md.rst deleted file mode 100644 index 943983f7e..000000000 --- a/doc/services/monitoring/Agents.md.rst +++ /dev/null @@ -1,202 +0,0 @@ -Agents -====== - -Intro ------ - -The Monitoring Agent resides on the host server being monitored. The -agent allows you to gather on-host metrics based on agent checks and -push them to Cloud Monitoring where you can analyze them, use them with -the Cloud Monitoring infrastructure (such as alarms), and archive them. - -For more information about this feature, including a brief overview of -its core design principles and security layers, see the `official API -documentation `__. - -Setup ------ - -.. code:: php - - $agentId = '00-agent.example.com'; - $agent = $service->getAgent($agentId); - -You can view the `service page `__ for more information -about setting up the Cloud Monitoring service. - -List agents ------------ - -.. code:: php - - $agents = $service->getAgents(); - - foreach ($agents as $agent) { - echo $agent->getLastConnected(); - } - -Please consult the `iterator doc `__ for -more information about iterators. - -List connections ----------------- - -.. code:: php - - $connections = $agent->getConnections(); - -Please consult the `iterator doc `__ for -more information about iterators. - -Get connection --------------- - -.. code:: php - - /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ - $connection = $agent->getConnection('cntl4qsIbA'); - -Agent Connection properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------------+---------------+--------+---------------------------+ -| Name | Description | Type | Method | -+====================+===============+========+===========================+ -| id | - | - | ``getId()`` | -+--------------------+---------------+--------+---------------------------+ -| guid | - | - | ``getGuid()`` | -+--------------------+---------------+--------+---------------------------+ -| agent\_id | - | - | ``getAgentId()`` | -+--------------------+---------------+--------+---------------------------+ -| endpoint | - | - | ``getEndpoint()`` | -+--------------------+---------------+--------+---------------------------+ -| process\_version | - | - | ``getProcessVersion()`` | -+--------------------+---------------+--------+---------------------------+ -| bundle\_version | - | - | ``getBundleVersion()`` | -+--------------------+---------------+--------+---------------------------+ -| agent\_ip | - | - | ``getAgentIp()`` | -+--------------------+---------------+--------+---------------------------+ - -Agent tokens -============ - -Intro ------ - -Agent tokens are used to authenticate Monitoring agents to the -Monitoring Service. Multiple agents can share a single token. - -Setup ------ - -.. code:: php - - $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; - $agentToken = $service->getAgentToken($tokenId); - -Create agent token ------------------- - -.. code:: php - - $newToken = $service->getAgentToken(); - $newToken->create(array('label' => 'Foobar')); - -List agent tokens ------------------ - -.. code:: php - - $agentTokens = $service->getAgentTokens(); - - foreach ($agentTokens as $token) { - echo $token->getLabel(); - } - -Please consult the `iterator doc `__ for -more information about iterators. - -Update and delete Agent Token ------------------------------ - -.. code:: php - - // Update - $token->update(array( - 'label' => 'New label' - )); - - // Delete - $token->delete(); - -Agent Host Information -====================== - -Info ----- - -An agent can gather host information, such as process lists, network -configuration, and memory usage, on demand. You can use the -host-information API requests to gather this information for use in -dashboards or other utilities. - -Setup ------ - -.. code:: php - - $host = $monitoringService->getAgentHost(); - -Get some metrics ----------------- - -.. code:: php - - $cpuInfo = $host->info('cpus'); - $diskInfo = $host->info('disks'); - $filesystemInfo = $host->info('filesystems'); - $memoryInfo = $host->info('memory'); - $networkIntInfo = $host->info('network_interfaces'); - $processesInfo = $host->info('processes'); - $systemInfo = $host->info('system'); - $userInfo = $host->info('who'); - - // What CPU models do we have? - foreach ($cpuInfo as $cpuMetric) { - echo $cpuMetric->model, PHP_EOL; - } - - // How many disks do we have? - echo $diskInfo->count(); - - // What's the available space on our ext4 filesystem? - foreach ($filesystemInfo as $filesystemMetric) { - if ($filesystemMetric->sys_type_name == 'ext4') { - echo $filesystemMetric->avail; - } - } - -Agent targets -============= - -Info ----- - -Each agent check type gathers data for a related set of target devices -on the server where the agent is installed. For example, -``agent.network`` gathers data for network devices. The actual list of -target devices is specific to the configuration of the host server. By -focusing on specific targets, you can efficiently narrow the metric data -that the agent gathers. - -List agent targets -~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $targets = $service->getAgentTargets(); - - foreach ($targets as $target) { - echo $target->getType(); - } - diff --git a/doc/services/monitoring/Entities.md.rst b/doc/services/monitoring/Entities.md.rst deleted file mode 100644 index 068aec122..000000000 --- a/doc/services/monitoring/Entities.md.rst +++ /dev/null @@ -1,77 +0,0 @@ - Entities -========= - -Info ----- - -An entity is the target of what you are monitoring. For example, you can -create an entity to monitor your website, a particular web service, or -your Rackspace server. Note that an entity represents only one item in -the monitoring system -- if you wanted to monitor each server in a -cluster, you would create an entity for each of the servers. You would -not create a single entity to represent the entire cluster. - -An entity can have multiple checks associated with it. This allows you -to check multiple services on the same host by creating multiple checks -on the same entity, instead of multiple entities each with a single -check. - -Setup ------ - -.. code:: php - - $entity = $service->getEntity(); - -For more information about setting up the ``$service`` object, please -see the userguide tutorial for `services `__. - -Attributes ----------- - -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| Name | Description | Required? | Data type | Method | -+=================+=========================================================================+=============+=====================================================+========================+ -| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ - -Create Entity -------------- - -.. code:: php - - $service->createEntity(array( - 'label' => 'Brand New Entity', - 'ip_addresses' => array( - 'default' => '127.0.0.4', - 'b' => '127.0.0.5', - 'c' => '127.0.0.6', - 'test' => '127.0.0.7' - ), - 'metadata' => array( - 'all' => 'kinds', - 'of' => 'stuff', - 'can' => 'go', - 'here' => 'null is not a valid value' - ) - )); - -Update and delete Entity ------------------------- - -.. code:: php - - // Update - $entity->update(array( - 'label' => 'New label for my entity' - )); - - // Delete - $entity->delete(); - diff --git a/doc/services/monitoring/Service.md.rst b/doc/services/monitoring/Service.md.rst deleted file mode 100644 index 09c25c21d..000000000 --- a/doc/services/monitoring/Service.md.rst +++ /dev/null @@ -1,23 +0,0 @@ -Cloud Monitoring Service -======================== - -Initializing the Cloud Monitoring is easy - and can be done in a similar -way to all other Rackspace services: - -1. Create client and pass in auth details. For more information about - creating clients, please consult the `Client - documentation <../Clients.md>`__. -2. Use the factory method, specifying additional parameters where - necessary: - -.. code:: php - - $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); - -All three parameters are optional - if not specified, it will revert to -the service's default values which are: - -- Name = ``cloudMonitoring`` -- Region = ``DFW`` -- URL type = ``publicURL`` - diff --git a/doc/services/monitoring/agents.rst b/doc/services/monitoring/agents.rst new file mode 100644 index 000000000..f4c673608 --- /dev/null +++ b/doc/services/monitoring/agents.rst @@ -0,0 +1,188 @@ +Agents +====== + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Retrieve details about an agent +------------------------------- + +.. code-block:: php + + $agent = $service->getAgent('{agentId}'); + + +List agents +----------- + +.. code-block:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + + +List connections +---------------- + +.. code-block:: php + + $connections = $agent->getConnections(); + + +Get connection +-------------- + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('{connectionId}'); + + +Once you have access to an agent's ``OpenCloud\CloudMonitoring\Resource\AgentConnection`` +object, these are the attributes you can access: + ++--------------------+---------------------------+ +| Name | Method | ++====================+===========================+ +| id | ``getId()`` | ++--------------------+---------------------------+ +| guid | ``getGuid()`` | ++--------------------+---------------------------+ +| agent_id | ``getAgentId()`` | ++--------------------+---------------------------+ +| endpoint | ``getEndpoint()`` | ++--------------------+---------------------------+ +| process_version | ``getProcessVersion()`` | ++--------------------+---------------------------+ +| bundle_version | ``getBundleVersion()`` | ++--------------------+---------------------------+ +| agent_ip | ``getAgentIp()`` | ++--------------------+---------------------------+ + +Agent tokens +============ + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Retrieve an agent token +----------------------- + +.. code-block:: php + + $agentToken = $service->getAgentToken('{tokenId}'); + + +Create agent token +------------------ + +.. code-block:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + + +List agent tokens +----------------- + +.. code-block:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + + +Update agent token +------------------ + +.. code-block:: php + + $token->update(array( + 'label' => 'New label' + )); + + +Update agent token +------------------ + +.. code-block:: php + + $token->delete(); + + +Agent Host Information +====================== + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code-block:: php + + $host = $service->getAgentHost(); + + +Get some metrics +---------------- + +.. code-block:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +------------------ + +.. code-block:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } diff --git a/doc/services/monitoring/Alarms.md.rst b/doc/services/monitoring/alarms.rst similarity index 66% rename from doc/services/monitoring/Alarms.md.rst rename to doc/services/monitoring/alarms.rst index 2918637e9..297a57407 100644 --- a/doc/services/monitoring/Alarms.md.rst +++ b/doc/services/monitoring/alarms.rst @@ -1,9 +1,6 @@ Alarms ====== -Info ----- - Alarms bind alerting rules, entities, and notification plans into a logical unit. Alarms are responsible for determining a state (``OK``, ``WARNING`` or ``CRITICAL``) based on the result of a Check, and @@ -15,25 +12,39 @@ documentation getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + +Retrieve alarm +-------------- -.. code:: php +.. code-block:: php - $alarmId = 'alAAAA'; - $alarm = $check->getAlarm(); + $alarm = $check->getAlarm('{alarmId}'); -For more information about working with Checks, please see the -`appropriate documentation `__. -Attributes ----------- +Once you have access to a ``OpenCloud\Monitoring\Resource\Alarm`` object, these +are the attributes you can access: +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ | Name | Description | Required? | Method | +==========================+=============================================================================+=============+=================================+ -| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | +| check_id | The ID of the check to alert on. | Required | ``getCheckId()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ -| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | +| notification_plan_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ | criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ @@ -44,38 +55,45 @@ Attributes | metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + Create Alarm ------------ -.. code:: php +.. code-block:: php + + $alarm = $check->getAlarm(); + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); - $alarm->create(array( - 'check_id' => 'chAAAA', - 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', - 'notification_plan_id' => 'npAAAAA' - )); List Alarms ----------- -.. code:: php +.. code-block:: php - $alarms = $entity->getAlarms(); + $alarms = $entity->getAlarms(); - foreach ($alarms as $alarm) { - echo $alarm->getId(); - } + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } -Update and delete Alarm ------------------------ -.. code:: php +Update Alarm +------------ + +.. code-block:: php - // Update - $alarm->update(array( - 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' - )); + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + +Delete alarm +------------ - // Delete - $alarm->delete(); +.. code-block:: php + $alarm->delete(); diff --git a/doc/services/monitoring/Changelogs.md.rst b/doc/services/monitoring/changelogs.rst similarity index 52% rename from doc/services/monitoring/Changelogs.md.rst rename to doc/services/monitoring/changelogs.rst index 2e9059e26..a6b26af25 100644 --- a/doc/services/monitoring/Changelogs.md.rst +++ b/doc/services/monitoring/changelogs.rst @@ -1,21 +1,18 @@ Changelogs ========== -Info ----- - The monitoring service records changelogs for alarm statuses. Changelogs are accessible as a Time Series Collection. By default the API queries the last 7 days of changelog information. -Working with Changelogs ------------------------ -.. code:: php +View Changelog +-------------- - $changelog = $service->getChangelog(); +.. code-block:: php - foreach ($changelog as $item) { - $entity = $item->getEntityId(); - } + $changelog = $service->getChangelog(); + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } diff --git a/doc/services/monitoring/Checks.md.rst b/doc/services/monitoring/checks.rst similarity index 80% rename from doc/services/monitoring/Checks.md.rst rename to doc/services/monitoring/checks.rst index 0e8e902a5..73474c170 100644 --- a/doc/services/monitoring/Checks.md.rst +++ b/doc/services/monitoring/checks.rst @@ -1,8 +1,6 @@ Checks ====== -Info ----- A check is one of the foundational building blocks of the monitoring system. The check determines the parts or pieces of the entity that you @@ -23,18 +21,31 @@ servers stops responding) or multiple alarms off of a single check. (e.g. ensure both that a HTTPS server is responding and that it has a valid certificate). -Setup ------ +Create a check +-------------- + +There are various attributes available to you when creating a new monitoring +check: -Checks are sub-resources of Entities: +.. code-block:: php -.. code:: php + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); - $checkId = 'chAAAA'; - $check = $entity->getCheck($checkId); +For a full list of available attributes, consult the list below. Attributes ----------- +~~~~~~~~~~ +------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ | Name | Description | Required? | Data type | @@ -54,92 +65,90 @@ Attributes | timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | +------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ -Attributes used for remote Checks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Optional attributes to be used with remote checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ | Name | Description | Required? | Data type | +===========================+========================================================================================================================================================+=============+============================================================+ -| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | +| monitoring_zones_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | +| target_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | +| target_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | +| target_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -Test parameters (before create) -------------------------------- - -.. code:: php - - $params = array( - 'type' => 'remote.http', - 'details' => array( - 'url' => 'http://example.com', - 'method' => 'GET' - ), - 'monitoring_zones_poll' => array('mzlon'), - 'period' => '100', - 'timeout' => '30', - 'target_alias' => 'default', - 'label' => 'Website check 1' - ); - - // You can do a test to see what would happen - // if a Check is launched with these params - $response = $entity->testNewCheckParams($params); - - echo $response->timestamp; // When was it executed? - echo $response->available; // Was it available? - echo $response->status; // Status code - -Create a Check --------------- -.. code:: php +Test parameters +--------------- + +Sometimes it can be useful to test out the parameters before sending them as a +create call. To do this, pass in the ``$params`` like so: + +.. code-block:: php + + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + + +Send parameters +~~~~~~~~~~~~~~~ + +Once you are satisfied with your configuration parameters, you can complete the +operation and send it to the API like so: + +.. code-block:: php + + $entity->createCheck($params); - $entity->createCheck($params); Test existing Check ------------------- -.. code:: php +.. code-block:: php - // Set arg to TRUE for debug information - $response = $check->test(true); + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; - echo $response->debug_info; List Checks ----------- -.. code:: php +.. code-block:: php + + $checks = $entity->getChecks(); - $checks = $entity->getChecks(); + foreach ($checks as $check) { + echo $check->getId(); + } - foreach ($checks as $check) { - echo $check->getId(); - } -Update and delete Check -~~~~~~~~~~~~~~~~~~~~~~~ +Update Check +------------ -.. code:: php +.. code-block:: php - // Update - $check->update(array('period' => 500)); + $check->update(array('period' => 500)); + + +Delete check +------------ + +.. code-block:: php + + $check->delete(); - // Delete - $check->delete(); Check types =========== -Info ----- - Each check within the Rackspace Cloud Monitoring has a designated check type. The check type instructs the monitoring system how to check the monitored resource. **Note:** Users cannot create, update or delete @@ -168,26 +177,46 @@ using the 'extract' attribute on the remote.http check, however the default metrics will always be present. To determine the exact metrics available, the Test Check API is provided. -Setup ------ +Find an existing check's type +----------------------------- + +If you want to see the type for an existing Check resource: + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + + +List all possible check types +----------------------------- + +.. code-block:: php + + $checkTypes = $service->getCheckTypes(); -If you want to see the type for an existing Check: + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } -.. code:: php - /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ - $checkType = $check->getCheckType(); +Retrieve details about a Type by its ID +--------------------------------------- Alternatively, you can retrieve a specific type based on its ID: -.. code:: php +.. code-block:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); - $checkTypeId = 'remote.dns'; - $checkType = $service->getCheckType($checkTypeId); Attributes ---------- +Once you have access to a ``OpenCloud\CloudMonitoring\Resource\CheckType`` object, +you can query these attributes: + +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ | Name | Description | Data type | Method | +========================+==========================================================================================================================================================================================+=============+===============================+ @@ -195,17 +224,5 @@ Attributes +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ | fields | Check type fields. | Array | ``getFields()`` | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ -| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | +| supported_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ - -List all possible check types ------------------------------ - -.. code:: php - - $checkTypes = $service->getCheckTypes(); - - foreach ($checkTypes as $checkType) { - echo $checkType->getId(); - } - diff --git a/doc/services/monitoring/entities.rst b/doc/services/monitoring/entities.rst new file mode 100644 index 000000000..7e4132af1 --- /dev/null +++ b/doc/services/monitoring/entities.rst @@ -0,0 +1,77 @@ + Entities +========= + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Create Entity +------------- + +.. code-block:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + + +Retrive an entity +----------------- + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + + +Attributes +~~~~~~~~~~ + ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+=========================================================================+=====================================================+========================+ +| label | Defines a name for the entity. | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| agent_id | Agent to which this entity is bound to. | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| ip_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ + + +Update an entity +---------------- + +.. code-block:: php + + $entity->update(array( + 'label' => 'New label for my entity' + )); + + +Delete entity +------------- + +.. code-block:: php + + $entity->delete(); diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index e69de29bb..df0597e8c 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -0,0 +1,34 @@ +Monitoring v1 +============= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Cloud Monitoring service: + +.. code-block:: php + + $service = $client->monitoringService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + +Operations +---------- + +.. toctree:: + + entities + checks + alarms + agents + changelogs + metrics + notifications + views + zones + + +Glossary +-------- diff --git a/doc/services/monitoring/Metrics.md.rst b/doc/services/monitoring/metrics.rst similarity index 83% rename from doc/services/monitoring/Metrics.md.rst rename to doc/services/monitoring/metrics.rst index 4231cf914..0b412a6a3 100644 --- a/doc/services/monitoring/Metrics.md.rst +++ b/doc/services/monitoring/metrics.rst @@ -1,9 +1,6 @@  Metrics ======== -Info ----- - When Monitoring checks run, they generate metrics. These metrics are stored as full resolution data points in the Cloud Monitoring system. Full resolution data points are periodically rolled up (condensed) into @@ -13,6 +10,7 @@ Depending on your needs, you can use the metrics API to fetch individual data points (fine-grained) or rolled up data points (coarse-grained) over a period of time. + Data Granularity ---------------- @@ -42,7 +40,7 @@ elapsed since January 1, 1970** ). The following table shows the number of points that the API returns for a given resolution. Specifying resolution to retrieve data in 48 hour period -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-----------------------------+-------------------------+ | You specify resolution... | API returns points... | @@ -61,7 +59,7 @@ Specifying resolution to retrieve data in 48 hour period +-----------------------------+-------------------------+ Specifying number of points to retrieve data in 48 hour period -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------------+-----------------------------+ | You specify points in the range... | API calculates resolution | @@ -80,7 +78,7 @@ Specifying number of points to retrieve data in 48 hour period +--------------------------------------+-----------------------------+ Data Point Expiration -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ Cloud Monitoring expires data points according to the following schedule: @@ -101,31 +99,45 @@ schedule: | MIN1440 | 365 days | +--------------+--------------+ - Setup +Setup ------ -Metrics are sub-resources of Checks. For more information about working -with Checks, please see the `relevant documentation `__. +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + List all metrics ---------------- -.. code:: php +.. code-block:: php - $metrics = $check->getMetrics(); + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } - foreach ($metrics as $metric) { - echo $metric->getName(); - } Fetch data points ----------------- -.. code:: php - - $data = $check->fetchDataPoints('mzdfw.available', array( - 'resolution' => 'FULL', - 'from' => 1369756378450, - 'to' => 1369760279018 - )); +.. code-block:: php + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); diff --git a/doc/services/monitoring/Notifications.md.rst b/doc/services/monitoring/notifications.rst similarity index 64% rename from doc/services/monitoring/Notifications.md.rst rename to doc/services/monitoring/notifications.rst index f361e3c9c..d7e289ad5 100644 --- a/doc/services/monitoring/Notifications.md.rst +++ b/doc/services/monitoring/notifications.rst @@ -1,9 +1,6 @@ Notifications ============= -Info ----- - A notification is a destination to send an alarm; it can be a variety of different types, and will evolve over time. @@ -11,16 +8,15 @@ For instance, with a webhook type notification, Cloud Monitoring posts JSON formatted data to a user-specified URL on an alert condition (Check goes from ``OK`` -> ``CRITICAL`` and so on). -Setup ------ +Get notification +---------------- -.. code:: php +.. code-block:: php - $id = 'ntAAAA'; - $notification = $service->getNotification($id); + $notification = $service->getNotification('{id}'); -Attributes ----------- +Once you have access to a ``OpenCloud\Monitoring\Resource\Notification`` object, +these are the attributes available for use: +-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ | Name | Description | Data type | Method | @@ -32,76 +28,96 @@ Attributes | type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | +-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +Creating notifications +---------------------- + +The first thing to do when creating a new notification is configure the +parameters which will define the behaviour of your resource: + +.. code-block:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + Test parameters ---------------- +~~~~~~~~~~~~~~~ + +Once this is done, it is often useful to test them out to check whether they +will result in a successful creation: -.. code:: php +.. code-block:: php - $params = array( - 'label' => 'My webhook #1', - 'type' => 'webhook', - 'details' => array( - 'url' => 'http://example.com' - ) - ); + // Test it + $response = $notification->testParams($params); - // Test it - $response = $notification->testParams($params); + if ($response->status == 'Success') { + echo $response->message; + } - if ($response->status == 'Success') { - echo $response->message; - } -Create Notification -------------------- +Send parameters +~~~~~~~~~~~~~~~ -.. code:: php +When you're happy with the parameters you've defined, you can complete the +operation by sending them to the API like so: + +.. code-block:: php + + $notification->create($params); - $notification->create($params); Test existing notification -------------------------- -.. code:: php +.. code-block:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; - $response = $notification->testExisting(true); - echo $response->debug_info; List Notifications ------------------ -.. code:: php +.. code-block:: php - $notifications = $service->getNotifications(); + $notifications = $service->getNotifications(); - foreach ($notifications as $notification) { - echo $notification->getId(); - } + foreach ($notifications as $notification) { + echo $notification->getId(); + } -Update and delete Notifications -------------------------------- -.. code:: php +Update a Notification +--------------------- - // Update - $notification->update(array( - 'label' => 'New notification label' - )); +.. code-block:: php + + $notification->update(array( + 'label' => 'New notification label' + )); + + +Delete a Notification +--------------------- + +.. code-block:: php + + $notification->delete(); - // Delete - $notification->delete(); Notification types ================== -Info ----- - -Pretty self-explanatory. Rackspace Cloud Monitoring currently supports -the following notification types: +Rackspace Cloud Monitoring currently supports the following notification types: Webhook -^^^^^^^ +~~~~~~~ Industry-standard web hooks, where JSON is posted to a configurable URL. It has these attributes: @@ -113,7 +129,7 @@ It has these attributes: +-----------+------------------------------------------+---------------+ Email -^^^^^ +~~~~~ Email alerts where the message is delivered to a specified address. It has these attributes: @@ -124,6 +140,7 @@ has these attributes: | url | An HTTP or HTTPS URL to POST to | Valid URL | +--------+-----------------------------------+-------------+ + Setup ----- @@ -131,34 +148,33 @@ If you've already set up a main Notification object, and want to access functionality for this Notification's particular Notification Type, you can access its property: -.. code:: php +.. code-block:: php - $type = $notification->getNotificationType(); + $type = $notification->getNotificationType(); Alternatively, you can retrieve an independent resource using the ID: -.. code:: php +.. code-block:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); - $typeId = 'pagerduty'; - $type = $service->getNotificationType($typeId); List all possible notification types ------------------------------------ -.. code:: php +.. code-block:: php + + $types = $service->getNotificationTypes(); - $types = $service->getNotificationTypes(); + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } - foreach ($types as $type) { - echo sprintf('%s %s', $type->getName(), $type->getDescription()); - } Notification plans ================== -Info ----- - A notification plan contains a set of notification actions that Rackspace Cloud Monitoring executes when triggered by an alarm. Rackspace Cloud Monitoring currently supports webhook and email @@ -180,60 +196,62 @@ A notification plan, ``npTechnicalContactsEmail``, is provided by default which will email all of the technical contacts on file for an account whenever there is a state change. -Setup ------ +Get a notification plan +----------------------- -.. code:: php +.. code-block:: php - $planId = 'npAAAA'; - $plan = $service->getNotificationPlan(); + $plan = $service->getNotificationPlan('{planId}'); -Attributes ----------- +Once you have access to a ``OpenCloud\\Monitoring\\Resource\\NotificationPlan`` +object, you can access these resources: +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ | Name | Description | Required? | Data type | Method | +===================+====================================================================+=============+=========================+==========================+ | label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | +| critical_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | +| ok_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | +| warning_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ Create Notification Plan ------------------------ -.. code:: php +.. code-block:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + + +Update notification plan +------------------------ + +.. code-block:: php - $plan->create(array( - 'label' => 'New Notification Plan', - 'critical_state' => array('ntAAAA'), - 'ok_state' => array('ntBBBB'), - 'warning_state' => array('ntCCCC') - )); + $plan->update(array( + 'label' => 'New label for my plan' + )); -Update and delete Notification Plan ------------------------------------ -.. code:: php +Delete notification plan +------------------------ + +.. code-block:: php - // Update - $plan->update(array( - 'label' => 'New label for my plan' - )); + $plan->delete(); - // Delete - $plan->delete(); Alarm Notification History ========================== -Info ----- - The monitoring service keeps a record of notifications sent for each alarm. This history is further subdivided by the check on which the notification occurred. Every attempt to send a notification is recorded, @@ -245,12 +263,31 @@ Alarm notification history is accessible as a Time Series Collection. By default alarm notification history is stored for 30 days and the API queries the last 7 days of information. - Setup +Setup ------ -Notification History is a sub-resource of an Alarm. For more information -about working with Alarms, please consult the relevant -`documentation `__. +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +and finally, retrieve the alarm: + +.. code-block:: php + + $alarm = $check->getAlarm('{alarmId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + Discover which Checks have a Notification History ------------------------------------------------- @@ -258,24 +295,25 @@ Discover which Checks have a Notification History This operation list checks for which alarm notification history is available: -.. code:: php +.. code-block:: php + + $checks = $alarm->getRecordedChecks(); - $checks = $alarm->getRecordedChecks(); List Alarm Notification History for a particular Check ------------------------------------------------------ -.. code:: php +.. code-block:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); - $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); Get a particular Notification History item ------------------------------------------ -.. code:: php - - $checkId = 'chAAAA'; - $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; +.. code-block:: php - $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); diff --git a/doc/services/monitoring/Views.md.rst b/doc/services/monitoring/views.rst similarity index 58% rename from doc/services/monitoring/Views.md.rst rename to doc/services/monitoring/views.rst index a5b7245fa..e83b97154 100644 --- a/doc/services/monitoring/Views.md.rst +++ b/doc/services/monitoring/views.rst @@ -1,27 +1,21 @@ Views ===== -Info ----- - Views contain a combination of data that usually includes multiple, different objects. The primary purpose of a view is to save API calls and make data retrieval more efficient. Instead of doing multiple API calls and then combining the result yourself, you can perform a single API call against the view endpoint. + List all Views -------------- -\`\`\`php $views = $service->getViews(); - -foreach ($views as $view) { $entity = $view->getEntity(); - -:: - - echo $view->getTimestamp(); +.. code-block:: php -} \`\`\` + $views = $service->getViews(); -Please consult the `iterator doc `__ for -more information about iterators. + foreach ($views as $view) { + $entity = $view->getEntity(); + echo $view->getTimestamp(); + } diff --git a/doc/services/monitoring/Zones.md.rst b/doc/services/monitoring/zones.rst similarity index 71% rename from doc/services/monitoring/Zones.md.rst rename to doc/services/monitoring/zones.rst index ffe4bba1e..bb588a303 100644 --- a/doc/services/monitoring/Zones.md.rst +++ b/doc/services/monitoring/zones.rst @@ -1,9 +1,6 @@ Zones ===== -Info ----- - A monitoring zone is a location that Rackspace Cloud Monitoring collects data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". It is an abstraction for a general location from which data is @@ -18,50 +15,43 @@ addresses or unrelated machines within that IP address range. A check references a list of monitoring zones it should be run from. - Setup ------- - -.. code:: php +Get details about a zone +------------------------ - $zoneId = 'mzAAAAA'; - $zone = $monitoringService->getMonitoringZone($zoneId); +.. code-block:: php -Attributes ----------- + $zone = $monitoringService->getMonitoringZone('{zoneId}'); +-----------------+------------------+-----------------------------------+------------------------+ | Name | Description | Data type | Method | +=================+==================+===================================+========================+ -| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | +| country_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | +-----------------+------------------+-----------------------------------+------------------------+ | label | Label | String | ``getLabel()`` | +-----------------+------------------+-----------------------------------+------------------------+ -| source\_ips | Source IP list | Array | ``getSourceIps()`` | +| source_ips | Source IP list | Array | ``getSourceIps()`` | +-----------------+------------------+-----------------------------------+------------------------+  List all zones --------------- -.. code:: php +.. code-block:: php $zones = $service->getMonitoringZones(); -Please consult the `iterator doc `__ for -more information about iterators. Perform a traceroute -------------------- -.. code:: php - - $traceroute = $zone->traceroute(array( - 'target' => 'http://test.com', - 'target_resolver' => 'IPv4' - )); +.. code-block:: php - // How many hops? - echo count($traceroute); + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); - // What was the first hop's IP? - echo $traceroute[0]->ip; + // How many hops? + echo count($traceroute); + // What was the first hop's IP? + echo $traceroute[0]->ip; From ddf4e5a05fdcef053e8a19e08a01062002b21002 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:41 +0100 Subject: [PATCH 583/835] Networking docs --- doc/services/networking/README.md.rst | 108 ----- doc/services/networking/USERGUIDE.md.rst | 582 ----------------------- doc/services/networking/index.rst | 50 ++ doc/services/networking/networks.rst | 128 +++++ doc/services/networking/ports.rst | 150 ++++++ doc/services/networking/subnets.rst | 152 ++++++ 6 files changed, 480 insertions(+), 690 deletions(-) delete mode 100644 doc/services/networking/README.md.rst delete mode 100644 doc/services/networking/USERGUIDE.md.rst create mode 100644 doc/services/networking/networks.rst create mode 100644 doc/services/networking/ports.rst create mode 100644 doc/services/networking/subnets.rst diff --git a/doc/services/networking/README.md.rst b/doc/services/networking/README.md.rst deleted file mode 100644 index 605805fe1..000000000 --- a/doc/services/networking/README.md.rst +++ /dev/null @@ -1,108 +0,0 @@ -Networking -========== - -**Networking** is a service that you can use to create virtual networks -and attach cloud devices such as servers to these networks. - -Concepts --------- - -Concepts --------- - -To use the Networking service effectively, you should understand the -following key concepts: - -- **Network**: A network is an isolated virtual layer-2 broadcast - domain that is typically reserved for the tenant who created it - unless you configure the network to be shared. The network is the - main entity in the Networking service. Ports and subnets are always - associated with a network. - -- **Subnet**: A subnet represents an IP address block that can be used - to assign IP addresses to virtual instances (such as servers created - using the Compute service). Each subnet must have a CIDR and must be - associated with a network. - -- **Port**: A port represents a virtual switch port on a logical - network switch. Virtual instances (such as servers created using the - Compute service) attach their interfaces into ports. The port also - defines the MAC address and the IP address(es) to be assigned to the - interfaces plugged into them. When IP addresses are associated to a - port, this also implies the port is associated with a subet, as the - IP address is taken from the allocation pool for a specific subnet. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the Networking service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Networking service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All Networking operations are done via an *networking service object*. -To instantiate this object, call the ``networkingService`` method on the -``$client`` object. This method takes two arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+=====================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ - -.. code:: php - - $region = ''; - $networkingService = $client->networkingService(null, $region); - -Any networks, subnets, and ports created with this -``$networkingService`` instance will be stored in the cloud region -specified by ``$region``. - -3. Create a network. -~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -Once you have created a network, there is more you can do with it. See -`complete user guide for networking `__. diff --git a/doc/services/networking/USERGUIDE.md.rst b/doc/services/networking/USERGUIDE.md.rst deleted file mode 100644 index bfeb27774..000000000 --- a/doc/services/networking/USERGUIDE.md.rst +++ /dev/null @@ -1,582 +0,0 @@ -Complete User Guide for the Networking Service -============================================== - -Networking is a service that you can use to create virtual networks and -attach cloud devices such as servers to these networks. - -This user guide introduces you the entities in the Networking service — -networks, subnets, and ports — and shows you how to create and manage -these entities. - -Table of contents ------------------ - -- `Concepts <#concepts>`__ -- `Prerequisites <#prerequisites>`__ - - - `Client <#client>`__ - - `Networking service <#networking-service>`__ - -- `Networks <#networks>`__ - - - `Create a network <#create-a-network>`__ - - `Create multiple networks <#create-multiple-networks>`__ - - `List networks <#list-networks>`__ - - `Get a network <#get-a-network>`__ - - `Update a network <#update-a-network>`__ - - `Delete a network <#delete-a-network>`__ - -- `Subnets <#subnets>`__ - - - `Create a subnet <#create-a-subnet>`__ - - `Create multiple subnets <#create-multiple-subnets>`__ - - `List subnets <#list-subnets>`__ - - `Get a subnet <#get-a-subnet>`__ - - `Update a subnet <#update-a-subnet>`__ - - `Delete a subnet <#delete-a-subnet>`__ - -- `Ports <#ports>`__ - - - `Create a port <#create-a-port>`__ - - `Create multiple ports <#create-multiple-ports>`__ - - `List ports <#list-ports>`__ - - `Get a port <#get-a-port>`__ - - `Update a port <#update-a-port>`__ - - `Delete a port <#delete-a-port>`__ - -Concepts --------- - -To use the Networking service effectively, you should understand the -following key concepts: - -- **Network**: An isolated virtual layer-2 broadcast domain that is - typically reserved for the tenant who created it unless it is - configured to be shared. The network is the main entity in the - Networking service. Ports and subnets are always associated with a - network. - -- **Subnet**: An IP address block that can be used to assign IP - addresses to virtual instances (such as servers created using the - Compute service). Each subnet must have a CIDR and must be associated - with a network. - -- **Port**: A virtual switch port on a logical network switch. Virtual - instances (such as servers created using the Compute service) attach - their interfaces into ports. The port also defines the MAC address - and the IP address or addresses to be assigned to the interfaces - plugged into them. When IP addresses are associated with a port, this - also implies the port is associated with a subnet because the IP - address is taken from the allocation pool for a specific subnet. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the Networking service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate an - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Networking service -~~~~~~~~~~~~~~~~~~ - -All Networking operations are done via a *networking service object*. To -instantiate this object, call the ``networkingService`` method on the -``$client`` object. This method takes the following arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+=====================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ - -.. code:: php - - $region = ''; - $networkingService = $client->networkingService(null, $region); - -Any networks, subnets, and ports created with this -``$networkingService`` instance are stored in the cloud region specified -by ``$region``. - -Networks --------- - -A network is an isolated virtual layer-2 broadcast domain that is -typically reserved for the tenant who created it unless it is configured -to be shared. The network is the main entity in the Networking service. -Ports and subnets are always associated with a network. - -Create a network -~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+====================+===================================================================================================+=============+=============+=======================================+==================================+ -| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ - -You can create a network as shown in the following example: - -.. code:: php - - $network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' - )); - /** @var $network OpenCloud\Networking\Resource\Network **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple networks -~~~~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-network>`__. - -You can create multiple networks as shown in the following example: - -.. code:: php - - $networks = $networkingService->createNetworks(array( - array( - 'name' => 'My private backend network #1' - ), - array( - 'name' => 'My private backend network #2' - ) - )); - - foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List networks -~~~~~~~~~~~~~ - -You can list all the networks to which you have access as shown in the -following example: - -.. code:: php - - $networks = $networkingService->listNetworks(); - foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a network -~~~~~~~~~~~~~ - -You can retrieve a specific network by using that network's ID, as shown -in the following example: - -.. code:: php - - $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); - /** @var $network OpenCloud\Networking\Resource\Network **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a network -~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+====================+===================================================================================================+=============+=============+=================+==========================================+ -| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ - -You can update a network as shown in the following example: - -.. code:: php - - $network->update(array( - 'name' => 'My updated private backend network' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a network -~~~~~~~~~~~~~~~~ - -You can delete a network as shown in the following example: - -.. code:: php - - $network->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Subnets -------- - -A subnet represents an IP address block that can be used to assign IP -addresses to virtual instances (such as servers created using the -Compute service). Each subnet must have a CIDR and must be associated -with a network. - -Create a subnet -~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ -| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ - -You can create a subnet as shown in the following example: - -.. code:: php - - $subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25' - )); - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple subnets -~~~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-subnet>`__. - -You can create multiple subnets as shown in the following example: - -.. code:: php - - $subnets = $networkingService->createSubnets(array( - array( - 'name' => 'My subnet #1' - ), - array( - 'name' => 'My subnet #2' - ) - )); - - foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List subnets -~~~~~~~~~~~~ - -You can list all the subnets to which you have access as shown in the -following example: - -.. code:: php - - $subnets = $networkingService->listSubnets(); - foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a subnet -~~~~~~~~~~~~ - -You can retrieve a specific subnet by using that subnet's ID, as shown -in the following example: - -.. code:: php - - $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a subnet -~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ -| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ - -You can update a subnet as shown in the following example: - -.. code:: php - - $subnet->update(array( - 'name' => 'My updated subnet', - 'hostRoutes' => array( - array( - 'destination' => '1.1.1.0/24', - 'nexthop' => '192.168.17.19' - ) - ), - 'gatewayIp' => '192.168.62.155' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a subnet -~~~~~~~~~~~~~~~ - -You can delete a subnet as shown in the following example: - -.. code:: php - - $subnet->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Ports ------ - -A port represents a virtual switch port on a logical network switch. -Virtual instances (such as servers created using the Compute service) -attach their interfaces into ports. The port also defines the MAC -address and the IP address or addresses to be assigned to the interfaces -plugged into them. When IP addresses are associated with a port, this -also implies the port is associated with a subnet because the IP address -is taken from the allocation pool for a specific subnet. - -Create a port -~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ -| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ - -You can create a port as shown in the following example: - -.. code:: php - - $port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - )); - /** @var $port OpenCloud\Networking\Resource\Port **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple ports -~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-port>`__. - -You can create multiple ports as shown in the following example: - -.. code:: php - - $ports = $networkingService->createPorts(array( - array( - 'name' => 'My port #1', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ), - array( - 'name' => 'My port #2', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ) - )); - - foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List ports -~~~~~~~~~~ - -You can list all the ports to which you have access as shown in the -following example: - -.. code:: php - - $ports = $networkingService->listPorts(); - foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a port -~~~~~~~~~~ - -You can retrieve a specific port by using that port's ID, as shown in -the following example: - -.. code:: php - - $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); - /** @var $port OpenCloud\Networking\Resource\Port **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a port -~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ -| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ - -You can update a port as shown in the following example: - -.. code:: php - - $port->update(array( - 'fixedIps' => array( - array( - 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', - 'ipAddress' => '192.168.199.59' - ) - ) - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a port -~~~~~~~~~~~~~ - -You can delete a port as shown in the following example: - -.. code:: php - - $port->delete(); - -[ `Get the executable PHP script for this -example `__ ] diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index e69de29bb..92270fe86 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -0,0 +1,50 @@ +Networking v2 +============= + +Setup +----- + +.. include:: rs-client.sample.rst + +Now, set up the Cloud Monitoring service: + +.. code-block:: php + + $service = $client->networkingService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + networks + subnets + ports + +Glossary +-------- + +.. glossary:: + + network + A network is an isolated virtual layer-2 broadcast domain that is typically + reserved for the tenant who created it unless you configure the network to + be shared. The network is the main entity in the Networking service. Ports + and subnets are always associated with a network. + + subnet + A subnet represents an IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the Compute + service). Each subnet must have a CIDR and must be associated with a network. + + port + A port represents a virtual switch port on a logical network switch. + Virtual instances (such as servers created using the Compute service) + attach their interfaces into ports. The port also defines the MAC address + and the IP address(es) to be assigned to the interfaces plugged into them. + When IP addresses are associated to a port, this also implies the port is + associated with a subet, as the IP address is taken from the allocation + pool for a specific subnet. diff --git a/doc/services/networking/networks.rst b/doc/services/networking/networks.rst new file mode 100644 index 000000000..a290a34e7 --- /dev/null +++ b/doc/services/networking/networks.rst @@ -0,0 +1,128 @@ +Networks +======== + +Create a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +`Get the executable PHP script for this example `__ + + +Create multiple networks +------------------------ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code-block:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +`Get the executable PHP script for this example `_ + +List networks +------------- + +You can list all the networks to which you have access as shown in the +following example: + +.. code-block:: php + + $networks = $networkingService->listNetworks(); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + + +`Get the executable PHP script for this example `_ + + +Get a network +------------- + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->getNetwork('{networkId}'); + +`Get the executable PHP script for this example `_ + + +Update a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code-block:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +`Get the executable PHP script for this example `_ + + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code-block:: php + + $network->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst new file mode 100644 index 000000000..06d2ec2cf --- /dev/null +++ b/doc/services/networking/ports.rst @@ -0,0 +1,150 @@ +Ports +===== + +Create a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + + +`Get the executable PHP script for this example `_ + + +Create multiple ports +--------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +List ports +---------- + +You can list all the ports to which you have access as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->listPorts(); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +Get a port +---------- + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->getPort('{portId}'); + +`Get the executable PHP script for this example `_ + + +Update a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code-block:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +`Get the executable PHP script for this example `_ + + +Delete a port +------------- + +You can delete a port as shown in the following example: + +.. code-block:: php + + $port->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/subnets.rst b/doc/services/networking/subnets.rst new file mode 100644 index 000000000..e8d6951a0 --- /dev/null +++ b/doc/services/networking/subnets.rst @@ -0,0 +1,152 @@ +Subnets +======= + +Create a subnet +--------------- + +This operation takes one parameter, an associative array, with the following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + +`Get the executable PHP script for this example `_ + + +Create multiple subnets +----------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code-block:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +List subnets +------------ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code-block:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +Get a subnet +------------ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->getSubnet('{subnetId}'); + +`Get the executable PHP script for this example `_ + + +Update a subnet +--------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code-block:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +`Get the executable PHP script for this example `_ + + +Delete a subnet +--------------- + +You can delete a subnet as shown in the following example: + +.. code-block:: php + + $subnet->delete(); + +`Get the executable PHP script for this example `_ From 27f2d1755739835f664ecbaaba7f5671fd1317b8 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:50 +0100 Subject: [PATCH 584/835] Object Store docs --- doc/services/object-store/Account.md.rst | 33 - .../object-store/Container.md.cdn.rst | 90 -- doc/services/object-store/Object.md.cdn.rst | 25 - .../object-store/Object.md.storage.rst | 330 ------- doc/services/object-store/README.md.rst | 86 -- doc/services/object-store/USERGUIDE.md.rst | 900 ------------------ .../{Access.md.rst => access.rst} | 69 +- doc/services/object-store/account.rst | 46 + doc/services/object-store/cdn.rst | 124 +++ ...ontainer.md.storage.rst => containers.rst} | 193 ++-- doc/services/object-store/index.rst | 57 ++ ...d.storage.rst => migrating-containers.rst} | 64 +- doc/services/object-store/objects.rst | 427 +++++++++ doc/services/object-store/rs-only.rst | 3 + 14 files changed, 834 insertions(+), 1613 deletions(-) delete mode 100644 doc/services/object-store/Account.md.rst delete mode 100644 doc/services/object-store/Container.md.cdn.rst delete mode 100644 doc/services/object-store/Object.md.cdn.rst delete mode 100644 doc/services/object-store/Object.md.storage.rst delete mode 100644 doc/services/object-store/README.md.rst delete mode 100644 doc/services/object-store/USERGUIDE.md.rst rename doc/services/object-store/{Access.md.rst => access.rst} (52%) create mode 100644 doc/services/object-store/account.rst create mode 100644 doc/services/object-store/cdn.rst rename doc/services/object-store/{Container.md.storage.rst => containers.rst} (53%) rename doc/services/object-store/{Migrating.md.storage.rst => migrating-containers.rst} (82%) create mode 100644 doc/services/object-store/objects.rst create mode 100644 doc/services/object-store/rs-only.rst diff --git a/doc/services/object-store/Account.md.rst b/doc/services/object-store/Account.md.rst deleted file mode 100644 index a2d380b2a..000000000 --- a/doc/services/object-store/Account.md.rst +++ /dev/null @@ -1,33 +0,0 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles'); - -View Account Details --------------------- - -To see how many containers you have in your account -(X-Account-Container-Count), how many objects are in your account -(X-Account-Object-Count), and how many total bytes your account uses -(X-Account-Bytes-Used): - -.. code:: php - - $account = $service->getAccount(); - - // Either return the full Metadata object - $details = $account->getDetails(); - - // or individual values - $account->getContainerCount(); - $account->getObjectCount(); - $account->getBytesUsed(); - diff --git a/doc/services/object-store/Container.md.cdn.rst b/doc/services/object-store/Container.md.cdn.rst deleted file mode 100644 index 2f1c8ea68..000000000 --- a/doc/services/object-store/Container.md.cdn.rst +++ /dev/null @@ -1,90 +0,0 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles'); - -To access the CDN functionality of a particular container: - -.. code:: php - - $container = $service->getContainer('foo_bar'); - - $cdn = $container->getCdn(); - -List CDN-enabled container --------------------------- - -To list CDN-only containers, follow the same operation for Storage which -lists all containers. The only difference is which service object you -execute the method on: - -.. code:: php - - $cdnService = $service->getCdnService(); - $cdnContainers = $cdnService->listContainers(); - - foreach ($cdnContainers as $cdnContainer) { - - } - -CDN-enable and -disable a container ------------------------------------ - -Before a container can be CDN-enabled, it must exist in the storage -system. When a container is CDN-enabled, any objects stored in it are -publicly accessible over the Content Delivery Network by combining the -container's CDN URL with the object name. - -Any CDN-accessed objects are cached in the CDN for the specified amount -of time called the TTL. The default TTL value is 259200 seconds, or 72 -hours. Each time the object is accessed after the TTL expires, the CDN -refetches and caches the object for the TTL period. - -.. code:: php - - $container->enableCdn(); - $container->disableCdn(); - -Serving containers through SSL ------------------------------- - -.. code:: php - - $cdn->getCdnSslUri(); - -Streaming CDN-enabled containers --------------------------------- - -.. code:: php - - $cdn->getCdnStreamingUri(); - -iOS streaming -------------- - -The Cloud Files CDN allows you to stream video to iOS devices without -needing to convert your video. Once you CDN-enable your container, you -have the tools necessary for streaming media to multiple devices. - -.. code:: php - - $cdn->getIosStreamingUri(); - -CDN logging ------------ - -To enable and disable logging for your CDN: - -.. code:: php - - $cdn->enableCdnLogging(); - $cdn->disableCdnLogging(); - diff --git a/doc/services/object-store/Object.md.cdn.rst b/doc/services/object-store/Object.md.cdn.rst deleted file mode 100644 index 64ac143b3..000000000 --- a/doc/services/object-store/Object.md.cdn.rst +++ /dev/null @@ -1,25 +0,0 @@ -Setup ------ - -You will need to instantiate the container object and access its CDN -functionality as `documented -here `__. - -Purge CDN-enabled objects -------------------------- - -To remove a CDN object from public access: - -.. code:: php - - $object->purge(); - -You can also provide an optional e-mail address (or comma-delimeted list -of e-mails), which the API will send a confirmation message to once the -object has been completely purged: - -.. code:: php - - $object->purge('jamie.hannaford@rackspace.com'); - $object->purge('hello@example.com,hallo@example.com'); - diff --git a/doc/services/object-store/Object.md.storage.rst b/doc/services/object-store/Object.md.storage.rst deleted file mode 100644 index 16dab18f4..000000000 --- a/doc/services/object-store/Object.md.storage.rst +++ /dev/null @@ -1,330 +0,0 @@ -Setup ------ - -Conceptually, a container contains objects (also known as files). In -order to work with objects, you will need to instantiate a container -object first as `documented -here `__. - -Note on object properties -------------------------- - -Please be aware that you cannot directly access the properties of -DataObject anymore, you **must** use appropriate getter/ setter methods: - -+----------------------+------------------------+ -| Property | Method | -+======================+========================+ -| Parent container | ``getContainer`` | -+----------------------+------------------------+ -| Name | ``getName`` | -+----------------------+------------------------+ -| Body of file | ``getContent`` | -+----------------------+------------------------+ -| Size of file | ``getContentLength`` | -+----------------------+------------------------+ -| Type of file | ``getContentType`` | -+----------------------+------------------------+ -| ETag checksum | ``getEtag`` | -+----------------------+------------------------+ -| Last modified date | ``getLastModified`` | -+----------------------+------------------------+ - -Create an object ----------------- - -There are three ways to upload a new file, each of which has different -business needs. - - **Note:** Unlike previous versions, you do not need to manually - specify your object's content type. The API will do this for you. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -To upload a single/basic file: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - use OpenCloud\ObjectStore\Resource\DataObject; - - $data = fopen('/path/to/sample.mp3', 'r+'); - - // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) - - $meta = array( - 'Author' => 'Camera Obscura', - 'Origin' => 'Glasgow' - ); - - $metaHeaders = DataObject::stockHeaders($meta); - $customHeaders = array(); - $allHeaders = $metaHeaders + $customHeaders; - - $container->uploadObject('sample.mp3', $data, $allHeaders); - -To upload multiple small-to-mid sized files: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $files = array( - array( - 'name' => 'apache.log', - 'path' => '/etc/httpd/logs/error_log' - ), - array( - 'name' => 'mysql.log', - 'body' => fopen('/tmp/mysql.log', 'r+') - ), - array( - 'name' => 'to_do_list.txt', - 'body' => 'PHONE HOME' - ) - ); - - $container->uploadObjects($files); - -As you can see, the ``name`` key is required for every file. You must -also specify *either* a path key (to an existing file), or a ``body``. -The ``body`` can either be a PHP resource or a string representation of -the content you want to upload. - -To upload large files -~~~~~~~~~~~~~~~~~~~~~ - -For files over 5GB, you will need to use the -``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your -transfer, upon which you can execute your upload functionality. For your -convenience, the Container resource object contains a simple method to -do this heavy lifting for you: - -.. code:: php - - $transfer = $container->setupObjectTransfer(array( - 'name' => 'video.mov', - 'path' => '/home/jamie/video.mov', - 'metadata' => array( - 'Author' => 'Jamie' - ), - 'concurrency' => 4, - 'partSize' => 1.5 * Size::GB - )); - - $transfer->upload(); - -You can specify how many concurrent cURL connections are used to upload -parts of your file. The file is fragmented into chunks, each of which is -uploaded individually as a separate file (the filename of each part will -indicate that it's a segment rather than the full file). After all parts -are uploaded, a manifest is uploaded. When the end-user accesses the 5GB -by its true filename, it actually references the manifest file which -concatenates each segment into a streaming download. - -List objects in a container ---------------------------- - -To return a list of objects: - -.. code:: php - - $files = $container->objectList(); - - foreach ($files as $file) { - // ... do something - } - -By default, 10,000 objects are returned as a maximum. To get around -this, you can construct a query which refines your result set. For a -full specification of query parameters relating to collection filtering, -see the `official -docs `__. - -.. code:: php - - $container->objectList(array('prefix' => 'logFile_')); - -Get object ----------- - -To retrieve a specific file from Cloud Files: - -.. code:: php - - $file = $container->getObject('summer_vacation.mp4'); - -Conditional requests -~~~~~~~~~~~~~~~~~~~~ - -You can also perform conditional requests according to `RFC 2616 -specification `__ (§§ 14.24-26). -Supported headers are ``If-Match``, ``If-None-Match``, -``If-Modified-Since`` and ``If-Unmodified-Since``. - -So, to retrieve a file's contents only if it's been recently changed - -.. code:: php - - $file = $container->getObject('error_log.txt', array( - 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' - )); - - if ($file->getContentLength()) { - echo 'Has been changed since the above date'; - } else { - echo 'Has not been changed'; - } - -Retrieve a file only if it has NOT been modified (and expect a 412 on -failure): - -:: - - use Guzzle\Http\Exception\ClientErrorResponseException; - - try { - $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( - 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' - )); - } catch (ClientErrorResponseException $e) { - echo 'This file has been modified...'; - } - -Finally, you can specify a range - which will return a subset of bytes -from the file specified. To return the last 20B of a file: - -.. code:: php - - $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); - -Update an existing object -------------------------- - -Updating content is easy: - -.. code:: php - - $file->setContent(fopen('/path/to/new/content', 'r+')); - $file->update(); - -Bear in mind that updating a file name will result in a new file being -generated (under the new name). You will need to delete the old file. - -Copy object ------------ - -To copy a file to another location, you need to specify a string-based -destination path: - -.. code:: php - - $object->copy('/container_2/new_object_name'); - -Delete object -------------- - -.. code:: php - - $object->delete(); - -Get object metadata -------------------- - -You can fetch just the object metadata without fetching the full -content: - -.. code:: php - - $container->getPartialObject('summer_vacation.mp4'); - -In order to access the metadata on a partial or complete object, use: - -.. code:: php - - $object->getMetadata(); - -You can turn a partial object into a full object to get the content -after looking at the metadata: - -.. code:: php - - $object->refresh(); - -You can also update to get the latest metadata: - -.. code:: php - - $object->retrieveMetadata(); - -Update object metadata ----------------------- - -Similarly, with setting metadata there are two options: you can update -the metadata values of the local object (i.e. no HTTP request) if you -anticipate you'll be executing one soon (an update operation for -example): - -.. code:: php - - // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation - $object->setMetadata(array( - 'Author' => 'Hemingway' - )); - - // ... code here - - $object->update(); - -Alternatively, you can update the API straight away - so that everything -is retained: - -.. code:: php - - $object->saveMetadata(array( - 'Author' => 'Hemingway' - )); - -Please be aware that these methods override and wipe existing values. If -you want to append values to your metadata, use the correct method: - -.. code:: php - - $metadata = $object->appendToMetadata(array( - 'Author' => 'Hemingway' - )); - - $object->saveMetadata($metadata); - -Extract archive ---------------- - -CloudFiles provides you the ability to extract uploaded archives to -particular destinations. The archive will be extracted and its contents -will populate the particular area specified. To upload file (which might -represent a directory structure) into a particular container: - -.. code:: php - - use OpenCloud\ObjectStore\Constants\UrlType; - - $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); - -You can also omit the container name (i.e. provide an empty string as -the first argument). If you do this, the API will create the containers -necessary to house the extracted files - this is done based on the -filenames inside the archive. - -Bulk delete ------------ - -Bulk delete a set of paths: - -.. code:: php - - $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - - $service->bulkDelete($pathsToBeDeleted); - diff --git a/doc/services/object-store/README.md.rst b/doc/services/object-store/README.md.rst deleted file mode 100644 index 01cf26733..000000000 --- a/doc/services/object-store/README.md.rst +++ /dev/null @@ -1,86 +0,0 @@ -Object Store -============ - -**Object Store** is an object-based storage system that stores content -and metadata as objects in a cloud. - -Specifically, a cloud is made up of one or more regions. Each region can -have several **containers**, created by a user. Each container can -container several **objects** (sometimes referred to as files), uploaded -by the user. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Choose one of the following two options: - -- If you are working with a vanilla OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as shown below. - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as shown below. - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Object Store service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $region = 'DFW'; - $objectStoreService = $client->objectStoreService(null, $region); - -In the example above, you are connecting to the ``DFW`` region of the -cloud. Any containers and objects created with this -``$objectStoreService`` instance will be stored in that cloud region. - -3. Create a container for your objects (also referred to as files). -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $container = $objectStoreService->createContainer('logos'); - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -4. Upload an object to the container. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -There is a lot more you can do with containers and objects. See the -`complete user guide to the Object Store service `__. diff --git a/doc/services/object-store/USERGUIDE.md.rst b/doc/services/object-store/USERGUIDE.md.rst deleted file mode 100644 index d9732ea36..000000000 --- a/doc/services/object-store/USERGUIDE.md.rst +++ /dev/null @@ -1,900 +0,0 @@ -The Complete User Guide to the Object Store Service -=================================================== - -**Object Store** is an object-based storage system that stores content -and metadata as objects in a cloud. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the object store service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with a vanilla OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as shown below. - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as shown below. - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Object Store Service -~~~~~~~~~~~~~~~~~~~~ - -All operations on the object store are done via an object store service -object. - -.. code:: php - - $region = 'DFW'; - $objectStoreService = $client->objectStoreService(null, $region); - -In the example above, you are connecting to the ``DFW`` region of the -cloud. Any containers and objects created with this -``$objectStoreService`` instance will be stored in that cloud region. - -Containers ----------- - -A **container** defines a namespace for **objects**. An object with the -same name in two different containers represents two different objects. - -For example, you may create a container called ``logos`` to hold all the -image files for your blog. - -A container may contain zero or more objects in it. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -Create Container -~~~~~~~~~~~~~~~~ - -.. code:: php - - $container = $objectStoreService->createContainer('logos'); - -[ `Get the executable PHP script for this -example `__ ] - -Get Container Details -~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a single container's details by using its name. An -instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. - -.. code:: php - - $container = $objectStoreService->getContainer('logos'); - - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - -[ `Get the executable PHP script for this -example `__ ] - -List Containers -~~~~~~~~~~~~~~~ - -You can retrieve a list of all your containers. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $containers = $objectStoreService->listContainers(); - foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Set or Update Container Metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can set metadata on a container. - -.. code:: php - - $container->saveMetadata(array( - 'author' => 'John Doe' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Get Container Metadata -~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve the metadata for a container. - -.. code:: php - - $containerMetadata = $container->getMetadata(); - -[ `Get the executable PHP script for this -example `__ ] - -Delete Container -~~~~~~~~~~~~~~~~ - -When you no longer have a need for the container, you can remove it. - -If the container is empty (that is, it has no objects in it), you can -remove it as shown below: - -.. code:: php - - $container->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -If the container is not empty (that is, it has objects in it), you have -two choices in how to remove it: - -- `Individually remove each object <#delete-object>`__ in the - container, then remove the container itself as shown above, or - -- Remove the container and all the objects within it as shown below: - - .. code:: php - - $container->delete(true); - - [ `Get the executable PHP script for this - example `__ ] - -Get Object Count -~~~~~~~~~~~~~~~~ - -You can quickly find out how many objects are in a container. - -.. code:: php - - $containerObjectCount = $container->getObjectCount(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$containerObjectCount`` will contain the number -of objects in the container represented by ``$container``. - -Get Bytes Used -~~~~~~~~~~~~~~ - -You can quickly find out the space used by a container, in bytes. - -.. code:: php - - $containerSizeInBytes = $container->getBytesUsed(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$containerSizeInBytes`` will contain the space -used, in bytes, by the container represented by ``$container``. - -Container Quotas -~~~~~~~~~~~~~~~~ - -Set Quota for Number of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can set a quota for the maximum number of objects that may be stored -in a container. - -.. code:: php - - $maximumNumberOfObjectsAllowedInContainer = 25; - $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); - -[ `Get the executable PHP script for this -example `__ ] - -Set Quota for Total Size of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can set a quota for the maximum total space (in bytes) used by -objects in a container. - -.. code:: php - - use OpenCloud\Common\Constants\Size; - - $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; - $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); - -[ `Get the executable PHP script for this -example `__ ] - -Get Quota for Number of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can retrieve the quota for the maximum number of objects that may be -stored in a container. - -.. code:: php - - $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); - -[ `Get the executable PHP script for this -example `__ ] - -Get Quota for Total Size of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can retrieve the quota for the maximum total space (in bytes) used -by objects in a container. - -.. code:: php - - $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); - -[ `Get the executable PHP script for this -example `__ ] - -Objects -------- - -An **object** (sometimes referred to as a file) is the unit of storage -in an Object Store. An object is a combination of content (data) and -metadata. - -For example, you may upload an object named ``php-elephant.jpg``, a JPEG -image file, to the ``logos`` container. Further, you may assign metadata -to this object to indicate that the author of this object was someone -named Jane Doe. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -Upload Object -~~~~~~~~~~~~~ - -Once you have created a container, you can upload objects to it. - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, an image file from the local filesystem -(``path/to/local/php-elephant.jpg``) is uploaded to a container in the -Object Store. - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -It is also possible to upload an object and associate metadata with it. - -.. code:: php - - use OpenCloud\ObjectStore\Resource\DataObject; - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - $metadata = array('author' => 'Jane Doe'); - - $customHeaders = array(); - $metadataHeaders = DataObject::stockHeaders($metadata); - $allHeaders = $customHeaders + $metadataHeaders; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData, $allHeaders); - -[ `Get the executable PHP script for this -example `__ ] - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -Pseudo-hierarchical Folders -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Although you cannot nest directories in an Object Store, you can -simulate a hierarchical structure within a single container by adding -forward slash characters (``/``) in the object name. - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'languages/php/elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, an image file from the local filesystem -(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the -Object Store. Within that container, the filename is -``languages/php/elephant.jpg``, where ``languages/php/`` is a -pseudo-hierarchical folder hierarchy. - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -Upload Multiple Objects -^^^^^^^^^^^^^^^^^^^^^^^ - -You can upload more than one object at a time to a container. - -.. code:: php - - $objects = array( - array( - 'name' => 'php-elephant.jpg', - 'path' => '/path/to/local/php-elephant.jpg' - ), - array( - 'name' => 'python-snake.jpg', - 'path' => '/path/to/local/python-snake.jpg' - ), - a - ); - - $container->uploadObjects($objects); - -[ `Get the executable PHP script for this -example `__ ] - -In the above example, the contents of two files present on the local -filesystem are uploaded as objects to the container referenced by -``$container``. - -Instead of specifying the ``path`` key in an element of the ``$objects`` -array, you can specify a ``body`` key whose value is a string or a -stream representation. - -Finally, you can pass headers as the second parameter to the -``uploadObjects`` method. These headers will be applied to every object -that is uploaded. - -:: - - $metadata = array('author' => 'Jane Doe'); - - $customHeaders = array(); - $metadataHeaders = DataObject::stockHeaders($metadata); - $allHeaders = $customHeaders + $metadataHeaders; - - $container->uploadObjects($objects, $allHeaders); - -[ `Get the executable PHP script for this -example `__ -] - -In the example above, every object referenced within the ``$objects`` -array will be uploaded with the same metadata. - -Large Objects -~~~~~~~~~~~~~ - -If you want to upload objects larger than 5GB in size, you must use a -different upload process. - -.. code:: php - - $options = array( - 'name' => 'san_diego_vacation_video.mp4', - 'path' => '/path/to/local/videos/san_diego_vacation.mp4' - ); - $objectTransfer = $container->setupObjectTransfer($options); - $objectTransfer->upload(); - -[ `Get the executable PHP script for this -example `__ ] - -The process shown above will automatically partition your large object -into small chunks and upload them concurrently to the container -represented by ``$container``. - -You can tune the parameters of this process by specifying additional -options in the ``$options`` array. Here is a complete listing of keys -that can be specified in the ``$options`` array: - -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| Key name | Description | Data Type | Required? | Default Value | Example | -+===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ -| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ - -Auto-extract Archive Files -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can upload a tar archive file and have the Object Store service -automatically extract it into a container. - -.. code:: php - - use OpenCloud\ObjectStore\Constants\UrlType; - - $localArchiveFileName = '/path/to/local/image_files.tar.gz'; - $remotePath = 'images/'; - - $fileData = fopen($localArchiveFileName, 'r'); - $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); - -[ `Get the executable PHP script for this -example `__ ] - -In the above example, a local archive file named ``image_files.tar.gz`` -is uploaded to an Object Store container named ``images`` (defined by -the ``$remotePath`` variable). - -Note that while we call ``fopen`` to open a file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``bulkExtract`` call. - -The third parameter to ``bulkExtract`` is the type of the archive file -being uploaded. The acceptable values for this are: - -- ``UrlType::TAR`` for tar archive files, *or*, -- ``UrlType:TAR_GZ`` for tar archive files that are compressed with - gzip, *or* -- ``UrlType::TAR_BZ`` for tar archive file that are compressed with - bzip - -Note that the value of ``$remotePath`` could have been a -(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as -``images/blog`` as well. - -List Objects in a Container -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all the objects stored in a container. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $objects = $container->objectList(); - foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -You can list only those objects in the container whose names start with -a certain prefix. - -.. code:: php - - $options = array( - 'prefix' => 'php' - ); - - $objects = $container->objectList($options); - -[ `Get the executable PHP script for this -example `__ ] - -In general, the ``objectList()`` method described above takes an -optional parameter (``$options`` in the example above). This parameter -is an associative array of various options. Here is a complete listing -of keys that can be specified in the ``$options`` array: - -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| Key name | Description | Data Type | Required? | Default Value | Example | -+==================+=============================================================================+=============+=============+=================+=========================+ -| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ - -Retrieve Object -~~~~~~~~~~~~~~~ - -You can retrieve an object and its metadata, given the object's -container and name. - -.. code:: php - - $objectName = 'php-elephant.jpg'; - $object = $container->getObject($objectName); - - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - - $objectContent = $object->getContent(); - - /** @var $objectContent Guzzle\Http\EntityBody **/ - - // Write object content to file on local filesystem. - $objectContent->rewind(); - $stream = $objectContent->getStream(); - $localFilename = tempnam("/tmp", 'php-opencloud-'); - file_put_contents($localFilename, $stream); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$object`` is the object named -``php-elephant.jpg`` in the container represented by ``$container``. -Further, ``$objectContent`` represents the contents of the object. It is -of type -```Guzzle\Http\EntityBody`` `__. - -Retrieve Object Metadata -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve just an object's metadata without retrieving its -contents. - -.. code:: php - - $objectName = 'php-elephant.jpg'; - $object = $container->getPartialObject($objectName); - $objectMetadata = $object->getMetadata(); - - /** @var $objectMetadata \OpenCloud\Common\Metadata **/ - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, while ``$object`` is an instance of -``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only -partially populated. Specifically, only properties of the instance -relating to object metadata are populated. - -Temporary URLs -~~~~~~~~~~~~~~ - -The Temporary URL feature allows you to create limited-time Internet -addresses that allow you to grant limited access to your Object Store -account. Using this feature, you can allow others to retrieve or place -objects in your Object Store account for a specified amount of time. -Access to the temporary URL is independent of whether or not your -account is `CDN-enabled <#cdn-containers>`__. Even if you do not -CDN-enable a container, you can still grant temporary public access -through a temporary URL. - -First, you must set the temporary URL secret on your account. This is a -one-time operation; you only need to perform it the very first time you -wish to use the temporary URLs feature. - -.. code:: php - - $account->setTempUrlSecret(); - -[ `Get the executable PHP script for this -example `__ ] - -Note that this operation is carried out on ``$account``, which is an -instance of ``OpenCloud\ObjectStore\Resource\Account``, a class -representing `your object store account <#accounts>`__. - -The above operation will generate a random secret and set it on your -account. Instead of a random secret, if you wish to provide a secret, -you can supply it as a parameter to the ``setTempUrlSecret`` method. - -.. code:: php - - $account->setTempUrlSecret(''); - -[ `Get the executable PHP script for this -example `__ -] - -Once a temporary URL secret has been set on your account, you can -generate a temporary URL for any object in your Object Store. - -.. code:: php - - $expirationTimeInSeconds = 3600; // one hour from now - $httpMethodAllowed = 'GET'; - $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, a temporary URL for the object is generated. This -temporary URL will provide public access to the object for an hour (3600 -seconds), as specified by the ``$expirationTimeInSeconds`` variable. -Further, only GET HTTP methods will be allowed on this URL, as specified -by the ``$httpMethodAllowed`` variable. The other value allowed for the -``$httpMethodAllowed`` variable would be ``PUT``. - -You can also retrieve the temporary URL secret that has been set on your -account. - -.. code:: php - - $tempUrlSecret = $account->getTempUrlSecret(); - -[ `Get the executable PHP script for this -example `__ ] - -Update Object -~~~~~~~~~~~~~ - -You can update an object's contents (as opposed to `updating its -metadata <#update-object-metadata>`__) by simply re-\ `uploading the -object <#upload-object>`__ to its container using the same object name -as before. - -Update Object Metadata -~~~~~~~~~~~~~~~~~~~~~~ - -You can update an object's metadata after it has been uploaded to a -container. - -.. code:: php - - $object->saveMetadata(array( - 'author' => 'John Doe' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Copy Object -~~~~~~~~~~~ - -You can copy an object from one container to another, provided the -destination container already exists. - -.. code:: php - - $object->copy('logos_copy/php.jpg'); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, both the name of the destination container -(``logos_copy``)and the name of the destination object (``php.jpg``) -have to be specified, separated by a ``/``. - -Delete Object -~~~~~~~~~~~~~ - -When you no longer need an object, you can delete it. - -.. code:: php - - $object->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Bulk Delete -~~~~~~~~~~~ - -While you can delete individual objects as shown above, you can also -delete objects and empty containers in bulk. - -.. code:: php - - $objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' - )); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, two objects (``some_container/object_a.png``, -``some_other_container/object_z.png``) and one empty container -(``some_empty_container``) are all being deleted in bulk via a single -command. - -CDN Containers --------------- - -Note: The functionality described in this section is available only on -the Rackspace cloud. It will not work as described when working with a -vanilla OpenStack cloud. - -Any container can be converted to a CDN-enabled container. When this is -done, the objects within the container can be accessed from anywhere on -the Internet via a URL. - -Enable CDN Container -~~~~~~~~~~~~~~~~~~~~ - -To take advantage of CDN capabilities for a container and its objects, -you must CDN-enable that container. - -.. code:: php - - $container->enableCdn(); - -[ `Get the executable PHP script for this -example `__ ] - -Public URLs -~~~~~~~~~~~ - -Once you have CDN-enabled a container, you can retrieve a -publicly-accessible URL for any of its objects. There are four types of -publicly-accessible URLs for each object. Each type of URL is meant for -a different purpose. The sections below describe each of these URL types -and how to retrieve them. - -HTTP URL -^^^^^^^^ - -You can use this type of URL to access the object over HTTP. - -:: - - $httpUrl = $object->getPublicUrl(); - -[ `Get the executable PHP script for this -example `__ ] - -Secure HTTP URL -^^^^^^^^^^^^^^^ - -You can use this type of URL to access the object over HTTP + TLS/SSL. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $httpsUrl = $object->getPublicUrl(UrlType::SSL); - -[ `Get the executable PHP script for this -example `__ ] - -Streaming URL -^^^^^^^^^^^^^ - -You can use this type of URL to stream a video or audio object using -Adobe's HTTP Dynamic Streaming. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); - -[ `Get the executable PHP script for this -example `__ ] - -IOS Streaming URL -^^^^^^^^^^^^^^^^^ - -You can use this type of URL to stream an audio or video object to an -iOS device. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); - -[ `Get the executable PHP script for this -example `__ ] - -Update CDN Container TTL -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can update the TTL of a CDN-enabled container. - -.. code:: php - - $cdnContainer = $container->getCdn(); - $cdnContainer->setTtl(); - -[ `Get the executable PHP script for this -example `__ ] - -Disable CDN Container -~~~~~~~~~~~~~~~~~~~~~ - -If you no longer need CDN capabilities for a container, you can disable -them. - -.. code:: php - - $container->disableCdn(); - -[ `Get the executable PHP script for this -example `__ ] - -Accounts --------- - -An **account** defines a namespace for **containers**. An account can -have zero or more containers in it. - -Retrieve Account -~~~~~~~~~~~~~~~~ - -You must retrieve the account before performing any operations on it. - -.. code:: php - - $account = $objectStoreService->getAccount(); - -Get Container Count -~~~~~~~~~~~~~~~~~~~ - -You can quickly find out how many containers are in your account. - -.. code:: php - - $accountContainerCount = $account->getContainerCount(); - -[ `Get the executable PHP script for this -example `__ ] - -Get Object Count -~~~~~~~~~~~~~~~~ - -You can quickly find out how many objects are in your account. - -.. code:: php - - $accountObjectCount = $account->getObjectCount(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$accountObjectCount`` will contain the number of -objects in the account represented by ``$account``. - -Get Bytes Used -~~~~~~~~~~~~~~ - -You can quickly find out the space used by your account, in bytes. - -.. code:: php - - $accountSizeInBytes = $account->getBytesUsed(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$accountSizeInBytes`` will contain the space -used, in bytes, by the account represented by ``$account``. diff --git a/doc/services/object-store/Access.md.rst b/doc/services/object-store/access.rst similarity index 52% rename from doc/services/object-store/Access.md.rst rename to doc/services/object-store/access.rst index ee85a938e..62cb541ca 100644 --- a/doc/services/object-store/Access.md.rst +++ b/doc/services/object-store/access.rst @@ -1,31 +1,19 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want - Temporary URLs --------------- +============== Temporary URLs allow you to create time-limited Internet addresses that allow you to grant access to your Cloud Files account. Using Temporary URL, you may allow others to retrieve or place objects in your containers - regardless of whether they're CDN-enabled. + Set "temporary URL" metadata key -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------- You must set this "secret" value on your account, where it can be used in a global state: -.. code:: php +.. code-block:: php $account = $service->getAccount(); $account->setTempUrlSecret('my_secret'); @@ -35,41 +23,64 @@ in a global state: The string argument of ``setTempUrlSecret()`` is optional - if left out, the SDK will generate a random hashed secret for you. + Create a temporary URL -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- Once you've set an account secret, you can create a temporary URL for your object. To allow GET access to your object for 1 minute: -.. code:: php +.. code-block:: php $object->getTemporaryUrl(60, 'GET'); + To allow PUT access for 1 hour: -.. code:: php +.. code-block:: php $object->getTemporaryUrl(360, 'PUT'); -Hosting websites on CloudFiles ------------------------------- -To host a static (i.e. HTML) website on CloudFiles, you must follow +Hosting HTML sites on CDN +========================= + +.. include:: rs-only.rst + +To host a static (i.e. HTML) website on Cloud Files, you must follow these steps: -1. CDN-enable a container +1. CDN-enable a container: + +.. code-block:: php + + $container = $service->getContainer('html_site'); + $container->enableCdn(); + 2. Upload all HTML content. You can use nested directory structures. -3. Tell CloudFiles what to use for your default index page like this: -.. code:: php +.. code-block:: php + + $container->uploadObjects(array( + array('name' => 'index.html', 'path' => 'index.html'), + array('name' => 'contact.html', 'path' => 'contact.html'), + array('name' => 'error.html', 'path' => 'error.html'), + array('name' => 'styles.css', 'path' => 'styles.css'), + array('name' => 'main.js', 'path' => 'main.js'), + )); + +3. Tell Cloud Files what to use for your default index page like this: + +.. code-block:: php + + $container->setStaticIndexPage('index.html'); - $container->setStaticIndexPage('index.html'); +4. (Optional) Tell Cloud Files which error page to use by default: -4. (Optional) Tell CloudFiles which error page to use by default: +.. code-block:: php -.. code:: php + $container->setStaticErrorPage('error.html'); - $container->setStaticErrorPage('error.html'); Bear in mind that steps 3 & 4 do not upload content, but rather specify a reference to an existing page/CloudFiles object. diff --git a/doc/services/object-store/account.rst b/doc/services/object-store/account.rst new file mode 100644 index 000000000..fb51c8855 --- /dev/null +++ b/doc/services/object-store/account.rst @@ -0,0 +1,46 @@ +Account Details +=============== + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +Setup +----- + +.. code-block:: php + + $account = $service->getAccount(); + + +View all details +---------------- + +.. code-block:: php + + $details = $account->getDetails(); + + +Retrieve total container count +------------------------ + +.. code-block:: php + + $account->getContainerCount(); + + +Retrieve total object count +--------------------- + +.. code-block:: php + + $account->getObjectCount(); + + +Retrieve total bytes used +------------------------- + +.. code-block:: php + + $account->getBytesUsed(); diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst new file mode 100644 index 000000000..5ad72b6cd --- /dev/null +++ b/doc/services/object-store/cdn.rst @@ -0,0 +1,124 @@ +CDN Containers +============== + +.. include:: rs-only.rst + +Setup +----- + +In order to interact with CDN containers, you first need to instantiate a +CDN service object: + +.. code-block:: php + + $cdnService = $service->getCdnService(); + + +List CDN-enabled containers +--------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code-block:: php + + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ + } + + +CDN-enable a container +---------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code-block:: php + + $container->enableCdn(); + + +CDN-disable a container +----------------------- + +.. code-block:: php + + $container->disableCdn(); + + +Operations on CDN-enabled containers +------------------------------------ + +Once a container has been CDN-enabled, you can retrieve it like so: + +.. code-block:: php + + $cdnContainer = $cdnService->cdnContainer('{containerName}'); + + +Retrieve the SSL URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnSslUri(); + + +Retrieve the streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnStreamingUri(); + + +Retrieve the iOS streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code-block:: php + + $cdnContainer->getIosStreamingUri(); + + +CDN logging +~~~~~~~~~~~ + +To enable and disable logging for your CDN-enabled container: + +.. code-block:: php + + $cdnContainer->enableCdnLogging(); + $cdnContainer->disableCdnLogging(); + + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code-block:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code-block:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); diff --git a/doc/services/object-store/Container.md.storage.rst b/doc/services/object-store/containers.rst similarity index 53% rename from doc/services/object-store/Container.md.storage.rst rename to doc/services/object-store/containers.rst index 89798fdd0..4b9c66ac3 100644 --- a/doc/services/object-store/Container.md.storage.rst +++ b/doc/services/object-store/containers.rst @@ -1,26 +1,12 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - // Create a client object to communicate with various Rackspace Cloud services. - $client = new Rackspace(RACKSPACE_US, array( - 'username' => 'Replace this with your Rackspace Cloud user name', - 'apiKey' => 'Replace this with your Rackspace Cloud API key' - )); - - // Create a service object to use the object store service. The sample code - // creates the object store in the 'DFW' region. - $service = $client->objectStoreService('cloudFiles', 'DFW'); +Containers +========== Create container ---------------- To create a new container, you just need to define its name: -.. code:: php +.. code-block:: php $container = $service->createContainer('my_amazing_container'); @@ -30,133 +16,153 @@ likely due to the fact you have a naming collision. Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in List containers --------------- -Return a list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $containerList = $service->listContainers(); + $containers = $service->listContainers(); - while ($container = $containerList->next()) { - // Do stuff; some examples below - printf("Container name: %s\n", $container->name); - printf("Number of objects within container: %d\n", $container->getObjectCount()); - } + foreach ($containers as $container) { + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } Container names are sorted based on a binary comparison, a single built-in collating sequence that compares string data using SQLite's memcmp() function, regardless of text encoding. -The list is limited to 10,000 containers at a time. See 1.3 for ways to -limit and navigate this list. +The list is limited to 10,000 containers at a time. To work with larger +collections, please read the next section. -Return a formatted list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, the SDK only supports JSON-formatted responses. - -Controlling a large list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Filtering large collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You may limit and control this list of results by using the ``marker`` +When you need more control over collections of containers, you can filter the +results and return back a subset of the total collection by using the ``marker`` and ``end_marker`` parameters. The former parameter (``marker``) tells the API where to begin the list, and the latter (``end_marker``) tells it where to end the list. You may use either of them independently or -together. You may also use the ``limit`` parameter to fix the number of +together. + +You may also use the ``limit`` parameter to fix the number of containers returned. To list a set of containers between two fixed points: -.. code:: php +.. code-block:: php - $someContainers = $service->listContainers(array( - 'marker' => 'container_55', - 'end_marker' => 'container_2001' - )); + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); Or to return a limited set: -.. code:: php +.. code-block:: php + + $someContainers = $service->listContainers(array('limit' => 560)); - $someContainers = $service->listContainers(array('limit' => 560)); Get container ------------- -To retrieve a certain container, either to access its object or -metadata: +To retrieve a certain container: + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $service->getContainer('{containerName}'); + + +Retrieve a container's name +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php -.. code:: php + $name = $container->name; - $container = $service->getContainer('container_name'); - echo $container->getObjectCount(); - echo $container->getBytesUsed(); +Retrieve a container's object count +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $count = $container->getObjectCount(); + + +Retrieve a container's total bytes used +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $bytes = $container->getBytesUsed(); + Delete container ---------------- -Deleting a container is easy: +Deleting an empty container is easy: -.. code:: php +.. code-block:: php + + $container->delete(); - $container->delete(); Please bear mind that you must delete all objects inside a container before deleting it. This is done for you if you set the ``$deleteObjects`` parameter to ``TRUE`` like so: -.. code:: php +.. code-block:: php + + $container->delete(true); - $container->delete(TRUE); -You can also do it manually: +You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ +first, and then call ``delete``. -.. code:: php - $container->deleteAllObjects(); - $container->delete(); +Deleting all objects inside a container +--------------------------------------- + +.. code-block:: php + + $container->deleteAllObjects(); + Create or update container metadata ----------------------------------- -.. code:: php +.. code-block:: php - $container->saveMetadata(array( - 'Author' => 'Virginia Woolf', - 'Published' => '1931' - )); + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); Please bear in mind that this action will set metadata to this array - overriding existing values and wiping those left out. To *append* values to the current metadata: -.. code:: php - - $metadata = $container->appendToMetadata(array( - 'Publisher' => 'Hogarth' - )); +.. code-block:: php -If you only want to set the metadata to the local object, and not -immediately retain these values on the API, you can use a standard -setter method - which can contribute to eventual actions like an update: + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); -.. code:: php - - $container->setMetadata(array('Foo' => 'Bar')); Container quotas ---------------- -The container\_quotas middleware implements simple quotas that can be +The ``container_quotas`` middleware implements simple quotas that can be imposed on Cloud Files containers by a user. Setting container quotas can be useful for limiting the scope of containers that are delegated to non-admin users, exposed to formpost uploads, or just as a self-imposed @@ -164,19 +170,20 @@ sanity check. To set quotas for a container: -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Size; + use OpenCloud\Common\Constants\Size; - $container->setCountQuota(1000); - $container->setBytesQuota(2.5 * Size::GB); + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); And to retrieve them: -.. code:: php +.. code-block:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); - echo $container->getCountQuota(); - echo $container->getBytesQuota(); Access log delivery ------------------- @@ -186,10 +193,11 @@ access logs to analyze the number of people who access your objects, where they come from, how many requests for each object you receive, and time-based usage patterns (such as monthly or seasonal usage). -.. code:: php +.. code-block:: php + + $container->enableLogging(); + $container->disableLogging(); - $container->enableLogging(); - $container->disableLogging(); Syncing containers ------------------ @@ -198,9 +206,9 @@ You can synchronize local directories with your CloudFiles/Swift containers very easily. When you do this, the container will mirror exactly the nested file structure within your local directory: -.. code:: php +.. code-block:: php - $container->uploadDirectory('/home/Jamie/blog'); + $container->uploadDirectory('/home/user/my-blog'); There are four scenarios you should be aware of: @@ -215,4 +223,3 @@ There are four scenarios you should be aware of: +------------------------+-----------------------+----------------------+--------------------------------+ | Files does not exist | File exists | - | Remote file deleted | +------------------------+-----------------------+----------------------+--------------------------------+ - diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index e69de29bb..6b3a18b6f 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -0,0 +1,57 @@ +Object Store v1 +=============== + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Object Store service: + +.. code-block:: php + + $service = $client->objectStoreService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + account + containers + objects + cdn + migrating-containers + access + +Glossary +-------- + +.. glossary:: + + account + The portion of the system designated for your use. An Object Store system is + typically designed to be used by many different customers, and your user + account is your portion of it. + + container + A storage compartment that provides a way for you to organize data. A + container is similar to a folder in Windows or a directory in UNIX. The + primary difference between a container and these other file system concepts + is that containers cannot be nested. + + cdn + A system of distributed servers (network) that delivers web pages and other + web content to a user based on the geographic locations of the user, the + origin of the web page, and a content delivery server. + + metadata + Optional information that you can assign to Cloud Files accounts, + containers, and objects through the use of a metadata header. + + object + An object (sometimes referred to as a file) is the unit of storage in an + Object Store. An object is a combination of content (data) and metadata. diff --git a/doc/services/object-store/Migrating.md.storage.rst b/doc/services/object-store/migrating-containers.rst similarity index 82% rename from doc/services/object-store/Migrating.md.storage.rst rename to doc/services/object-store/migrating-containers.rst index 99738554b..22adb839b 100644 --- a/doc/services/object-store/Migrating.md.storage.rst +++ b/doc/services/object-store/migrating-containers.rst @@ -1,8 +1,5 @@ -Migrating containers (across regions) -===================================== - -Introduction ------------- +Migrating containers across regions +=================================== Currently, there exists no single API operation to copy containers across geographic endpoints. Although the API offers a ``COPY`` @@ -12,6 +9,7 @@ copying. The SDK, however, does offer this functionality. You **will** be charged for bandwidth between regions, so it's advisable to use ServiceNet where possible (which is free). + Requirements ------------ @@ -20,71 +18,84 @@ Requirements requests to be batched for greater efficiency). You can do this by running: -.. code:: bash +.. code-block:: bash - php composer.phar install --dev + composer require guzzle/guzzle - Depending on the size and number of transfer items, you will need to raise PHP's memory limit: -.. code:: php +.. code-block:: php - ini_set('memory_limit', '512M'); + ini_set('memory_limit', '512M'); - You will need to enact some kind of backoff/retry strategy for rate limits. Guzzle comes with a convenient feature that just needs to be added as a normal subscriber: -.. code:: php +.. code-block:: php use Guzzle\Plugin\Backoff\BackoffPlugin; - $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + // set timeout in secs + $timeout = 10; + + // set HTTP error codes + $httpErrors = array(500, 503, 408); + + $backoffPlugin = BackoffPlugin::getExponentialBackoff($timeout, $httpErrors); + $client->addSubscriber($backoffPlugin); + This tells the client to retry up to ``10`` times for failed requests have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + Setup ----- You can access all this functionality by executing: -.. code:: php +.. code-block:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); - $ordService = $client->objectStoreService('cloudFiles', 'ORD'); - $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); - $oldContainer = $ordService->getContainer('old_container'); - $newContainer = $iadService->getContainer('new_container'); + $iadService->migrateContainer($oldContainer, $newContainer); - $iadService->migrateContainer($oldContainer, $newContainer); It's advisable to do this process in a Cloud Server in one of the two regions you're migrating to/from. This allows you to use ``privateURL`` as the third argument in the ``objectStoreService`` methods like this: -.. code:: php +.. code-block:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); - $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); This will ensure that traffic between your server and your new IAD container will be held over the internal Rackspace network which is free. + Options ------- You can pass in an array of arguments to the method: -.. code:: php +.. code-block:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); - $options = array( - 'read.batchLimit' => 100, - 'read.pageLimit' => 100, - 'write.batchLimit' => 50 - ); + $iadService->migrateContainer($oldContainer, $newContainer, $options); - $iadService->migrateContainer($oldContainer, $newContainer, $options); Options explained ~~~~~~~~~~~~~~~~~ @@ -98,4 +109,3 @@ Options explained +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ | ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ - diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst new file mode 100644 index 000000000..e38e0eb8b --- /dev/null +++ b/doc/services/object-store/objects.rst @@ -0,0 +1,427 @@ +Objects +======= + +Setup +----- + +In order to interact with this feature, you must first retrieve a particular +container using its unique name: + +.. code-block:: php + + $container = $service->getContainer('{containerName}'); + + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + +.. note:: + + Unlike previous versions, you do not need to manually specify your object's + content type. The API will do this for you. + +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in. + +Upload a single file (under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The simplest way to upload a local object, without additional metadata, is by +its path: + +.. code-block:: php + + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+')); + + +The resource handle will be automatically closed by Guzzle in its destructor, +so there is no need to execute ``fclose``. + + +Upload a single file (under 5GB) with metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although the previous section handles most use cases, there are times when you +want greater control over what is being uploaded. For example, you might want +to control the object's metadata, or supply additional HTTP headers to coerce +browsers to handle the download a certain way. To add metadata to a new object: + +.. code-block:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + // specify optional metadata + $metadata = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow', + ); + + // specify optional HTTP headers + $httpHeaders = array( + 'Content-Type' => 'application/json', + ); + + // merge the two + $allHeaders = array_merge(DataObject::stockHeaders($metadata), $httpHeaders); + + // upload as usual + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+'), $allHeaders); + + +As you will notice, the first argument to ``uploadObject`` is the remote object +name, i.e. the name it will be uploaded as. The second argument is either a +file handle resource, or a string representation of object content (a temporary +resource will be created in memory), and the third is an array of additional +headers. + + +Batch upload multiple files (each under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + + +Upload large files (over 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build and execute your +transfer. For your convenience, the Container resource object contains a simple +method to do this heavy lifting for you: + +.. code-block:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/user/video.mov', + 'metadata' => array('Author' => 'Jamie'), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifestfile is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. +To find out more details, please consult the `official documentation +`_. + + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code-block:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code-block:: php + + $container->objectList(array('prefix' => 'logFile_')); + + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code-block:: php + + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + $file = $container->getObject('summer_vacation.mp4'); + +Once you have access to this ``OpenCloud\ObjectStore\Resource\DataObject`` +object, you can access these attributes: + +Get object's parent container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file name +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file size +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $size int */ + $size = $object->getContentLength(); + + +Get content of file +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $content Guzzle\Http\EntityBody */ + $content = $object->getContainer(); + + +Get type of file +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $type string */ + $type = $object->getContentType(); + + +Get file checksum +~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $etag string */ + $etag = $object->getEtag(); + + +Get last modified date of file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $lastModified string */ + $lastModified = $object->getLastModified(); + + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code-block:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code-block:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + + +Update an existing object +------------------------- + +.. code-block:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + + +Copy object to new location +--------------------------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code-block:: php + + $object->copy('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container that does not exist yet. + + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code-block:: php + + $container->getPartialObject('summer_vacation.mp4'); + + +In order to access the metadata on a partial or complete object, use: + +.. code-block:: php + + $object->getMetadata(); + + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code-block:: php + + $object->refresh(); + + +You can also update to get the latest metadata: + +.. code-block:: php + + $object->retrieveMetadata(); + + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code-block:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code-block:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code-block:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code-block:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + + +Delete object +------------- + +.. code-block:: php + + $object->delete(); + + +Delete multiple objects +----------------------- + +Bulk delete a set of paths: + +.. code-block:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); diff --git a/doc/services/object-store/rs-only.rst b/doc/services/object-store/rs-only.rst new file mode 100644 index 000000000..fab4e423f --- /dev/null +++ b/doc/services/object-store/rs-only.rst @@ -0,0 +1,3 @@ +.. note:: + + This feature is only available to Rackspace users. From c9835d25baa9f46de828ce80ef6f715a4959bafa Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:57 +0100 Subject: [PATCH 585/835] Modify toctree --- doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 45c453a40..9c06b86e8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,7 +10,7 @@ Contents: .. toctree:: :glob: - :maxdepth: 2 + :maxdepth: 1 services/**/index From 4957bb79a68e16ad3e9956a1f6b9667fe0f440c2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 17:58:16 +0100 Subject: [PATCH 586/835] Add orchestration docs --- doc/services/orchestration/README.md.rst | 98 --- doc/services/orchestration/USERGUIDE.md.rst | 672 ------------------ doc/services/orchestration/build-info.rst | 15 + doc/services/orchestration/events.rst | 53 ++ doc/services/orchestration/index.rst | 50 ++ doc/services/orchestration/resource-types.rst | 48 ++ doc/services/orchestration/resources.rst | 50 ++ doc/services/orchestration/stacks.rst | 299 ++++++++ doc/services/orchestration/templates.rst | 55 ++ 9 files changed, 570 insertions(+), 770 deletions(-) delete mode 100644 doc/services/orchestration/README.md.rst delete mode 100644 doc/services/orchestration/USERGUIDE.md.rst create mode 100644 doc/services/orchestration/build-info.rst create mode 100644 doc/services/orchestration/events.rst create mode 100644 doc/services/orchestration/resource-types.rst create mode 100644 doc/services/orchestration/resources.rst create mode 100644 doc/services/orchestration/stacks.rst create mode 100644 doc/services/orchestration/templates.rst diff --git a/doc/services/orchestration/README.md.rst b/doc/services/orchestration/README.md.rst deleted file mode 100644 index 1a983d187..000000000 --- a/doc/services/orchestration/README.md.rst +++ /dev/null @@ -1,98 +0,0 @@ -Orchestration -============= - -**Orchestration** is a service that can be used to create and manage -cloud resources. Examples of such resources are databases, load -balancers, servers and software installed on them. - -Concepts --------- - -To use the Orchestration service effectively, you should understand -several key concepts: - -- **Template**: An Orchestration template is a JSON or YAML document - that describes how a set of resources should be assembled to produce - a working deployment. The template specifies what resources should be - used, what attributes of these resources are parameterized and what - information is output to the user when a template is instantiated. - -- **Resource**: A resource is a template artifact that represents some - component of your desired architecture (a Cloud Server, a group of - scaled Cloud Servers, a load balancer, some configuration management - system, and so forth). - -- **Stack**: A stack is a running instance of a template. When a stack - is created, the resources specified in the template are created. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the Orchestration service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Orchestration service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All Orchestration operations are done via an *orchestration service -object*. To instantiate this object, call the ``orchestrationService`` -method on the ``$client`` object as shown in the following example: - -.. code:: php - - $region = ''; - $orchestrationService = $client->orchestrationService(null, $region); - -Any stacks and resources created with this ``$orchestrationService`` -instance will be stored in the cloud region specified by ``$region``. - -3. Create a stack from a template. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -Once you have created a stack, there is more you can do with it. See -`complete user guide for orchestration `__. diff --git a/doc/services/orchestration/USERGUIDE.md.rst b/doc/services/orchestration/USERGUIDE.md.rst deleted file mode 100644 index 98bd51db7..000000000 --- a/doc/services/orchestration/USERGUIDE.md.rst +++ /dev/null @@ -1,672 +0,0 @@ -Complete User Guide for the Orchestration Service -================================================= - -Orchestration is a service that you can use to create and manage cloud -resources such as databases, load balancers, and servers, and the -software installed on servers. - -Table of Contents ------------------ - -- `Concepts <#concepts>`__ -- `Prerequisites <#prerequisites>`__ -- `Client <#client>`__ -- `Orchestration service <#orchestration-service>`__ -- `Templates <#templates>`__ -- `Validate template <#validate-template>`__ - - - `Validate a template from a - file <#validate-a-template-from-a-file>`__ - - `Validate Template from URL <#validate-template-from-url>`__ - -- `Stacks <#stacks>`__ -- `Preview stack <#preview-stack>`__ - - - `Preview a stack from a template - file <#preview-a-stack-from-a-template-file>`__ - - `Preview a stack from a template - URL <#preview-a-stack-from-a-template-url>`__ - -- `Create stack <#create-stack>`__ - - - `Create a stack from a template - file <#create-a-stack-from-a-template-file>`__ - - `Create a stack from a template - URL <#create-a-stack-from-a-template-url>`__ - -- `List stacks <#list-stacks>`__ -- `Get stack <#get-stack>`__ -- `Get stack template <#get-stack-template>`__ -- `Update stack <#update-stack>`__ - - - `Update a stack from a template - file <#update-a-stack-from-a-template-file>`__ - - `Update Stack from Template - URL <#update-stack-from-template-url>`__ - -- `Delete stack <#delete-stack>`__ -- `Abandon Stack <#abandon-stack>`__ -- `Adopt stack <#adopt-stack>`__ -- `Stack resources <#stack-resources>`__ -- `List stack resources <#list-stack-resources>`__ -- `Get stack resource <#get-stack-resource>`__ -- `Get stack resource metadata <#get-stack-resource-metadata>`__ -- `Stack resource events <#stack-resource-events>`__ -- `List stack events <#list-stack-events>`__ -- `List stack resource events <#list-stack-resource-events>`__ -- `Get stack resource event <#get-stack-resource-event>`__ -- `Resource types <#resource-types>`__ -- `List resource types <#list-resource-types>`__ -- `Get resource type <#get-resource-type>`__ -- `Get resource type template <#get-resource-type-template>`__ -- `Build info <#build-info>`__ -- `Get build info <#get-build-info>`__ - -Concepts --------- - -To use the Orchestration service effectively, you should understand the -following key concepts: - -- **Template**: A JSON or YAML document that describes how a set of - resources should be assembled to produce a working deployment. The - template specifies the resources to use, the attributes of these - resources that are parameterized and the information that is sent to - the user when a template is instantiated. - -- **Resource**: Some component of your architecture (a cloud server, a - group of scaled cloud servers, a load balancer, some configuration - management system, and so on) that is defined in a template. - -- **Stack**: A running instance of a template. When a stack is created, - the resources specified in the template are created. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the Orchestration service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Orchestration service -~~~~~~~~~~~~~~~~~~~~~ - -All Orchestration operations are done via an *orchestration service -object*. To instantiate this object, call the ``orchestrationService`` -method on the ``$client`` object. This method takes two arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+==========================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ - -.. code:: php - - $region = ''; - $orchestrationService = $client->orchestrationService(null, $region); - -Any stacks and resources created with this ``$orchestrationService`` -instance will be stored in the cloud region specified by ``$region``. - -Templates ---------- - -An Orchestration template is a JSON or YAML document that describes how -a set of resources should be assembled to produce a working deployment -(known as a `stack <#stacks>`__). The template specifies the resources -to use, the attributes of these resources that are parameterized and the -information that is sent to the user when a template is instantiated. - -Validate template -~~~~~~~~~~~~~~~~~ - -Before you use a template to create a stack, you might want to validate -it. - -Validate a template from a file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can validate it as shown in the following example: - -.. code:: php - - use OpenCloud\Common\Exceptions\InvalidTemplateError; - - try { - $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') - )); - } catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid - } - -[ `Get the executable PHP script for this -example `__ -] - -Validate Template from URL -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can validate it as shown in the -following example: - -.. code:: php - - use OpenCloud\Common\Exceptions\InvalidTemplateError; - - try { - $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' - )); - } catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid - } - -[ `Get the executable PHP script for this -example `__ -] - -Stacks ------- - -A stack is a running instance of a template. When a stack is created, -the `resources <#stack-resources>`__ specified in the template are -created. - -Preview stack -~~~~~~~~~~~~~ - -Before you create a stack from a template, you might want to see what -that stack will look like. This is called *previewing the stack*. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Preview a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to preview a stack as shown in the following -example: - -.. code:: php - - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Preview a stack from a template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to preview a stack as shown -in the following example: - -.. code:: php - - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Create stack -~~~~~~~~~~~~ - -You can create a stack from a template. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Create a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to create a stack as shown in the following -example: - -.. code:: php - - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Create a stack from a template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to create a stack as shown -in the following example: - -.. code:: php - - $stack = $orchestrationService->stack(); - $stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -[ `Get the executable PHP script for this -example `__ ] - -List stacks -~~~~~~~~~~~ - -You can list all the stacks that you have created as shown in the -following example: - -.. code:: php - - $stacks = $orchestrationService->listStacks(); - foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack -~~~~~~~~~ - -You can retrieve a specific stack using its name, as shown in the -following example: - -.. code:: php - - $stack = $orchestrationService->getStack('simple-lamp-setup'); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get stack template -~~~~~~~~~~~~~~~~~~ - -You can retrieve the template used to create a stack. Note that a JSON -string is returned, regardless of whether a JSON or YAML template was -used to create the stack. - -.. code:: php - - $stackTemplate = $stack->getTemplate(); - /** @var $stackTemplate string **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update stack -~~~~~~~~~~~~ - -You can update a running stack. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ - -Update a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to update a stack as shown in the following -example: - -.. code:: php - - $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Update Stack from Template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to update a stack as shown -in the following example: - -.. code:: php - - $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Delete stack -~~~~~~~~~~~~ - -If you no longer need a stack and all its resources, you can delete the -stack *and* the resources as shown in the following example: - -.. code:: php - - $stack->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Abandon Stack -~~~~~~~~~~~~~ - -If you want to delete a stack but preserve all its resources, you can -abandon the stack as shown in the following example: - -.. code:: php - - $abandonStackData = $stack->abandon(); - /** @var $abandonStackData string **/ - - file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); - -[ `Get the executable PHP script for this -example `__ ] - -Note that this operation returns data about the abandoned stack as a -string. You can use this data to recreate the stack by using the `adopt -stack <#adopt-stack>`__ operation. - -Adopt stack -~~~~~~~~~~~ - -If you have data from an abandoned stack, you can re-create the stack as -shown in the following example: - -.. code:: php - - $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Stack resources ---------------- - -A stack is made up of zero or more resources such as databases, load -balancers, and servers, and the software installed on servers. - -List stack resources -~~~~~~~~~~~~~~~~~~~~ - -You can list all the resources for a stack as shown in the following -example: - -.. code:: php - - $resources = $stack->listResources(); - foreach ($resources as $resource) { - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource -~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource in a stack bt using that resource's -name, as shown in the following example: - -.. code:: php - - $resource = $stack->getResource('load-balancer'); - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve the metadata for a specific resource in a stack as -shown in the following example: - -.. code:: php - - $resourceMetadata = $resource->getMetadata(); - /** @var $resourceMetadata \stdClass **/ - -[ `Get the executable PHP script for this -example `__ ] - -Stack resource events ---------------------- - -Operations on resources within a stack (such as the creation of a -resource) produce events. - -List stack events -~~~~~~~~~~~~~~~~~ - -You can list all of the events for all of the resources in a stack as -shown in the following example: - -.. code:: php - - $stackEvents = $stack->listEvents(); - foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List stack resource events -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all of the events for a specific resource in a stack as -shown in the following example: - -.. code:: php - - $resourceEvents = $resource->listEvents(); - foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource event -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific event for a specific resource in a stack, by -using the resource event's ID, as shown in the following example: - -.. code:: php - - $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ - -[ `Get the executable PHP script for this -example `__ ] - -Resource types --------------- - -When you define a template, you must use resource types supported by -your cloud. - -List resource types -~~~~~~~~~~~~~~~~~~~ - -You can list all supported resource types as shown in the following -example: - -.. code:: php - - $resourceTypes = $orchestrationService->listResourceTypes(); - foreach ($resourceTypes as $resourceType) { - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get resource type -~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource type's schema as shown in the -following example: - -.. code:: php - - $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get resource type template -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource type's representation as it would -appear in a template, as shown in the following example: - -.. code:: php - - $resourceTypeTemplate = $resourceType->getTemplate(); - /** @var $resourceTypeTemplate string **/ - -[ `Get the executable PHP script for this -example `__ ] - -Build info ----------- - -Get build info -~~~~~~~~~~~~~~ - -You can retrieve information about the current Orchestration service -build as shown in the following example: - -.. code:: php - - $buildInfo = $orchestrationService->getBuildInfo(); - /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ - -[ `Get the executable PHP script for this -example `__ ] diff --git a/doc/services/orchestration/build-info.rst b/doc/services/orchestration/build-info.rst new file mode 100644 index 000000000..c30ce5cfa --- /dev/null +++ b/doc/services/orchestration/build-info.rst @@ -0,0 +1,15 @@ +Build info +========== + +Get build info +-------------- + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + $buildInfo = $orchestrationService->getBuildInfo(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/events.rst b/doc/services/orchestration/events.rst new file mode 100644 index 000000000..68dbf611b --- /dev/null +++ b/doc/services/orchestration/events.rst @@ -0,0 +1,53 @@ +Stack resource events +===================== + +Operations on resources within a stack (such as the creation of a +resource) produce events. + + +List stack events +----------------- + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code-block:: php + + $stackEvents = $stack->listEvents(); + + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +List stack resource events +-------------------------- + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + $resourceEvents = $resource->listEvents(); + + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource event +------------------------ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code-block:: php + + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index e69de29bb..0dce8ac43 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -0,0 +1,50 @@ +Orchestration v1 +================ + +Setup +----- + +.. include:: rs-client.sample.rst + +Now set up the Orchestration service: + +.. code-block:: php + + $service = $client->orchestrationService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + templates + stacks + resources + resource-types + build-info + + +Glossary +-------- + +.. glossary:: + + template + An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + + resource + A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + + stack + A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. diff --git a/doc/services/orchestration/resource-types.rst b/doc/services/orchestration/resource-types.rst new file mode 100644 index 000000000..cd229d660 --- /dev/null +++ b/doc/services/orchestration/resource-types.rst @@ -0,0 +1,48 @@ +Resource types +============== + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +------------------- + +You can list all supported resource types as shown in the following +example: + +.. code-block:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +`Get the executable PHP script for this example `_ + + +Get resource type +----------------- + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + +`Get the executable PHP script for this example `_ + + +Get resource type template +-------------------------- + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code-block:: php + + /** @var $resourceTypeTemplate string **/ + $resourceTypeTemplate = $resourceType->getTemplate(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resources.rst b/doc/services/orchestration/resources.rst new file mode 100644 index 000000000..049150f58 --- /dev/null +++ b/doc/services/orchestration/resources.rst @@ -0,0 +1,50 @@ +Stack resources +=============== + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + + +List stack resources +-------------------- + +You can list all the resources for a stack as shown in the following +example: + +.. code-block:: php + + $resources = $stack->listResources(); + + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource +------------------ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code-block:: php + + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + $resource = $stack->getResource('load-balancer'); + +`Get the executable PHP script for this example `_ + + +Get stack resource metadata +--------------------------- + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + /** @var $resourceMetadata \stdClass **/ + $resourceMetadata = $resource->getMetadata(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst b/doc/services/orchestration/stacks.rst new file mode 100644 index 000000000..75a14cd4c --- /dev/null +++ b/doc/services/orchestration/stacks.rst @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/templates.rst b/doc/services/orchestration/templates.rst new file mode 100644 index 000000000..5eca2519d --- /dev/null +++ b/doc/services/orchestration/templates.rst @@ -0,0 +1,55 @@ +Templates +========= + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validating templates +-------------------- + +Before you use a template to create a stack, you might want to validate it. + + +Validate a template from a file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ + +Validate Template from URL +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ From fe2adb3e2244be0b3f15a6256dd19691012d7b70 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 19:12:57 +0100 Subject: [PATCH 587/835] Add Queues docs --- doc/services/queues/Queue.md.rst | 197 ------------- .../queues/{Claim.md.rst => claims.rst} | 115 +++----- doc/services/queues/index.rst | 48 +++ .../queues/{Message.md.rst => messages.rst} | 274 +++++++----------- doc/services/queues/queues.rst | 124 ++++++++ 5 files changed, 316 insertions(+), 442 deletions(-) delete mode 100644 doc/services/queues/Queue.md.rst rename doc/services/queues/{Claim.md.rst => claims.rst} (68%) rename doc/services/queues/{Message.md.rst => messages.rst} (52%) create mode 100644 doc/services/queues/queues.rst diff --git a/doc/services/queues/Queue.md.rst b/doc/services/queues/Queue.md.rst deleted file mode 100644 index 1fe9f7036..000000000 --- a/doc/services/queues/Queue.md.rst +++ /dev/null @@ -1,197 +0,0 @@ -1. Introduction ---------------- - -A Queue is an entity that holds messages. Ideally, a queue is created -per work type. For example, if you want to compress files, you would -create a queue dedicated to this job. Any application that reads from -this queue would only compress files. - -2. Setup --------- - -.. code:: php - - $service = $client->queuesService('cloudQueues', 'ORD'); - -3. Client IDs -------------- - -With most of Marconi's operation, you must specify a **Client ID** which -will be used as a unique identifier for the process accessing this -Queue. This is basically a UUID that must be unique to each client -accessing the API - it can be an arbitrary string. - -.. code:: php - - $service->setClientId(); - - echo $service->getClientId(); - -If you call ``setClientId`` without any parameters, a UUID is -automatically generated for you. - -4. List queues --------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation lists queues for the project. The queues are sorted -alphabetically by name. - -4.2 Parameters -~~~~~~~~~~~~~~ - -\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| -\|marker\|Query\|​String\|Specifies the name of the last queue received -in a previous request, or none to get the first page of results. -Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to -return. The default value for the number of queues returned is 10. If -you do not specify this parameter, the default number of queues is -returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether -queue metadata is included in the response. The default value for this -parameter is false, which excludes the metadata. Optional.\| -\|----\|-----\|----\|-----------\| - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queues = $service->listQueues(); - - while ($queue = $queues->next()) { - echo $queue->getName() . PHP_EOL; - } - -5. Create queue ---------------- - -5.1 Description -~~~~~~~~~~~~~~~ - -This operation creates a new queue. - -5.2 Parameters -~~~~~~~~~~~~~~ - -A string representation of the name for your new Queue. The name must -not exceed 64 bytes in length, and it is limited to US-ASCII letters, -digits, underscores, and hyphens. - -5.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue = $service->createQueue('new_queue'); - -6. Retrieve queue ------------------ - -6.1 Description -~~~~~~~~~~~~~~~ - -Returns a ``Queue`` object for use. - -6.2 Parameters -~~~~~~~~~~~~~~ - -Queue name. - -6.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue = $service->getQueue('new_queue'); - -7. Check queue existence ------------------------- - -7.1 Description -~~~~~~~~~~~~~~~ - -This operation verifies whether the specified queue exists by returning -``TRUE`` or ``FALSE``. - -7.2 Parameters -~~~~~~~~~~~~~~ - -7.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - if ($service->hasQueue('new_queue')) { - // do something - } - -8. Update queue metadata (permanently to the API) -------------------------------------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation replaces any existing metadata document in its entirety. -Ensure that you do not accidentally overwrite existing metadata that you -want to retain. If you want to *append* metadata, ensure you merge a new -array to the existing values. - -4.2 Parameters -~~~~~~~~~~~~~~ - -Hash of key pairs. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue->saveMetadata(array( - 'foo' => 'bar' - )); - -9. Retrieve the queue metadata (fresh from the API) ---------------------------------------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation returns metadata, such as message TTL, for the queue. - -4.2 Parameters -~~~~~~~~~~~~~~ - -None. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadata = $queue->retrieveMetadata(); - - print_r($metadata->toArray()); - -10. Get queue stats -------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation returns queue statistics, including how many messages are -in the queue, categorized by status. - -4.2 Parameters -~~~~~~~~~~~~~~ - -None. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue->getStats(); - diff --git a/doc/services/queues/Claim.md.rst b/doc/services/queues/claims.rst similarity index 68% rename from doc/services/queues/Claim.md.rst rename to doc/services/queues/claims.rst index 42161536d..7e375c8d3 100644 --- a/doc/services/queues/Claim.md.rst +++ b/doc/services/queues/claims.rst @@ -1,28 +1,18 @@ -1. Introduction ---------------- +Claims +====== -A **Claim** is the process of a worker checking out a message to perform -a task. Claiming a message prevents other workers from attempting to -process the same messages. +Setup +----- -2. Setup --------- +In order to work with messages, you must first retrieve a queue by its name: -A Claim is initialized on its parent object, a Queue: +.. code-block:: php -.. code:: php + $queue = $service->getQueue('{queueName}'); - // To initialize an empty object: - $claim = $queue->getClaim(); - // or retrieve a specific claim: - $claim = $queue->getClaim('51db7067821e727dc24df754'); - -3. Claim messages ------------------ - -3.1 Description -~~~~~~~~~~~~~~~ +Claim messages +-------------- This operation claims a set of messages (up to the value of the limit parameter) from oldest to newest and skips any messages that are already @@ -46,8 +36,8 @@ and whether a given message's claim is about to expire. When a claim expires, it is released. If the original worker failed to process the message, another client worker can then claim the message. -3.2 Attributes -~~~~~~~~~~~~~~ +Parameters +~~~~~~~~~~ The ``ttl`` attribute specifies how long the server waits before releasing the claim. The ttl value must be between 60 and 43200 seconds @@ -67,46 +57,30 @@ The ``limit`` attribute specifies the number of messages to return, up to 20 messages. If limit is not specified, limit defaults to 10. The limit parameter is optional. -3.3 Code -~~~~~~~~ +.. code-block:: php -.. code:: php + use OpenCloud\Common\Constants\Datetime; - use OpenCloud\Common\Constants\Datetime; + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); - $queue->claimMessages(array( - 'limit' => 15, - 'grace' => 5 * Datetime::MINUTE, - 'ttl' => 5 * Datetime::MINUTE - )); - -4. Query claim --------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation queries the specified claim for the specified queue. -Claims with malformed IDs or claims that are not found by ID are -ignored. -4.2 Attributes -~~~~~~~~~~~~~~ +Query claim +----------- -Claim ID. +This operation queries the specified claim for the specified queue. Claims with +malformed IDs or claims that are not found by ID are ignored. -4.3 Code -~~~~~~~~ +.. code-block:: php -.. code:: php + $claim = $queue->getClaim('{claimId}'); - $claim = $queue->getClaim('51db7067821e727dc24df754'); -5. Update claim ---------------- - -5.1 Description -~~~~~~~~~~~~~~~ +Update claim +------------ This operation updates the specified claim for the specified queue. Claims with malformed IDs or claims that are not found by ID are @@ -118,27 +92,17 @@ renew a claim by executing this method on a specific **Claim** and including a new TTL. The API will then reset the age of the claim and apply the new TTL. -5.2 Attributes -~~~~~~~~~~~~~~ - -See section 4.2. - -5.3 Code -~~~~~~~~ - -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $claim->update(array( - 'ttl' => 10 * Datetime::MINUTE - )); + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); -6. Release claim ----------------- -6.1 Description -~~~~~~~~~~~~~~~ +Release claim +------------- This operation immediately releases a claim, making any remaining undeleted messages that are associated with the claim available to other @@ -150,15 +114,6 @@ shutdown, fails to process one or more messages, or is taking longer than expected to process messages, and wants to make the remainder of the messages available to other workers. -6.2 Attributes -~~~~~~~~~~~~~~ - -See section 4.2. - -6.3 Code -~~~~~~~~ - -.. code:: php - - $message->delete(); +.. code-block:: php + $message->delete(); diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index e69de29bb..e37845d76 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -0,0 +1,48 @@ +Queues v1 +========= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now to instantiate the Queues service: + +.. code-block:: php + + $service = $client->queuesService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + queues + messages + claims + + +Glossary +-------- + +.. glossary:: + + claim + A Claim is the process of a worker checking out a message to perform + a task. Claiming a message prevents other workers from attempting to + process the same messages. + + queue + A Queue is an entity that holds messages. Ideally, a queue is created + per work type. For example, if you want to compress files, you would + create a queue dedicated to this job. Any application that reads from + this queue would only compress files. + + message + A Message is a task, a notification, or any meaningful data that a + producer or publisher sends to the queue. A message exists until it is + deleted by a recipient or automatically by the system based on a TTL + (time-to-live) value. diff --git a/doc/services/queues/Message.md.rst b/doc/services/queues/messages.rst similarity index 52% rename from doc/services/queues/Message.md.rst rename to doc/services/queues/messages.rst index ca67e22bb..ea23caea3 100644 --- a/doc/services/queues/Message.md.rst +++ b/doc/services/queues/messages.rst @@ -1,29 +1,18 @@ -1. Introduction ---------------- +Messages +======== -A **Message** is a task, a notification, or any meaningful data that a -producer or publisher sends to the queue. A message exists until it is -deleted by a recipient or automatically by the system based on a TTL -(time-to-live) value. +Setup +----- -2. Setup --------- +In order to work with messages, you must first retrieve a queue by its name: -A message is initialized from its parent object, a Queue: +.. code-block:: php -.. code:: php + $queue = $service->getQueue('{queueName}'); - // Setup an empty object - $message = $queue->getMessage(); - // or retrieve an existing one - $message = $queue->getMessage(''); - -3. Post message ---------------- - -3.1 Description -~~~~~~~~~~~~~~~ +Post new message +---------------- This operation posts the specified message or messages. You can submit up to 10 messages in a single request. @@ -31,73 +20,67 @@ up to 10 messages in a single request. When posting new messages, you specify only the ``body`` and ``ttl`` for the message. The API will insert metadata, such as ID and age. -3.2 Parameters -~~~~~~~~~~~~~~ - How you pass through the array structure depends on whether you are -executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the -same. +executing multiple or single posts, but the keys are the +same: -The ``body`` attribute specifies an arbitrary document that constitutes -the body of the message being sent. The size of this body is limited to -256 KB, excluding whitespace. +* The ``body`` attribute specifies an arbitrary document that constitutes + the body of the message being sent. The size of this body is limited to + 256 KB, excluding whitespace. -The ``ttl`` attribute specifies how long the server waits before marking -the message as expired and removing it from the queue. The value of ttl -must be between 60 and 1209600 seconds (14 days). Note that the server -might not actually delete the message until its age has reached up to -(ttl + 60) seconds, to allow for flexibility in storage implementations. +* The ``ttl`` attribute specifies how long the server waits before marking + the message as expired and removing it from the queue. The value of ttl + must be between 60 and 1209600 seconds (14 days). Note that the server + might not actually delete the message until its age has reached up to + (ttl + 60) seconds, to allow for flexibility in storage implementations. -3.3 Code samples -~~~~~~~~~~~~~~~~ -3.3.1 Posting a single message -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Posting a single message +~~~~~~~~~~~~~~~~~~~~~~~~ -:: +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $queue->createMessage(array( - 'body' => (object) array( - 'event' => 'BackupStarted', - 'deadline' => '26.12.2013 - ), - 'ttl' => 2 * Datetime::DAY - )); + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013', + ), + 'ttl' => 2 * Datetime::DAY + )); -3.3.2 Post a batch of messages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Post a batch of messages +~~~~~~~~~~~~~~~~~~~~~~~~ Please note that the list of messages will be truncated at 10. For more, please execute another method call. -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $messages = array( - array( - 'body' => (object) array( - 'play' => 'football' - ), - 'ttl' => 2 * Datetime::DAY - ), - array( - 'body' => (object) array( - 'play' => 'tennis' - ), - 'ttl' => 50 * Datetime::HOUR - ) - ); + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); - $queue->createMessages($messages); + $queue->createMessages($messages); -4. Get messages ---------------- -4.1 Description -~~~~~~~~~~~~~~~ +Get messages +------------ This operation gets the message or messages in the specified queue. @@ -109,10 +92,11 @@ variety of storage driver implementations. Results are ordered by age, oldest message first. -4.2 Parameters -~~~~~~~~~~~~~~ -A hash of options. +Parameters +~~~~~~~~~~ + +When retrieving messages, you can filter using these options: +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Name | Style | Type | Description | @@ -123,135 +107,95 @@ A hash of options. +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | +| include_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $messages = $queue->listMessages(array( - 'marker' => '51db6f78c508f17ddc924357', - 'limit' => 20, - 'echo' => true - )); + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); - while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; - } + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } -5. Get a set of messages by ID ------------------------------- -5.1 Description -~~~~~~~~~~~~~~~ +Get a set of messages by ID +--------------------------- This operation provides a more efficient way to query multiple messages compared to using a series of individual GET. Note that the list of IDs cannot exceed 20. If a malformed ID or a nonexistent message ID is provided, it is ignored, and the remaining messages are returned. -5.2 Parameters -~~~~~~~~~~~~~~ - -A hash of options. +Parameters +~~~~~~~~~~ -\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| -\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format -multiple message ID values by separating them with commas -(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies -the claim ID with which the message is associated. Optional.\| -\|----\|-----\|----\|-----------\| ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++============+=========+============+========================================================================================================================================+ +| ids | Query | String | Specifies the IDs of the messages to get. Format multiple message ID values by separating them with commas (comma-separated). Optional | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| claim_id | Query | ​Boolean | Specifies the claim ID with which the message is associated. Optional. | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ -5.3 Code sample -~~~~~~~~~~~~~~~ -.. code:: php +.. code-block:: php - $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + $ids = array('id_1', 'id_2'); - $messages = $queue->listMessages(array('ids' => $ids)); + $messages = $queue->listMessages(array('ids' => $ids)); - while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; - } + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } -6. Delete a set of messages by ID ---------------------------------- -6.1 Description -~~~~~~~~~~~~~~~ +Delete a set of messages by ID +------------------------------ This operation immediately deletes the specified messages. If any of the message IDs are malformed or non-existent, they are ignored. The remaining valid messages IDs are deleted. -6.2 Parameters -~~~~~~~~~~~~~~ - -An array of IDs. - -6.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + $ids = array('id_1', 'id_2'); + $response = $queue->deleteMessages($ids); - $response = $queue->deleteMessages($ids); -7. Get a specific message -------------------------- - -7.1 Description -~~~~~~~~~~~~~~~ +Get a specific message +---------------------- This operation gets the specified message from the specified queue. -7.2 Parameters -~~~~~~~~~~~~~~ - -Message ID. - -7.3 Object properties -~~~~~~~~~~~~~~~~~~~~~ - -``href`` is an opaque relative URI that the client can use to uniquely -identify a message resource and interact with it. - -``ttl`` is the TTL that was set on the message when it was posted. The -message expires after (ttl - age) seconds. - -``age`` is the number of seconds relative to the server's clock. - -``body`` is the arbitrary document that was submitted with the original -request to post the message. - -7.4 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $message = $queue->getMessage('51db6f78c508f17ddc924357'); - -8. Delete message ------------------ +.. code-block:: php -8.1 Description -~~~~~~~~~~~~~~~ + /** @var $message OpenCloud\Queues\Message */ + $message = $queue->getMessage('{messageId}'); -This operation immediately deletes the specified message. -8.2 Parameters -~~~~~~~~~~~~~~ +Once you have access to the ``Message`` object, you access its attributes: -None. ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| attribute | method | description | ++===========+=============+==============================================================================================================+ +| href | ``getHref`` | An opaque relative URI that the client can use to uniquely identify a message resource and interact with it. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| ttl | ``getTtl`` | The TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| age | ``getAge`` | The number of seconds relative to the server's clock. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| body | ``getBody`` | The arbitrary document that was submitted with the original request to post the message. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ -8.3 Code sample -~~~~~~~~~~~~~~~ -.. code:: php +Delete message +-------------- - $message->delete(); +.. code-block:: php + $message->delete(); diff --git a/doc/services/queues/queues.rst b/doc/services/queues/queues.rst new file mode 100644 index 000000000..7958adfae --- /dev/null +++ b/doc/services/queues/queues.rst @@ -0,0 +1,124 @@ +Queues +====== + +A note on Client IDs +-------------------- + +For most of the operations in Cloud Queues, you must specify a **Client ID** +which will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code-block:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + + +List queues +----------- + +This operation lists queues for the project. The queues are sorted alphabetically by name. + +.. code-block:: php + + $queues = $service->listQueues(); + + foreach ($queues as $queue) { + echo $queue->getName() , PHP_EOL; + } + + +Filtering lists +~~~~~~~~~~~~~~~ + +You can also filter collections using the following query parameters: + ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++==========+=======+=========+================================================================================================================================================================================================+ +| marker | Query | ​String | Specifies the name of the last queue received in a previous request, or none to get the first page of results. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | Specifies the number of queues to return. The default value for the number of queues returned is 10. If you do not specify this parameter, the default number of queues is returned. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| detailed | Query | ​Boolean | Determines whether queue metadata is included in the response. The default value for this parameter is false, which excludes the metadata. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. code-block:: php + + $queues = $service->listQueues(array('detailed' => false)); + + +Create queue +------------ + +The only parameter required is the name of the queue you're creating. The name +must not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +.. code-block:: php + + $queue = $service->createQueue('new_queue'); + + +Find queue details +------------------ + +.. code-block:: php + + /** @var $queue OpenCloud\Queues\Resource\Queues */ + $queue = $service->getQueue('{name}'); + + +Check queue existence +--------------------- + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +.. code-block:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + + +Update queue metadata +--------------------- + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +.. code-block:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + + +Retrieve the queue metadata +--------------------------- + +This operation returns metadata, such as message TTL, for the queue. + +.. code-block:: php + + $metadata = $queue->retrieveMetadata(); + print_r($metadata->toArray()); + + +Get queue stats +--------------- + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +.. code-block:: php + + $queue->getStats(); From 78a3e6a5a7dbd2e45cb488a2a5982ae850c3161c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:20:43 -0800 Subject: [PATCH 588/835] Fix what properties can be updated. --- lib/OpenCloud/Networking/Resource/Network.php | 4 +--- lib/OpenCloud/Networking/Resource/Port.php | 6 +----- tests/OpenCloud/Smoke/Unit/Networking.php | 8 +------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 53716a8f0..19454445e 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -57,9 +57,7 @@ class Network extends PersistentResource implements NetworkInterface ); protected $updateKeys = array( - 'adminStateUp', - 'name', - 'shared' + 'name' ); /** diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 484de675e..bc7ee4dd0 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -78,11 +78,7 @@ class Port extends PersistentResource protected $updateKeys = array( 'name', - 'adminStateUp', - 'deviceId', - 'deviceOwner', - 'fixedIps', - 'securityGroups' + 'deviceId' ); /** diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index b6ab4592e..2518119bd 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -245,13 +245,7 @@ protected function testPortOperations() $this->step('Update port'); $port->update(array( - 'name' => 'updated_test_port', - 'fixedIps' => array( - array( - 'subnetId' => $subnet1->getId(), - 'ipAddress' => '192.168.62.17' - ) - ) + 'name' => 'updated_test_port' )); } From af451ae39839f7879e65516a5f7e5efe55365088 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:12 -0800 Subject: [PATCH 589/835] Fix variable names. --- tests/OpenCloud/Smoke/Unit/Networking.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 2518119bd..7b424c46f 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -252,7 +252,7 @@ protected function testPortOperations() protected function testSecurityGroupOperations() { $this->step('Create security group'); - $security group = $this->getService()->createSecurityGroup(array( + $securityGroup = $this->getService()->createSecurityGroup(array( 'name' => 'new-webservers', 'description' => 'security group for webservers' )); @@ -269,7 +269,7 @@ protected function testSecurityGroupOperations() } $this->step('Get security group'); - $security group = $this->getService()->getSecurityGroup($securityGroup->getId()); + $securityGroup = $this->getService()->getSecurityGroup($securityGroup->getId()); $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); } @@ -282,7 +282,7 @@ protected function testSecurityGroupRuleOperations() $this->cleanupSecurityGroupIds[] = $securityGroup1->getId(); $this->step('Create security group rule'); - $security group rule = $this->getService()->createSecurityGroupRule(array( + $securityGroupRule = $this->getService()->createSecurityGroupRule(array( 'securityGroupId' => $securityGroup1->getId(), 'direction' => 'egress', 'ethertype' => 'IPv4', From 1b8c6663dce1828e6c6a0bef16307cb120775644 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:27 -0800 Subject: [PATCH 590/835] Only ingress rules are currently allowed. --- tests/OpenCloud/Smoke/Unit/Networking.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 7b424c46f..38d903ff0 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -284,7 +284,7 @@ protected function testSecurityGroupRuleOperations() $this->step('Create security group rule'); $securityGroupRule = $this->getService()->createSecurityGroupRule(array( 'securityGroupId' => $securityGroup1->getId(), - 'direction' => 'egress', + 'direction' => 'ingress', 'ethertype' => 'IPv4', 'portRangeMin' => 80, 'portRangeMax' => 80, From 1add751b25b8a9231a35b0ebea097b29f993fd24 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:41 -0800 Subject: [PATCH 591/835] Remote groups are not currently allowed. --- tests/OpenCloud/Smoke/Unit/Networking.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 38d903ff0..deaefda33 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -288,8 +288,7 @@ protected function testSecurityGroupRuleOperations() 'ethertype' => 'IPv4', 'portRangeMin' => 80, 'portRangeMax' => 80, - 'protocol' => 'tcp', - 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + 'protocol' => 'tcp' )); $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); From 710eb1996b4ab54216d29b884655657108b48315 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:55 -0800 Subject: [PATCH 592/835] Security group rules don't have names. s/name/direction/. --- tests/OpenCloud/Smoke/Unit/Networking.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index deaefda33..8ccd30a94 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -296,16 +296,16 @@ protected function testSecurityGroupRuleOperations() $this->step('List security group rules'); $securityGroupRules = $this->getService()->listSecurityGroupRules(); - $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule name'); + $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule direction'); $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); foreach ($securityGroupRules as $securityGroupRule) { - $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getName()); + $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getDirection()); } $this->step('Get security group rule'); - $security group rule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); + $securityGroupRule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); - $this->stepInfo('Security Group Rule Name: ' . $securityGroupRule->getName()); + $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); } public function teardown() From a192da8e000647b71fcc08a252f873ad689d6968 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:38:43 -0800 Subject: [PATCH 593/835] Appeasing the linter. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 +- lib/OpenCloud/Networking/Resource/SecurityGroupRule.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index 105fc22a1..2892f5471 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * + * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * * @package OpenCloud\Networking\Resource diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php index 3659d08ac..5527ce272 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * + * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * * @package OpenCloud\Networking\Resource From f5728c940fe2bb160dd4f829c8a0bed5299dad93 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 11:24:03 +0100 Subject: [PATCH 594/835] Shuffle setup org --- doc/services/autoscale/index.rst | 8 +- doc/services/common/clients.sample.rst | 22 ++++++ doc/services/common/os-client.sample.rst | 13 --- .../{rs-client.sample.rst => rs-client.rst} | 8 +- doc/services/common/rs-only.sample.rst | 8 ++ doc/services/common/service-args.rst | 23 +++--- doc/services/compute/index.rst | 13 +-- doc/services/database/index.rst | 8 +- doc/services/dns/index.rst | 8 +- doc/services/identity/index.rst | 8 +- doc/services/image/index.rst | 8 +- doc/services/load-balancer/index.rst | 12 +-- doc/services/monitoring/index.rst | 8 +- doc/services/networking/index.rst | 8 +- doc/services/object-store/index.rst | 10 +-- doc/services/orchestration/index.rst | 8 +- doc/services/queues/index.rst | 6 +- doc/services/volume/index.rst | 43 ++++++++++ doc/services/volume/snapshots.rst | 48 +++++++++++ doc/services/volume/volume-types.rst | 30 +++++++ doc/services/volume/volumes.rst | 79 +++++++++++++++++++ doc/url-types.rst | 2 + 22 files changed, 293 insertions(+), 88 deletions(-) create mode 100644 doc/services/common/clients.sample.rst delete mode 100644 doc/services/common/os-client.sample.rst rename doc/services/common/{rs-client.sample.rst => rs-client.rst} (58%) create mode 100644 doc/services/common/rs-only.sample.rst create mode 100644 doc/services/volume/snapshots.rst create mode 100644 doc/services/volume/volume-types.rst create mode 100644 doc/services/volume/volumes.rst create mode 100644 doc/url-types.rst diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst index d6ec512ac..1bfd20e68 100644 --- a/doc/services/autoscale/index.rst +++ b/doc/services/autoscale/index.rst @@ -1,12 +1,12 @@ Auto Scale v2 ============= -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +Auto Scale service +~~~~~~~~~~~~~~~~~~ -Now, set up the Auto Scale service: +Now to instantiate the Auto Scale service: .. code-block:: php diff --git a/doc/services/common/clients.sample.rst b/doc/services/common/clients.sample.rst new file mode 100644 index 000000000..a952da730 --- /dev/null +++ b/doc/services/common/clients.sample.rst @@ -0,0 +1,22 @@ +Setup +----- + +Rackspace setup +~~~~~~~~~~~~~~~ + +.. include:: /services/common/rs-client.rst + + +OpenStack setup +~~~~~~~~~~~~~~~ + +If you're an OpenStack user, you will also need to prove a few other +configuration parameters: + +.. code-block:: php + + $client = new OpenCloud\OpenStack('{keystoneUrl}', array( + 'username' => '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/services/common/os-client.sample.rst b/doc/services/common/os-client.sample.rst deleted file mode 100644 index b4614474d..000000000 --- a/doc/services/common/os-client.sample.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. code-block:: php - - '{username}', - 'password' => '{apiKey}', - 'tenantId' => '{tenantId}', - )); diff --git a/doc/services/common/rs-client.sample.rst b/doc/services/common/rs-client.rst similarity index 58% rename from doc/services/common/rs-client.sample.rst rename to doc/services/common/rs-client.rst index e33983270..46f9a5291 100644 --- a/doc/services/common/rs-client.sample.rst +++ b/doc/services/common/rs-client.rst @@ -1,12 +1,8 @@ -The first thing to do is pass in your credentials and instantiate a Rackspace -client: +The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key: .. code-block:: php - `_. +* ``{region}`` is the region the service will operate in. For Rackspace + users, you can select one of the following from the `supported regions page + `_. -``{urlType}`` is the type of URL to use, depending on what endpoints your -catalog provides. For Rackspace, you may use either ``internalURL`` or -``publicURL``. The former will execute HTTP transactions over the internal -network configured for your service, possibly reducing latency and the overall -bandwidth cost - the caveat is that all of your resources must be in same region. -``publicURL``, however, which is the default, will operate over the public -Internet and is to be used for multi-region installations. +* ``{urlType}`` is the `type of URL `_ to use, depending on which + endpoints your catalog provides. If omitted, it will default to the public + network. diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 207151d50..234517661 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -1,17 +1,12 @@ Compute v2 ========== -.. note:: +.. include:: ../common/clients.sample.rst - This is a joint service that supports both Rackspace Cloud Servers v2 API, and - OpenStack Nova v2 API. +Compute service +~~~~~~~~~~~~~~~ -Setup ------ - -.. include:: ../common/rs-client.sample.rst - -Now, set up the Compute service: +Now to instantiate the Compute service: .. code-block:: php diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index 850e56c8a..c330d4ac8 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -1,12 +1,12 @@ Databases v1 ============ -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Databases service +~~~~~~~~~~~~~~~~~ -Now, set up the Database service: +Now to instantiate the Databases service: .. code-block:: php diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index 4caca9fe5..99d19c5e8 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -1,12 +1,12 @@ DNS v1 ====== -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +DNS service +~~~~~~~~~~~ -Now, set up the DNS service: +Now to instantiate the DNS service: .. code-block:: php diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index 35c27131b..3eaa08de6 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -1,10 +1,12 @@ Identity v2 =========== -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample +Identity service +~~~~~~~~~~~~~~~~ + +Now to instantiate the Identity service: .. code-block:: php diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index d54b09ad0..c6451554b 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -1,10 +1,12 @@ Images v1 ========= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Images service +~~~~~~~~~~~~~~ + +Now to instantiate the Images service: .. code-block:: php diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index bf2b6dd0c..e2e4ca973 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -1,17 +1,13 @@ Load Balancer v1 ================ -.. note:: +.. include:: ../common/rs-only.sample.rst - This service is only available for Rackspace users. +Load Balancer service +~~~~~~~~~~~~~~~~~~~~~ -Setup ------ - -.. include:: ../common/rs-client.sample.rst - -Now, set up the Load Balancer service: +Now to instantiate the Load Balancer service: .. code-block:: php diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index df0597e8c..be38d1067 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -1,12 +1,12 @@ Monitoring v1 ============= -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +Monitoring service +~~~~~~~~~~~~~~~~~~ -Now, set up the Cloud Monitoring service: +Now to instantiate the Monitoring service: .. code-block:: php diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 92270fe86..89a159d4f 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -1,12 +1,12 @@ Networking v2 ============= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: rs-client.sample.rst +Networking service +~~~~~~~~~~~~~~~~~~ -Now, set up the Cloud Monitoring service: +Now to instantiate the Networking service: .. code-block:: php diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index 6b3a18b6f..8c50d7de0 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -1,12 +1,12 @@ Object Store v1 =============== -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Object Store service +~~~~~~~~~~~~~~~~~~~~ -Now, set up the Object Store service: +Now to instantiate the Object Store service: .. code-block:: php @@ -38,7 +38,7 @@ Glossary account is your portion of it. container - A storage compartment that provides a way for you to organize data. A + A storage compartment that provides a way for you to organize data. A container is similar to a folder in Windows or a directory in UNIX. The primary difference between a container and these other file system concepts is that containers cannot be nested. diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index 0dce8ac43..fcb5e46b9 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -1,12 +1,12 @@ Orchestration v1 ================ -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: rs-client.sample.rst +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ -Now set up the Orchestration service: +Now to instantiate the Orchestration service: .. code-block:: php diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index e37845d76..317257d65 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -1,10 +1,10 @@ Queues v1 ========= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Queues service +~~~~~~~~~~~~~~ Now to instantiate the Queues service: diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst index e69de29bb..7e318a5cc 100644 --- a/doc/services/volume/index.rst +++ b/doc/services/volume/index.rst @@ -0,0 +1,43 @@ +Volumes v1 +========== + +.. include:: ../common/clients.sample.rst + +Volume service +~~~~~~~~~~~~~~ + +Now to instantiate the Volume service: + +.. code-block:: php + + $service = $client->volumeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + volumes + volume-types + snapshots + + +Glossary +-------- + +.. glossary:: + + volume + A volume is a detachable block storage device. You can think of it as a USB + hard drive. It can only be attached to one instance at a time. + + volume type + Providers may support multiple types of volumes; at Rackspace, a volume + can either be ``SSD`` (solid state disk: expensive, high-performance) or + ``SATA`` (serial attached storage: regular disks, less expensive). + + snapshot + A snapshot is a point-in-time copy of the data contained in a volume. diff --git a/doc/services/volume/snapshots.rst b/doc/services/volume/snapshots.rst new file mode 100644 index 000000000..c2d8ef6cc --- /dev/null +++ b/doc/services/volume/snapshots.rst @@ -0,0 +1,48 @@ +Snapshots +========= + +Create a snapshot +----------------- + +A ``Snapshot`` object is created from the Cloud Block Storage service. +However, it is associated with a volume, and you must specify a volume +to create one: + +.. code-block:: php + + // New instance of OpenCloud\Volume\Resource\Snapshot + $snapshot = $service->snapshot(); + + // Send to API + $snapshot->create(array( + 'display_name' => 'Name that snapshot', + 'volume_id' => $volume->id + )); + + +List snapshots +-------------- + +.. code-block:: php + + $snapshots = $service->snapshotList(); + + foreach ($snapshots as $snapshot) { + /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ + } + + +To get details on a single snapshot +----------------------------------- + +.. code-block:: php + + $snapshot = $dallas->snapshot('{snapshotId}'); + + +To delete a snapshot +-------------------- + +.. code-block:: php + + $snapshot->delete(); diff --git a/doc/services/volume/volume-types.rst b/doc/services/volume/volume-types.rst new file mode 100644 index 000000000..ba3176df9 --- /dev/null +++ b/doc/services/volume/volume-types.rst @@ -0,0 +1,30 @@ +Volume Types +============ + +List volume types +----------------- + +.. code-block:: php + + $volumeTypes = $service->volumeTypeList(); + + foreach ($volumeTypes as $volumeType) { + /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ + } + + +Describe a volume type +---------------------- + +If you know the ID of a volume type, use the ``volumeType`` method to +retrieve information on it: + +.. code-block:: php + + $volumeType = $service->volumeType(1); + +A volume type has three attributes: + +- ``id`` the volume type identifier +- ``name`` its name +- ``extra_specs`` additional information for the provider diff --git a/doc/services/volume/volumes.rst b/doc/services/volume/volumes.rst new file mode 100644 index 000000000..6a35bb135 --- /dev/null +++ b/doc/services/volume/volumes.rst @@ -0,0 +1,79 @@ +Volumes +======= + +Create a volume +--------------- + +To create a volume, you must specify its size (in gigabytes). All other +parameters are optional: + +.. code-block:: php + + // Create instance of OpenCloud\Volume\Resource\Volume + $volume = $service->volume(); + + $volume->create(array( + 'size' => 200, + 'volume_type' => $service->volumeType(''), + 'display_name' => 'My Volume', + 'display_description' => 'Used for large object storage' + )); + +List volumes +------------ + +.. code-block:: php + + $volumes = $service->volumeList(); + + foreach ($volumes as $volume) { + /** @param $volumeType OpenCloud\Volume\Resource\Volume */ + } + + +Get details on a single volume +------------------------------ + +If you specify an ID on the ``volume()`` method, it retrieves +information on the specified volume: + +.. code-block:: php + + $volume = $dallas->volume(''); + echo $volume->size; + + +To delete a volume +------------------ + +.. code-block:: php + + $volume->delete(); + + +Attach a volume to a server +--------------------------- + +.. code-block:: php + + // retrieve server + $computeService = $client->computeService('{catalogName}', '{region}'); + $server = $computeService->server('{serverId}'); + + // attach volume + $server->attachVolume($volume, '{mountPoint}') + +The ``{mountPoint}`` is the location on the server on which to mount +the volume (usually ``/dev/xvhdd`` or similar). You can also supply +``'auto'`` as the mount point, in which case the mount point will be +automatically selected for you. ``auto`` is the default value for +``{mountPoint}``, so you do not actually need to supply anything for +that parameter. + + +Detach a volume from a server +----------------------------- + +.. code-block:: php + + $server->detachVolume($volume); diff --git a/doc/url-types.rst b/doc/url-types.rst new file mode 100644 index 000000000..b0e2cc0ea --- /dev/null +++ b/doc/url-types.rst @@ -0,0 +1,2 @@ +URL types +========= From f9d236de3ff6e80dfeabfea38ab57be7dc5cbfa0 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 11:43:03 +0100 Subject: [PATCH 595/835] Refactor root pages and index page --- doc/caching-creds.rst | 36 ++++++++++----------- doc/debugging.rst | 60 +++++++++++++++++++---------------- doc/index.rst | 74 ++++++++++++++++++++++++++++++++++++------- doc/iterators.rst | 18 ++++------- doc/regions.rst | 7 +++- doc/url-types.rst | 13 ++++++++ 6 files changed, 138 insertions(+), 70 deletions(-) diff --git a/doc/caching-creds.rst b/doc/caching-creds.rst index 3c3e8df23..4eb5be5c8 100644 --- a/doc/caching-creds.rst +++ b/doc/caching-creds.rst @@ -16,29 +16,29 @@ Filesystem example In this example, credentials will be saved to a file in the local filesystem. Be sure to exclude it from your VCS. -.. code:: php +.. code-block:: php - use OpenCloud\Rackspace; + use OpenCloud\Rackspace; - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' - )); + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); - $cacheFile = __DIR__ . '/.opencloud_token'; + $cacheFile = __DIR__ . '/.opencloud_token'; - // If the cache file exists, try importing it into the client - if (file_exists($cacheFile)) { - $data = unserialize(file_get_contents($cacheFile)); - $client->importCredentials($data); - } + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } - $token = $client->getTokenObject(); + $token = $client->getTokenObject(); - // If no token exists, or the current token is expired, re-authenticate and save the new token to disk - if (!$token || ($token && $token->hasExpired())) { - $client->authenticate(); - file_put_contents($cacheFile, serialize($client->exportCredentials())); - } + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/debugging.rst b/doc/debugging.rst index 445915284..5d35d5bc1 100644 --- a/doc/debugging.rst +++ b/doc/debugging.rst @@ -36,38 +36,40 @@ If you're trying to retrieve a Swift resource, such as a Data Object, and you're not completely certain that it exists, it makes sense to wrap your call in a try/catch block: -.. code:: php +.. code-block:: php - use Guzzle\Http\Exception\ClientErrorResponseException; + use Guzzle\Http\Exception\ClientErrorResponseException; - try { - return $service->getObject('foo.jpg'); - } catch (ClientErrorResponseException $e) { - // Okay, the resource probably does not exist + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + // Okay, the resource does not exist return false; - } catch (\Exception $e) { - // Some other exception was thrown, probably critical - $this->logException($e); - $this->alertDevs(); - } + } + } catch (\Exception $e) { + // Some other exception was thrown... + } + Both ``ClientErrorResponseException`` and ``ServerErrorResponseException`` have two methods that allow you to access the HTTP transaction: -.. code:: php +.. code-block:: php + + // Find out the faulty request + $request = $e->getRequest(); - // Find out the faulty request - $request = $e->getRequest(); + // Display everything by casting as string + echo (string) $request; - // Display everything by casting as string - echo (string) $request; + // Find out the HTTP response + $response = $e->getResponse(); - // Find out the HTTP response - $response = $e->getResponse(); + // Output that too + echo (string) $response; - // Output that too - echo (string) $response; Strategy 2: Wire logging ------------------------ @@ -80,20 +82,22 @@ don't know what's going on. Here's how you enable it: Install the plugin -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ -.. code:: bash +.. code-block:: bash + + composer require guzzle/guzzle - php composer.phar require guzzle/plugin-log:~3.8 Add to your client -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php -.. code:: php + use Guzzle\Plugin\Log\LogPlugin; - use Guzzle\Plugin\Log\LogPlugin; + $client->addSubscriber(LogPlugin::getDebugPlugin()); - $client->addSubscriber(LogPlugin::getDebugPlugin()); The above will add a generic logging subscriber to your client, which -will be notified every time a relevant HTTP event is fired off. +will output every HTTP transaction to `STDOUT`. diff --git a/doc/index.rst b/doc/index.rst index 9c06b86e8..8a211dab4 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,12 +1,37 @@ -.. php-opencloud documentation master file, created by - sphinx-quickstart on Tue Mar 3 12:28:19 2015. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +Welcome to php-opencloud! +========================= -Welcome to php-opencloud's documentation! -========================================= +Installation +------------ -Contents: +You must install this library through Composer: + +.. code-block:: bash + + composer require rackspace/php-opencloud + + +If you do not have Composer installed, please consult the `official docs +`_. + +Once you have installed the library, you will need to load Composer's autoloader +(which registers all the required namespaces). To do this, place the following +line of PHP code at the top of your application's PHP files: + +.. code-block:: php + + require 'vendor/autoload.php'; + +This assumes your application's PHP files are located in the same folder as +``vendor/``. If your files are located elsewhere, please supply the path to +``vendor/autoload.php`` in the require statement above. + +Read the `OpenStack Getting Started guide `_ +or `Rackspace Getting Started guide `_ to help +you get started with basic Compute operations. + +Services +-------- .. toctree:: :glob: @@ -14,10 +39,35 @@ Contents: services/**/index +Usage tips +---------- + +.. toctree:: + :maxdepth: 1 + + debugging + caching-creds + iterators + regions + url-types + +Help and support +---------------- + +If you have specific problems or bugs with this SDK, please file an issue on +our official `Github `_. We also +have a `mailing list `_, +so feel free to join to keep up to date with all the latest changes and +announcements to the library. + +For general feedback and support requests, send an email to +sdk-support@rackspace.com. + +You can also find assistance via IRC on #rackspace at freenode.net. -Indices and tables -================== +Contributing +------------ -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +If you'd like to contribute to the project, or require help running the +unit/acceptance tests, please view the `contributing guidelines +`_. diff --git a/doc/iterators.rst b/doc/iterators.rst index e5d3c198d..bef59e339 100644 --- a/doc/iterators.rst +++ b/doc/iterators.rst @@ -1,9 +1,6 @@ Iterators ========= -Intro ------ - Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: @@ -27,27 +24,27 @@ the SDK: Common behaviour ---------------- -.. code:: php +.. code-block:: php $iterator = $computeService->flavorList(); There are two ways to traverse an iterator. The first is the longer, more traditional way: -.. code:: php +.. code-block:: php while ($iterator->valid()) { $flavor = $iterator->current(); - + // do stuff.. echo $flavor->id; - + $iterator->next(); } There is also a shorter and more intuitive version: -.. code:: php +.. code-block:: php foreach ($iterator as $flavor) { // do stuff... @@ -63,7 +60,7 @@ Very important note Until now, users have been expected to do this: -.. code:: php +.. code-block:: php while ($flavor = $iterator->next()) { // ... @@ -119,7 +116,7 @@ precedence. Setting up a PaginatedIterator ------------------------------ -.. code:: php +.. code-block:: php use OpenCloud\Common\Collection\PaginatedIterator; @@ -175,4 +172,3 @@ needs to work. These are: +-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ | request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | +-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ - diff --git a/doc/regions.rst b/doc/regions.rst index 82edf28ec..42c14595c 100644 --- a/doc/regions.rst +++ b/doc/regions.rst @@ -3,13 +3,18 @@ Rackspace regions Below are the supported regions on the Rackspace network: -+======+===========+ ++------+-----------+ | code | location | +======+===========+ | IAD | Virginia | ++------+-----------+ | ORD | Chicago | ++------+-----------+ | DFW | Dallas | ++------+-----------+ | LON | London | ++------+-----------+ | SYD | Sydney | ++------+-----------+ | HKG | Hong Kong | +------+-----------+ diff --git a/doc/url-types.rst b/doc/url-types.rst index b0e2cc0ea..26a0410bd 100644 --- a/doc/url-types.rst +++ b/doc/url-types.rst @@ -1,2 +1,15 @@ URL types ========= + +internalURL +----------- + +An internal URL is a URL that is accessible only from within the Rackspace +Cloud network. Access to an internal URL is always free of charge. + + +publicURL +--------- + +A public URL is a URL that is accessible from anywhere. Access to a public URL +usually incurs traffic charges. From 75aab24392ec826354ab12530db66b5b20b2791f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 12:29:29 +0100 Subject: [PATCH 596/835] Add final links --- doc/services/compute/index.rst | 8 +++- doc/services/database/index.rst | 8 ++++ doc/services/dns/index.rst | 8 ++++ doc/services/identity/index.rst | 7 +++ doc/services/image/index.rst | 7 +++ doc/services/load-balancer/index.rst | 9 +++- doc/services/monitoring/index.rst | 65 +++++++++++++++++++++++++++- doc/services/networking/index.rst | 8 ++++ doc/services/object-store/index.rst | 8 ++++ doc/services/orchestration/index.rst | 9 ++++ doc/services/queues/index.rst | 8 ++++ doc/services/volume/index.rst | 8 ++++ 12 files changed, 150 insertions(+), 3 deletions(-) diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 234517661..ee27bce8e 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -46,4 +46,10 @@ Glossary server A server is a virtual machine instance in the Cloud Servers environment. - keypair + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index c330d4ac8..32888e27d 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -67,3 +67,11 @@ Glossary for increased performance, scalability, availability and manageability. Applications with high I/O demands are performance optimized and data is protected through both local and network RAID-10. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index 99d19c5e8..39db3d675 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -49,3 +49,11 @@ Glossary DNS usually determines an IP address associated with a domain name. Reverse DNS is the opposite process: resolving a domain name from an IP address. This is usually achieved with a domain name pointer. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index 3eaa08de6..6cb4dfdf4 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -46,3 +46,10 @@ Glossary access the requested resources. Users may be directly assigned to a particular tenant and behave as if they are contained within that tenant. + + +Further Links +------------- + +- `Quickstart for the API `_ +- `API Developer Guide `_ diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index c6451554b..a7331b430 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -41,3 +41,10 @@ Glossary tag An image tag is a string of characters used to identify a specific image or images. + + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index e2e4ca973..9ea7614ad 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -21,7 +21,7 @@ Operations .. toctree:: - load-balancers + load-balancer nodes virtual-ips access @@ -87,3 +87,10 @@ Glossary (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP address (``SERVICENET``), routable only within the region in which the load balancer resides. + + +Further Links +------------- + +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index be38d1067..0a7225e36 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -29,6 +29,69 @@ Operations views zones - Glossary -------- + +.. glossary:: + + agent + A monitoring daemon that resides on the server being monitored. The agent + gathers metrics based on agent checks and pushes them to Cloud Monitoring. + The agent provides insight into your servers with checks for information + such as load average and network usage. The agent acts as a single small + service that runs scheduled checks and pushes metrics to the rest of Cloud + Monitoring so the metrics can be analyzed, trigger alerts, and be archived. + These metrics are gathered via checks using agent check types, and can be + used with the other Cloud Monitoring primitives such as alarms. + + agent token + An authentication token used to identify the agent when it communicates + with Cloud Monitoring. + + alarm + An alarm contains a set of rules that determine when the monitoring system + sends a notification. You can create multiple alarms for the different + checks types associated with an entity. For example, if your entity is a + web server that hosts your company's website, you can create one alarm to + monitor the server itself, and another alarm to monitor the website. + + check + Checks explicitly specify how you want to monitor an entity. Once you've + created an entity, you can configure one or more checks for it. A check is + the foundational building block of the monitoring system, and is always + associated with an entity. The check specifies the parts or pieces of the + entity that you want to monitor, the monitoring frequency, how many + monitoring zones are launching the check, and so on. It contains the + specific details of how you are monitoring the entity. + + entity + The object or resource that you want to monitor. It can be any object or + device that you want to monitor. It's commonly a web server, but it might + also be a website, a web page or a web service. + + monitoring zone + A monitoring zone is the "launch point" of a check. When you create a + check, you specify which monitoring zone(s) you want to launch the check + from. This concept of a monitoring zone is similar to that of a datacenter, + however in the monitoring system, you can think of it more as a geographical + region. + + notification + A notification is an informational message sent to one or more addresses + by the monitoring system when an alarm is triggered. You can set up + notifications to alert a single individual or an entire team. Rackspace + Cloud Monitoring currently supports webhooks and email for sending + notifications. + + notification plan + A notification plan contains a set of notification rules to execute when an + alarm is triggered. A notification plan can contain multiple notifications + for each of the following states: + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 89a159d4f..515f9ddce 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -48,3 +48,11 @@ Glossary When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index 8c50d7de0..fcdc5aff2 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -55,3 +55,11 @@ Glossary object An object (sometimes referred to as a file) is the unit of storage in an Object Store. An object is a combination of content (data) and metadata. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index fcb5e46b9..bae5ec6aa 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -25,6 +25,7 @@ Operations resources resource-types build-info + events Glossary @@ -48,3 +49,11 @@ Glossary stack A stack is a running instance of a template. When a stack is created, the resources specified in the template are created. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index 317257d65..4175df760 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -46,3 +46,11 @@ Glossary producer or publisher sends to the queue. A message exists until it is deleted by a recipient or automatically by the system based on a TTL (time-to-live) value. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst index 7e318a5cc..bee589086 100644 --- a/doc/services/volume/index.rst +++ b/doc/services/volume/index.rst @@ -41,3 +41,11 @@ Glossary snapshot A snapshot is a point-in-time copy of the data contained in a volume. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ From 2bdcd81942f515329ccd2f57179b06a0abdc6387 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 13:05:09 +0100 Subject: [PATCH 597/835] Add GS guides --- doc/index.rst | 4 ++-- doc/services/load-balancer/lb-setup.sample.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 8a211dab4..297a64c6c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,8 +26,8 @@ This assumes your application's PHP files are located in the same folder as ``vendor/``. If your files are located elsewhere, please supply the path to ``vendor/autoload.php`` in the require statement above. -Read the `OpenStack Getting Started guide `_ -or `Rackspace Getting Started guide `_ to help +Read the `OpenStack Getting Started guide `_ +or `Rackspace Getting Started guide `_ to help you get started with basic Compute operations. Services diff --git a/doc/services/load-balancer/lb-setup.sample.rst b/doc/services/load-balancer/lb-setup.sample.rst index 96e228b8d..d82c80a1f 100644 --- a/doc/services/load-balancer/lb-setup.sample.rst +++ b/doc/services/load-balancer/lb-setup.sample.rst @@ -6,4 +6,4 @@ load balancer, like so: .. code-block:: php - $loadBalancer = $service->loadBalancer('{id}'); + $loadBalancer = $service->loadBalancer('{id}'); From b909cf5922f835ddb269e9225e7e88c10e5c9c20 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 13:22:42 +0100 Subject: [PATCH 598/835] Tweak GS guides --- doc/getting-started-with-openstack.rst | 233 +++++++++++++++++++++++++ doc/getting-started-with-rackspace.rst | 185 ++++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 doc/getting-started-with-openstack.rst create mode 100644 doc/getting-started-with-rackspace.rst diff --git a/doc/getting-started-with-openstack.rst b/doc/getting-started-with-openstack.rst new file mode 100644 index 000000000..da6c6ee24 --- /dev/null +++ b/doc/getting-started-with-openstack.rst @@ -0,0 +1,233 @@ +Getting Started with OpenStack +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Nova instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against Keystone: + +.. code-block:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( + 'username' => 'foo', + 'password' => 'bar', + 'tenantName' => 'baz' + )); + +You will need to substitute in the public URL endpoint for your Keystone +service, as well as your ``username``, ``password`` and ``tenantName``. +You can also specify your ``tenantId`` instead of ``tenantName`` if you +prefer. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Nova service: + +.. code-block:: php + + $compute = $client->computeService('nova', 'regionOne'); + + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. For OpenStack users, this must be retrieved +and entered in your code. If you are unsure how to retrieve the service +name, follow these steps: + +1. Setup the ``$client`` object, as above +2. Copy and run this code: + +.. code-block:: php + + $client->authenticate(); + print_r($client->getCatalog()->getItems()); + + +3. This will output all the items in your service catalog. Go through + the outputted list and find your service, making note of the "name" + field. This is the name you will need to enter as the first argument. + You will also be able to see the available regions. + +The second argument is the region. The third and last argument is the +type of URL; you may use either ``publicURL`` or ``internalURL``. + + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instances are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of an instance is its +hardware configuration. So if you want a 2GB instance but don't know the +ID, you have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + + use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +As you can see, you're creating a server called "My lovely server" - +this will take a few minutes for the build to complete. You can always +check the progress by logging into your Controller node and running: + +.. code-block:: bash + + nova list + +You can also execute a polling function immediately after the ``create`` +method that checks the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +More fun with Nova +------------------ + +Once you've booted up your instance, you can use other API operations to +monitor your Compute nodes. To list every node on record, you can +execute: + +.. code-block:: php + + $servers = $compute->serverList(); + + foreach ($servers as $server) { + // do something with each server... + echo $server->name, PHP_EOL; + } + +or, if you know a particular instance ID you can retrieve its details: + +.. code-block:: php + + $server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); + +allowing you to update its properties: + +.. code-block:: php + + $server->update(array( + 'name' => 'New server name' + )); + +or delete it entirely: + +.. code-block:: php + + $server->delete(); + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. diff --git a/doc/getting-started-with-rackspace.rst b/doc/getting-started-with-rackspace.rst new file mode 100644 index 000000000..35317a3e6 --- /dev/null +++ b/doc/getting-started-with-rackspace.rst @@ -0,0 +1,185 @@ +Getting Started with Rackspace +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Cloud Servers instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against the Rackspace API and use its services: + +.. code-block:: php + + 'foo', + 'apiKey' => 'bar' + )); + +You can see in the first example that the constant +``Rackspace::US_IDENTITY_ENDPOINT`` is just a string representation of +Rackspace's identity endpoint +(``https://identity.api.rackspacecloud.com/v2.0/``). Another difference +is that Rackspace uses API key for authentication, whereas OpenStack +uses a generic password. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Compute (Nova) service: + +.. code-block:: php + + $compute = $client->computeService(null, 'ORD'); + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. If in doubt, you can leave blank and it will +revert to the default name for the service. The second argument is the +region; you may use: + +- **DFW** (Dallas) +- **ORD** (Chicago) +- **IAD** (Virginia) +- **LON** (London) +- **HKG** (Hong Kong) +- **SYD** (Sydney) + +The third and last argument is the type of URL; you may use either +``publicURL`` or ``internalURL``. If you select ``internalUrl`` all API +traffic will use ServiceNet (internal IPs) and will receive a +performance boost. + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Servers are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of a server is its hardware +configuration. So if you want a 2GB instance but don't know the ID, you +have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + +use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +You can also call a polling function that checks on the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. From c9423b2701b78738cc2a6d0b5d53aa721a1e1556 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:15:29 -0800 Subject: [PATCH 599/835] Adding descriptions to docblocks. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 ++ lib/OpenCloud/Networking/Resource/SecurityGroupRule.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index 2892f5471..b4a8e08d2 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,6 +20,8 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * A security group is a named container for security group rules, each of which is + * represented by {@see \OpenCloud\Networking\Resource\SecurityGroupRule}. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php index 5527ce272..06ad028b2 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -20,6 +20,10 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * + * Security group rules provide users the ability to specify the types of traffic that are allowed + * to pass through to and from ports (represented by {@see \OpenCloud\Networking\Resource\Port}) + * on a virtual server instance. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * From 4591aba4ecadb04199332bd7b011d008cffc65f3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:32:30 -0800 Subject: [PATCH 600/835] Fixing typo. --- docs/userguide/Networking/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index bfa1a217c..13342977f 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -65,9 +65,9 @@ be assigned to the interfaces plugged into them. When IP addresses are associated with a port, this also implies the port is associated with a subnet because the IP address is taken from the allocation pool for a specific subnet. -* **Security Group**: TODO +* **Security Group**: A named container for security group rules. -* **Security Group Rule**: TODO +* **Security Group Rule**: Provide users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. ## Prerequisites From be70e03fdaf0766d981cc67b45450116c4b526fc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:32:38 -0800 Subject: [PATCH 601/835] Adding descriptions for Security Groups and Security Group Rules sections. --- docs/userguide/Networking/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 13342977f..5a7beb07f 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -485,7 +485,7 @@ $port->delete(); ## Security Groups -TODO: Add description +A security group is a named container for [security group rules](#security-group-rules). ### Create a security group @@ -546,7 +546,7 @@ $securityGroup->delete(); ## Security Group Rules -TODO: Add description +A security group rule provides users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. ### Create a security group rule From db2e6d6f1be975751cb72cc95da739e9dcbd9768 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:51:36 -0800 Subject: [PATCH 602/835] Moving @see annotations to their own lines. --- lib/OpenCloud/Networking/Service.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 12bb7477f..e3fcc879e 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -51,8 +51,10 @@ public function network($id = null) /** * Creates a new Network and returns it. * - * @param array $params Network creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network + * @param array $params Network creation parameters. * @return \OpenCloud\Networking\Resource\Network Object representing created network + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network */ public function createNetwork(array $params = array()) { @@ -136,8 +138,10 @@ public function subnet($id = null) /** * Creates a new Subnet and returns it. * - * @param array $params Subnet creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet + * @param array $params Subnet creation parameters. * @return \OpenCloud\Networking\Resource\Subnet Object representing created subnet + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet */ public function createSubnet(array $params = array()) { @@ -221,8 +225,10 @@ public function port($id = null) /** * Creates a new Port and returns it. * - * @param array $params Port creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port + * @param array $params Port creation parameters. * @return \OpenCloud\Networking\Resource\Port Object representing created port + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port */ public function createPort(array $params = array()) { @@ -306,8 +312,10 @@ public function securityGroup($id = null) /** * Creates a new SecurityGroup and returns it. * - * @param array $params SecurityGroup creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group + * @param array $params SecurityGroup creation parameters. * @return \OpenCloud\Networking\Resource\SecurityGroup Object representing created security group + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group */ public function createSecurityGroup(array $params = array()) { @@ -355,8 +363,10 @@ public function securityGroupRule($id = null) /** * Creates a new SecurityGroupRule and returns it. * - * @param array $params SecurityGroupRule creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule + * @param array $params SecurityGroupRule creation parameters. * @return \OpenCloud\Networking\Resource\SecurityGroupRule Object representing created security group rule + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule */ public function createSecurityGroupRule(array $params = array()) { From 458f60324617deeb2a3e19ee1ed23d77e3e6017c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:53:56 -0800 Subject: [PATCH 603/835] Appeasing the PSR-2 linter. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index b4a8e08d2..4b2f85efd 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * A security group is a named container for security group rules, each of which is + * A security group is a named container for security group rules, each of which is * represented by {@see \OpenCloud\Networking\Resource\SecurityGroupRule}. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups From ee5eb6fc5c68ee1496d2c7984136793f4ac6863e Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Fri, 6 Mar 2015 17:29:07 -0600 Subject: [PATCH 604/835] Swap support email address for support URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42948e8c3..6031cf4ac 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Your feedback is appreciated! If you have specific problems or bugs with this SD also have a [mailing list](https://groups.google.com/forum/#!forum/php-opencloud), so feel free to join to keep up to date with all the latest changes and announcements to the library. -For general feedback and support requests, send an email to sdk-support@rackspace.com. +For general feedback and support requests, contact us at https://developer.rackspace.com/support/ You can also find assistance via IRC on #rackspace at freenode.net. From 9d41771d664a7bb36810e915def1fb09ddb61422 Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Fri, 6 Mar 2015 17:30:33 -0600 Subject: [PATCH 605/835] Swap support email address for support URL --- docs/getting-started-openstack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index b00e58693..6bb1ae260 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -198,4 +198,4 @@ $server->delete(); Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or [Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to e-mail sdk-support@rackspace.com or open a Github issue with details. +troubles, feel free to contact us at https://developer.rackspace.com/support/ or open a Github issue with details. From 8c6e2365c38ff5ef10df00d1d1c864d9458a82bf Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:34:55 +0100 Subject: [PATCH 606/835] Use full paths for downloadable samples --- doc/services/database/configurations.rst | 14 +- doc/services/database/datastores.rst | 8 +- doc/services/database/instances.rst | 4 +- doc/services/networking/networks.rst | 12 +- doc/services/networking/ports.rst | 12 +- doc/services/networking/subnets.rst | 12 +- doc/services/orchestration/build-info.rst | 2 +- doc/services/orchestration/events.rst | 6 +- doc/services/orchestration/resource-types.rst | 6 +- doc/services/orchestration/resources.rst | 6 +- doc/services/orchestration/stacks.rst | 24 +- doc/services/orchestration/stacks.rst-e | 299 ++++++++++++++++++ doc/services/orchestration/templates.rst | 4 +- 13 files changed, 354 insertions(+), 55 deletions(-) create mode 100644 doc/services/orchestration/stacks.rst-e diff --git a/doc/services/database/configurations.rst b/doc/services/database/configurations.rst index edfb9dc89..f50619914 100644 --- a/doc/services/database/configurations.rst +++ b/doc/services/database/configurations.rst @@ -22,7 +22,7 @@ Creating a configuration ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Listing configurations @@ -37,7 +37,7 @@ You can list out all the configurations you have created as shown below: /** @var $configuration OpenCloud\Database\Resource\Configuration **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a configuration @@ -50,7 +50,7 @@ You can retrieve a specific configuration, using its ID, as shown below: $configuration = $service->configuration('{configId}'); /** @var OpenCloud\Database\Resource\Configuration **/ -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Updating a configuration @@ -77,7 +77,7 @@ You can patch a configuration as shown below: ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Replacing a configuration @@ -94,7 +94,7 @@ You can replace a configuration as shown below: ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Deleting a configuration @@ -104,7 +104,7 @@ Deleting a configuration $configuration->delete(); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ .. note:: @@ -124,4 +124,4 @@ as shown below: /** @var $instance OpenCloud\Database\Resource\Instance **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/datastores.rst b/doc/services/database/datastores.rst index 6ca45daed..ad6d90e85 100644 --- a/doc/services/database/datastores.rst +++ b/doc/services/database/datastores.rst @@ -13,7 +13,7 @@ You can list out all the datastores available as shown below: /** @var $datastore OpenCloud\Database\Resource\Datastore **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a datastore @@ -27,7 +27,7 @@ shown below: /** @var OpenCloud\Database\Resource\Datastore **/ $datastore = $service->datastore('{datastoreId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Listing datastore versions @@ -43,7 +43,7 @@ shown below: /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a datastore version @@ -56,4 +56,4 @@ below: $datastoreVersion = $datastore->version('{versionId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/instances.rst b/doc/services/database/instances.rst index 082e56c05..6f3afc1e0 100644 --- a/doc/services/database/instances.rst +++ b/doc/services/database/instances.rst @@ -16,7 +16,7 @@ Create a new instance 'volume' => array('size' => 4) // 4GB of volume disk )); -`Get the executable PHP script for this sample `__ +`Get the executable PHP script for this sample `__ Waiting for the instance to build @@ -96,7 +96,7 @@ Retrieving an instance $instance = $service->instance('{instanceId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Updating an instance diff --git a/doc/services/networking/networks.rst b/doc/services/networking/networks.rst index a290a34e7..b95d44eba 100644 --- a/doc/services/networking/networks.rst +++ b/doc/services/networking/networks.rst @@ -28,7 +28,7 @@ You can create a network as shown in the following example: 'name' => 'My private backend network' )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Create multiple networks @@ -55,7 +55,7 @@ You can create multiple networks as shown in the following example: /** @var $network OpenCloud\Networking\Resource\Network **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List networks ------------- @@ -72,7 +72,7 @@ following example: } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a network @@ -86,7 +86,7 @@ in the following example: /** @var $network OpenCloud\Networking\Resource\Network **/ $network = $networkingService->getNetwork('{networkId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a network @@ -113,7 +113,7 @@ You can update a network as shown in the following example: 'name' => 'My updated private backend network' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a network @@ -125,4 +125,4 @@ You can delete a network as shown in the following example: $network->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst index 06d2ec2cf..8eeb07bf0 100644 --- a/doc/services/networking/ports.rst +++ b/doc/services/networking/ports.rst @@ -39,7 +39,7 @@ You can create a port as shown in the following example: )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create multiple ports @@ -68,7 +68,7 @@ You can create multiple ports as shown in the following example: /** @var $port OpenCloud\Networking\Resource\Port **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List ports @@ -84,7 +84,7 @@ You can list all the ports to which you have access as shown in the following ex /** @var $port OpenCloud\Networking\Resource\Port **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a port @@ -98,7 +98,7 @@ the following example: /** @var $port OpenCloud\Networking\Resource\Port **/ $port = $networkingService->getPort('{portId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a port @@ -135,7 +135,7 @@ You can update a port as shown in the following example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a port @@ -147,4 +147,4 @@ You can delete a port as shown in the following example: $port->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/subnets.rst b/doc/services/networking/subnets.rst index e8d6951a0..9a6c2a7b4 100644 --- a/doc/services/networking/subnets.rst +++ b/doc/services/networking/subnets.rst @@ -42,7 +42,7 @@ You can create a subnet as shown in the following example: 'cidr' => '192.168.199.0/25' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create multiple subnets @@ -69,7 +69,7 @@ You can create multiple subnets as shown in the following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List subnets @@ -85,7 +85,7 @@ following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a subnet @@ -99,7 +99,7 @@ in the following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ $subnet = $networkingService->getSubnet('{subnetId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a subnet @@ -137,7 +137,7 @@ You can update a subnet as shown in the following example: 'gatewayIp' => '192.168.62.155' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a subnet @@ -149,4 +149,4 @@ You can delete a subnet as shown in the following example: $subnet->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/build-info.rst b/doc/services/orchestration/build-info.rst index c30ce5cfa..b4016bab2 100644 --- a/doc/services/orchestration/build-info.rst +++ b/doc/services/orchestration/build-info.rst @@ -12,4 +12,4 @@ build as shown in the following example: /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ $buildInfo = $orchestrationService->getBuildInfo(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/events.rst b/doc/services/orchestration/events.rst index 68dbf611b..bceec0f5a 100644 --- a/doc/services/orchestration/events.rst +++ b/doc/services/orchestration/events.rst @@ -19,7 +19,7 @@ shown in the following example: /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List stack resource events @@ -36,7 +36,7 @@ shown in the following example: /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource event @@ -50,4 +50,4 @@ using the resource event's ID, as shown in the following example: /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resource-types.rst b/doc/services/orchestration/resource-types.rst index cd229d660..caa58b013 100644 --- a/doc/services/orchestration/resource-types.rst +++ b/doc/services/orchestration/resource-types.rst @@ -17,7 +17,7 @@ example: /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get resource type @@ -31,7 +31,7 @@ following example: /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get resource type template @@ -45,4 +45,4 @@ appear in a template, as shown in the following example: /** @var $resourceTypeTemplate string **/ $resourceTypeTemplate = $resourceType->getTemplate(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resources.rst b/doc/services/orchestration/resources.rst index 049150f58..b04115122 100644 --- a/doc/services/orchestration/resources.rst +++ b/doc/services/orchestration/resources.rst @@ -19,7 +19,7 @@ example: /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource @@ -33,7 +33,7 @@ name, as shown in the following example: /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ $resource = $stack->getResource('load-balancer'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource metadata @@ -47,4 +47,4 @@ shown in the following example: /** @var $resourceMetadata \stdClass **/ $resourceMetadata = $resource->getMetadata(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst b/doc/services/orchestration/stacks.rst index 75a14cd4c..29b6c4f7f 100644 --- a/doc/services/orchestration/stacks.rst +++ b/doc/services/orchestration/stacks.rst @@ -46,7 +46,7 @@ example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Preview a stack from a template URL @@ -68,7 +68,7 @@ in the following example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create stack @@ -111,7 +111,7 @@ example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create a stack from a template URL @@ -134,7 +134,7 @@ in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List stacks ----------- @@ -149,7 +149,7 @@ following example: /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack @@ -163,7 +163,7 @@ following example: /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->getStack('simple-lamp-setup'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack template @@ -178,7 +178,7 @@ used to create the stack. /** @var $stackTemplate string **/ $stackTemplate = $stack->getTemplate(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update stack @@ -221,7 +221,7 @@ example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update Stack from Template URL @@ -243,7 +243,7 @@ in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete stack @@ -256,7 +256,7 @@ stack *and* the resources as shown in the following example: $stack->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Abandon Stack @@ -277,7 +277,7 @@ abandon the stack as shown in the following example: $abandonStackData = $stack->abandon(); file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Adopt stack @@ -296,4 +296,4 @@ shown in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst-e b/doc/services/orchestration/stacks.rst-e new file mode 100644 index 000000000..29b6c4f7f --- /dev/null +++ b/doc/services/orchestration/stacks.rst-e @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/templates.rst b/doc/services/orchestration/templates.rst index 5eca2519d..24bc896bc 100644 --- a/doc/services/orchestration/templates.rst +++ b/doc/services/orchestration/templates.rst @@ -31,7 +31,7 @@ file, you can validate it as shown in the following example: // Use $e->getMessage() for explanation of why template is invalid } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Validate Template from URL ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,4 +52,4 @@ following example: // Use $e->getMessage() for explanation of why template is invalid } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ From 91d01c8bc4225615d1bfefb54a08021d6bf2cff7 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:37:13 +0100 Subject: [PATCH 607/835] Remove MD links --- doc/services/identity/roles.rst | 57 ++++++++++++--------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/doc/services/identity/roles.rst b/doc/services/identity/roles.rst index 96a56488f..09f0600fa 100644 --- a/doc/services/identity/roles.rst +++ b/doc/services/identity/roles.rst @@ -1,9 +1,6 @@ Roles ===== -Intro ------ - A role is a personality that a user assumes when performing a specific set of operations. A role includes a set of rights and privileges. A user assuming a role inherits the rights and privileges associated with @@ -13,12 +10,6 @@ determines how to interpret a user's roles. A role that grants access to a list of operations or resources within one service may grant access to a completely different list when interpreted by a different service. -Setup ------ - -Role objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - Useful object properties/methods -------------------------------- @@ -37,16 +28,13 @@ List roles This call lists the global roles available within a specified service. -.. code:: php - - $roles = $service->getRoles(); +.. code-block:: php - foreach ($roles as $role) { - // ... - } + $roles = $service->getRoles(); -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. + foreach ($roles as $role) { + // ... + } Get role -------- @@ -54,39 +42,36 @@ Get role This call lists detailed information (id, name, description) for a specified role. -.. code:: php +.. code-block:: php + + $role = $service->getRole('{roleId}'); - $roleId = '123abc'; - $role = $service->getRole($roleId); Add/delete user roles --------------------- -To add/remove user roles, you must first instantiate a -`user `__ object: +.. code-block:: php + + $user = $service->getUser('{userId}'); -.. code:: php + $roleId = '{roleId}'; - $roleId = '123abc'; + // add role to user + $user->addRole($roleId); - // add role to user - $user->addRole($roleId); + // remove role from user + $user->removeRole($roleId); - // remove role from user - $user->removeRole($roleId); List user global roles ---------------------- This call returns a list of global roles associated with a user: -.. code:: php - - $roles = $user->getRoles(); +.. code-block:: php - foreach ($roles as $role) { - // ... - } + $roles = $user->getRoles(); -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. + foreach ($roles as $role) { + // ... + } From 77a0751b911e35adaf627261c9d10c04ed74e665 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:43:57 +0100 Subject: [PATCH 608/835] Add Security Groups docs --- doc/services/networking/index.rst | 10 +++ .../networking/security-group-rules.rst | 61 +++++++++++++++++ doc/services/networking/security-groups.rst | 67 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 doc/services/networking/security-group-rules.rst create mode 100644 doc/services/networking/security-groups.rst diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 515f9ddce..335f6a10c 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -23,6 +23,8 @@ Operations networks subnets ports + security-groups + security-group-rules Glossary -------- @@ -49,6 +51,14 @@ Glossary associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + security group + A security group is a named container for security group rules. + + security group rule + A security group rule provides users the ability to specify the types of + traffic that are allowed to pass through to and from ports on a virtual + server instance. + Further links ------------- diff --git a/doc/services/networking/security-group-rules.rst b/doc/services/networking/security-group-rules.rst new file mode 100644 index 000000000..1ca6041d1 --- /dev/null +++ b/doc/services/networking/security-group-rules.rst @@ -0,0 +1,61 @@ +Security Group Rules +==================== + +Create a security group rule +---------------------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================================================================================================================================================================+=======================================+=============+=================+============================================+ +| ``securityGroupId`` | The security group ID to associate with this security group rule. | String | Yes | - | ``2076db17-a522-4506-91de-c6dd8e837028`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``direction`` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (``ingress`` or ``egress``) | Yes | - | ``ingress`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``ethertype`` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (``IPv4`` or ``IPv6``) | No | ``IPv4`` | ``IPv6`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMin`` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the ``portRangeMax`` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMax`` | The maximum port number in the range that is matched by the security group rule. The port\_range\_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``protocol`` | The protocol that is matched by the security group rule. | String (``tcp``, ``udp``, ``icmp``) | No | ``null`` | ``tcp`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteGroupId`` | The remote group ID to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``85cc3048-abc3-43cc-89b3-377341426ac5`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteIpPrefix`` | The remote IP prefix to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``192.168.5.0`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ + +You can create a security group rule as shown in the following example: + +.. code:: php + + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + $securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + +`Get the executable PHP script for this example `_ + + +List security group rules +------------------------- + +You can list all the security group rules to which you have access as +shown in the following example: + +.. code:: php + + $securityGroupRules = $networkingService->listSecurityGroupRules(); + foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + } + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/security-groups.rst b/doc/services/networking/security-groups.rst new file mode 100644 index 000000000..2ec8b384d --- /dev/null +++ b/doc/services/networking/security-groups.rst @@ -0,0 +1,67 @@ +Security Groups +=============== + +Create a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+================================================================================+=============+=============+=================+=====================================+ +| ``name`` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | ``new-webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| ``description`` | Description of the security group. | String | No | ``null`` | ``security group for webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ + +You can create a security group as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + +`Get the executable PHP script for this example `_ + +List security groups +~~~~~~~~~~~~~~~~~~~~ + +You can list all the security groups to which you have access as shown +in the following example: + +.. code-block:: php + + $securityGroups = $networkingService->listSecurityGroups(); + foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + } + +`Get the executable PHP script for this example `_ + +Get a security group +~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific security group by using that security +group’s ID, as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); + +`Get the executable PHP script for this example `_ + +Delete a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete a security group as shown in the following example: + +.. code-block:: php + + $securityGroup->delete(); + +`Get the executable PHP script for this example `_ From b233063a265b4f41547e960c9c4dc9f4ed0c90cb Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 16:54:07 +0100 Subject: [PATCH 609/835] Fixing relative links --- doc/services/networking/security-groups.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/services/networking/security-groups.rst b/doc/services/networking/security-groups.rst index 2ec8b384d..cde5c9a2d 100644 --- a/doc/services/networking/security-groups.rst +++ b/doc/services/networking/security-groups.rst @@ -40,7 +40,7 @@ in the following example: /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a security group ~~~~~~~~~~~~~~~~~~~~ @@ -53,7 +53,7 @@ group’s ID, as shown in the following example: /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a security group ~~~~~~~~~~~~~~~~~~~~~~~ @@ -64,4 +64,4 @@ You can delete a security group as shown in the following example: $securityGroup->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ From 56749c8eb77b393750c1b65a0cf11af99e686072 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 17:05:37 +0100 Subject: [PATCH 610/835] Use :doc: to link to inline documents --- doc/index.rst | 6 +++--- doc/services/identity/roles.rst | 1 + doc/services/identity/tokens.rst | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 297a64c6c..fc7c280c3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,9 +26,9 @@ This assumes your application's PHP files are located in the same folder as ``vendor/``. If your files are located elsewhere, please supply the path to ``vendor/autoload.php`` in the require statement above. -Read the `OpenStack Getting Started guide `_ -or `Rackspace Getting Started guide `_ to help -you get started with basic Compute operations. +Read the :doc:`getting-started-with-openstack` or +:doc:`getting-started-with-rackspace` to help you get started with basic +Compute operations. Services -------- diff --git a/doc/services/identity/roles.rst b/doc/services/identity/roles.rst index 09f0600fa..614518b9b 100644 --- a/doc/services/identity/roles.rst +++ b/doc/services/identity/roles.rst @@ -36,6 +36,7 @@ This call lists the global roles available within a specified service. // ... } + Get role -------- diff --git a/doc/services/identity/tokens.rst b/doc/services/identity/tokens.rst index f49484105..e4601c7c4 100644 --- a/doc/services/identity/tokens.rst +++ b/doc/services/identity/tokens.rst @@ -76,7 +76,7 @@ Interacting with users $user = $service->resource('User', $data); To see which methods you can call on ``$user`` (which implements -``OpenCloud\Identity\Resource\User``), see our `user documentation `_ +``OpenCloud\Identity\Resource\User``), see our :doc:`user documentation ` which accompanies this guide. @@ -89,7 +89,7 @@ Interacting with tenants $tenant = $service->resource('Tenant', $data); To see which methods you can call on ``$tenant`` (which implements -``OpenCloud\Identity\Resource\Tenant``), see our `user documentation `_ +``OpenCloud\Identity\Resource\Tenant``), see our :doc:`user documentation ` which accompanies this guide. From bae5eca721e33aad7eb2dabecac14b6cbca3c104 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 18:23:21 +0100 Subject: [PATCH 611/835] Adding sphinx extension --- doc/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/requirements.txt diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000..9873100ea --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1 @@ +sphinxcontrib.phpdomain From d34c759d1a3b9ce3224df32bcd98fcf4ac887586 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 18:31:28 +0100 Subject: [PATCH 612/835] Use correct ext name --- doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 9873100ea..3f092f372 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1 +1 @@ -sphinxcontrib.phpdomain +sphinxcontrib-phpdomain From 46044ba6faa9038ba0cd24604ae2b2ac512b2fd7 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 11:44:00 +0100 Subject: [PATCH 613/835] Adding sample links --- doc/services/compute/flavors.rst | 2 + doc/services/compute/images.rst | 3 + doc/services/compute/keypairs.rst | 5 + doc/services/compute/servers.rst | 29 +- doc/services/identity/users.rst | 14 + doc/services/load-balancer/access.rst | 7 +- doc/services/load-balancer/load-balancer.rst | 14 +- doc/services/load-balancer/ssl.rst | 2 + doc/services/object-store/access.rst | 11 +- doc/services/object-store/account.rst | 6 + doc/services/object-store/cdn.rst | 6 + doc/services/object-store/containers.rst | 22 ++ doc/services/object-store/objects.rst | 22 ++ doc/services/orchestration/stacks.rst-e | 299 ------------------- doc/services/queues/claims.rst | 2 + doc/services/queues/messages.rst | 2 + doc/services/queues/queues.rst | 11 + doc/services/volume/snapshots.rst | 8 + doc/services/volume/volumes.rst | 9 + 19 files changed, 162 insertions(+), 312 deletions(-) delete mode 100644 doc/services/orchestration/stacks.rst-e diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst index eeb447d3c..470ce8368 100644 --- a/doc/services/compute/flavors.rst +++ b/doc/services/compute/flavors.rst @@ -20,6 +20,8 @@ List flavors /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ } +`Get the executable PHP script for this example `_ + Detailed results ~~~~~~~~~~~~~~~~ diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst index fb68af7f7..570790c04 100644 --- a/doc/services/compute/images.rst +++ b/doc/services/compute/images.rst @@ -23,6 +23,9 @@ Below is the simplest usage for retrieving a list of images: } +`Get the executable PHP script for this example `_ + + Detailed results ~~~~~~~~~~~~~~~~ diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst index f7e67b113..823fcf5a2 100644 --- a/doc/services/compute/keypairs.rst +++ b/doc/services/compute/keypairs.rst @@ -21,6 +21,8 @@ value is automatically generated for you. $pubKey = $keypair->getPublicKey(); $priKey = $keypair->getPrivateKey(); +`Get the executable PHP script for this example `_ + Upload existing keypair ----------------------- @@ -41,6 +43,9 @@ filesystem. 'publicKey' => $content )); +`Get the executable PHP script for this example `_ + + List keypairs ------------- diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 229e8e4ff..8a55ce57f 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -49,6 +49,9 @@ URL parameters for filtering servers | RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | +--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +`Get the executable PHP script for this example `_ + + Create server ------------- @@ -59,19 +62,22 @@ Now we're ready to create our instance: .. code-block:: php - $server = $compute->server(); + $server = $compute->server(); - $server->create(array( - 'name' => 'My lovely server', - 'imageId' => '{imageId}', - 'flavorId' => '{flavorId}', - )); + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. +`Get the executable PHP script for this example `_ + + Using a bootable volume ~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,6 +104,9 @@ you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. +`Get the executable PHP script for this example `_ + + Create parameters ~~~~~~~~~~~~~~~~~ @@ -146,6 +155,8 @@ as usual, with one extra parameter: So, as you can see, you specify the **name** of an existing keypair that you previously created on the API. +`Get the executable PHP script for this example `_ + Creating a server with personality files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,6 +187,10 @@ attributes are detailed in the next section. 'name' => 'NEW SERVER NAME' )); + +`Get the executable PHP script for this example `_ + + Updatable attributes ~~~~~~~~~~~~~~~~~~~~ @@ -195,3 +210,5 @@ Delete server .. code-block:: php $server->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/identity/users.rst b/doc/services/identity/users.rst index b55236a74..944888902 100644 --- a/doc/services/identity/users.rst +++ b/doc/services/identity/users.rst @@ -34,6 +34,8 @@ List users // ... } +`Get the executable PHP script for this example `_ + Retrieve a user by username --------------------------- @@ -42,6 +44,8 @@ Retrieve a user by username $user = $service->getUser('jamie'); +`Get the executable PHP script for this example `_ + Retrieve a user by user ID -------------------------- @@ -52,6 +56,8 @@ Retrieve a user by user ID $user = $service->getUser('{userId}', UserConst::MODE_ID); +`Get the executable PHP script for this example `_ + Retrieve a user by email address -------------------------------- @@ -62,6 +68,8 @@ Retrieve a user by email address $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); +`Get the executable PHP script for this example `_ + Create user ----------- @@ -98,6 +106,8 @@ automatically generated and provided in the response. // show generated password echo $user->getPassword(); +`Get the executable PHP script for this example `_ + Update user ----------- @@ -129,6 +139,8 @@ Delete user $user->delete(); +`Get the executable PHP script for this example `_ + List credentials ---------------- @@ -159,3 +171,5 @@ you: $user->resetApiKey(); echo $user->getApiKey(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/load-balancer/access.rst b/doc/services/load-balancer/access.rst index 4387dd8de..42cd68315 100644 --- a/doc/services/load-balancer/access.rst +++ b/doc/services/load-balancer/access.rst @@ -68,6 +68,11 @@ You can add network items to a load balancer's access list very easily: In the above example, we allowed access for 1 IP address, and used the "0.0.0.0" wildcard to blacklist all other traffic. +Get the executable PHP scripts for this example: + +* `Blacklist IP range `_ +* `Limit access to 1 IP `_ + Remove Network Item From Access List ------------------------------------ @@ -76,4 +81,4 @@ You an remove a network item from a load balancer's access list: .. code-block:: php - $networkItem->delete(); + $networkItem->delete(); diff --git a/doc/services/load-balancer/load-balancer.rst b/doc/services/load-balancer/load-balancer.rst index 7071794df..d7867755d 100644 --- a/doc/services/load-balancer/load-balancer.rst +++ b/doc/services/load-balancer/load-balancer.rst @@ -51,9 +51,11 @@ port number, before submitting to the API: For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ please see the sections below. +`Get the executable PHP script for this example `_ -List Load Balancer Details --------------------------- + +Get Load Balancer Details +------------------------- You can retrieve a single load balancer's details by using its ID: @@ -64,7 +66,7 @@ You can retrieve a single load balancer's details by using its ID: List Load Balancers -~~~~~~~~~~~~~~~~~~~ +------------------- You can retrieve a list of all your load balancers: @@ -76,6 +78,8 @@ You can retrieve a list of all your load balancers: /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ } +`Get the executable PHP script for this example `_ + Update a Load Balancer ---------------------- @@ -121,6 +125,8 @@ When you no longer have a need for the load balancer, you can remove it: $loadBalancer->delete(); +`Get the executable PHP script for this example `_ + Protocols --------- @@ -159,7 +165,7 @@ You can programmatically list all supported load balancing algorithms: .. code-block:: php $algorithms = $service->algorithmList(); - + foreach ($algorithms as $algorithm) { /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ } diff --git a/doc/services/load-balancer/ssl.rst b/doc/services/load-balancer/ssl.rst index 12b28a6af..413bdc612 100644 --- a/doc/services/load-balancer/ssl.rst +++ b/doc/services/load-balancer/ssl.rst @@ -43,6 +43,8 @@ For a full list, with explanations, of required and optional attributes, please consult the `official documentation `__ +`Get the executable PHP script for this example `_ + Delete configuration -------------------- diff --git a/doc/services/object-store/access.rst b/doc/services/object-store/access.rst index 62cb541ca..40fe61670 100644 --- a/doc/services/object-store/access.rst +++ b/doc/services/object-store/access.rst @@ -23,6 +23,11 @@ in a global state: The string argument of ``setTempUrlSecret()`` is optional - if left out, the SDK will generate a random hashed secret for you. +Get the executable PHP script for this example: + +* `Specify a URL secret `_ +* `Generate random URL secret `_ + Create a temporary URL ---------------------- @@ -32,14 +37,16 @@ your object. To allow GET access to your object for 1 minute: .. code-block:: php - $object->getTemporaryUrl(60, 'GET'); + $object->getTemporaryUrl(60, 'GET'); To allow PUT access for 1 hour: .. code-block:: php - $object->getTemporaryUrl(360, 'PUT'); + $object->getTemporaryUrl(360, 'PUT'); + +`Get the executable PHP script for this example `_ Hosting HTML sites on CDN diff --git a/doc/services/object-store/account.rst b/doc/services/object-store/account.rst index fb51c8855..50c17f1a8 100644 --- a/doc/services/object-store/account.rst +++ b/doc/services/object-store/account.rst @@ -29,6 +29,8 @@ Retrieve total container count $account->getContainerCount(); +`Get the executable PHP script for this example `_ + Retrieve total object count --------------------- @@ -37,6 +39,8 @@ Retrieve total object count $account->getObjectCount(); +`Get the executable PHP script for this example `_ + Retrieve total bytes used ------------------------- @@ -44,3 +48,5 @@ Retrieve total bytes used .. code-block:: php $account->getBytesUsed(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst index 5ad72b6cd..eb29bb27f 100644 --- a/doc/services/object-store/cdn.rst +++ b/doc/services/object-store/cdn.rst @@ -29,6 +29,8 @@ execute the method on: /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ } +`Get the executable PHP script for this example `_ + CDN-enable a container ---------------------- @@ -47,6 +49,8 @@ refetches and caches the object for the TTL period. $container->enableCdn(); +`Get the executable PHP script for this example `_ + CDN-disable a container ----------------------- @@ -55,6 +59,8 @@ CDN-disable a container $container->disableCdn(); +`Get the executable PHP script for this example `_ + Operations on CDN-enabled containers ------------------------------------ diff --git a/doc/services/object-store/containers.rst b/doc/services/object-store/containers.rst index 4b9c66ac3..a2f0485e8 100644 --- a/doc/services/object-store/containers.rst +++ b/doc/services/object-store/containers.rst @@ -22,6 +22,9 @@ Forward slashes are not currently permitted. (such as spaces or non-English characters), you must ensure they are encoded with `urlencode `_ before passing them in +`Get the executable PHP script for this example `_ + + List containers --------------- @@ -42,6 +45,8 @@ memcmp() function, regardless of text encoding. The list is limited to 10,000 containers at a time. To work with larger collections, please read the next section. +`Get the executable PHP script for this example `_ + Filtering large collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -82,6 +87,8 @@ To retrieve a certain container: /** @param $container OpenCloud\ObjectStore\Resource\Container */ $container = $service->getContainer('{containerName}'); +`Get the executable PHP script for this example `_ + Retrieve a container's name ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,6 +105,8 @@ Retrieve a container's object count $count = $container->getObjectCount(); +`Get the executable PHP script for this example `_ + Retrieve a container's total bytes used ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -106,6 +115,8 @@ Retrieve a container's total bytes used $bytes = $container->getBytesUsed(); +`Get the executable PHP script for this example `_ + Delete container ---------------- @@ -129,6 +140,8 @@ before deleting it. This is done for you if you set the You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ first, and then call ``delete``. +`Get the executable PHP script for this example `_ + Deleting all objects inside a container --------------------------------------- @@ -137,6 +150,8 @@ Deleting all objects inside a container $container->deleteAllObjects(); +`Get the executable PHP script for this example `_ + Create or update container metadata ----------------------------------- @@ -158,6 +173,8 @@ to the current metadata: 'Publisher' => 'Hogarth' )); +`Get the executable PHP script for this example `_ + Container quotas ---------------- @@ -184,6 +201,11 @@ And to retrieve them: echo $container->getCountQuota(); echo $container->getBytesQuota(); +Get the executable PHP scripts for this example: + +* `Set bytes quota `_ +* `Set count quota `_ + Access log delivery ------------------- diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index e38e0eb8b..0eb0ea836 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -43,6 +43,8 @@ its path: The resource handle will be automatically closed by Guzzle in its destructor, so there is no need to execute ``fclose``. +`Get the executable PHP script for this example `_ + Upload a single file (under 5GB) with metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -80,6 +82,8 @@ file handle resource, or a string representation of object content (a temporary resource will be created in memory), and the third is an array of additional headers. +`Get the executable PHP script for this example `_ + Batch upload multiple files (each under 5GB) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -108,6 +112,8 @@ also specify *either* a path key (to an existing file), or a ``body``. The ``body`` can either be a PHP resource or a string representation of the content you want to upload. +`Get the executable PHP script for this example `_ + Upload large files (over 5GB) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -142,6 +148,8 @@ In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. To find out more details, please consult the `official documentation `_. +`Get the executable PHP script for this example `_ + List objects in a container --------------------------- @@ -166,6 +174,8 @@ docs objectList(array('prefix' => 'logFile_')); +`Get the executable PHP script for this example `_ + Get object ---------- @@ -313,6 +323,8 @@ destination path: Where ``container_2`` is the name of the container, and ``new_object_name`` is the name of the object inside the container that does not exist yet. +`Get the executable PHP script for this example `_ + Get object metadata ------------------- @@ -346,6 +358,8 @@ You can also update to get the latest metadata: $object->retrieveMetadata(); +`Get the executable PHP script for this example `_ + Update object metadata ---------------------- @@ -386,6 +400,8 @@ you want to append values to your metadata, use the correct method: $object->saveMetadata($metadata); +`Get the executable PHP script for this example `_ + Extract archive --------------- @@ -406,6 +422,8 @@ the first argument). If you do this, the API will create the containers necessary to house the extracted files - this is done based on the filenames inside the archive. +`Get the executable PHP script for this example `_ + Delete object ------------- @@ -414,6 +432,8 @@ Delete object $object->delete(); +`Get the executable PHP script for this example `_ + Delete multiple objects ----------------------- @@ -425,3 +445,5 @@ Bulk delete a set of paths: $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); $service->bulkDelete($pathsToBeDeleted); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst-e b/doc/services/orchestration/stacks.rst-e deleted file mode 100644 index 29b6c4f7f..000000000 --- a/doc/services/orchestration/stacks.rst-e +++ /dev/null @@ -1,299 +0,0 @@ -Stacks -====== - -A stack is a running instance of a template. When a stack is created, -the `resources <#stack-resources>`__ specified in the template are -created. - - -Preview stack -------------- - -Before you create a stack from a template, you might want to see what -that stack will look like. This is called *previewing the stack*. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Preview a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to preview a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - -`Get the executable PHP script for this example `_ - - -Preview a stack from a template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to preview a stack as shown -in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - -`Get the executable PHP script for this example `_ - - -Create stack ------------- - -You can create a stack from a template. This operation takes one parameter, an -associative array, with the following keys: - -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Create a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to create a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Create a stack from a template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to create a stack as shown -in the following example: - -.. code-block:: php - - $stack = $orchestrationService->stack(); - $stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - -List stacks ------------ - -You can list all the stacks that you have created as shown in the -following example: - -.. code-block:: php - - $stacks = $orchestrationService->listStacks(); - foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - } - -`Get the executable PHP script for this example `_ - - -Get stack ---------- - -You can retrieve a specific stack using its name, as shown in the -following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->getStack('simple-lamp-setup'); - -`Get the executable PHP script for this example `_ - - -Get stack template ------------------- - -You can retrieve the template used to create a stack. Note that a JSON -string is returned, regardless of whether a JSON or YAML template was -used to create the stack. - -.. code-block:: php - - /** @var $stackTemplate string **/ - $stackTemplate = $stack->getTemplate(); - -`Get the executable PHP script for this example `_ - - -Update stack ------------- - -You can update a running stack. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ - - -Update a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to update a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Update Stack from Template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to update a stack as shown -in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Delete stack ------------- - -If you no longer need a stack and all its resources, you can delete the -stack *and* the resources as shown in the following example: - -.. code-block:: php - - $stack->delete(); - -`Get the executable PHP script for this example `_ - - -Abandon Stack -------------- - -.. note:: - - This operation returns data about the abandoned stack as a string. You can - use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ - operation. - -If you want to delete a stack but preserve all its resources, you can -abandon the stack as shown in the following example: - -.. code-block:: php - - /** @var $abandonStackData string **/ - $abandonStackData = $stack->abandon(); - file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); - -`Get the executable PHP script for this example `_ - - -Adopt stack ------------ - -If you have data from an abandoned stack, you can re-create the stack as -shown in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ diff --git a/doc/services/queues/claims.rst b/doc/services/queues/claims.rst index 7e375c8d3..1111cb9eb 100644 --- a/doc/services/queues/claims.rst +++ b/doc/services/queues/claims.rst @@ -67,6 +67,8 @@ limit parameter is optional. 'ttl' => 5 * Datetime::MINUTE )); +`Get the executable PHP script for this example `_ + Query claim ----------- diff --git a/doc/services/queues/messages.rst b/doc/services/queues/messages.rst index ea23caea3..3a5dee89d 100644 --- a/doc/services/queues/messages.rst +++ b/doc/services/queues/messages.rst @@ -50,6 +50,8 @@ Posting a single message 'ttl' => 2 * Datetime::DAY )); +`Get the executable PHP script for this example `_ + Post a batch of messages ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/services/queues/queues.rst b/doc/services/queues/queues.rst index 7958adfae..f324e3f3b 100644 --- a/doc/services/queues/queues.rst +++ b/doc/services/queues/queues.rst @@ -64,6 +64,8 @@ digits, underscores, and hyphens. $queue = $service->createQueue('new_queue'); +`Get the executable PHP script for this example `_ + Find queue details ------------------ @@ -122,3 +124,12 @@ in the queue, categorized by status. .. code-block:: php $queue->getStats(); + +Delete queue +------------ + +.. code-block:: php + + $queue->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/volume/snapshots.rst b/doc/services/volume/snapshots.rst index c2d8ef6cc..555824a9d 100644 --- a/doc/services/volume/snapshots.rst +++ b/doc/services/volume/snapshots.rst @@ -19,6 +19,8 @@ to create one: 'volume_id' => $volume->id )); +`Get the executable PHP script for this example `_ + List snapshots -------------- @@ -31,6 +33,8 @@ List snapshots /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ } +`Get the executable PHP script for this example `_ + To get details on a single snapshot ----------------------------------- @@ -39,6 +43,8 @@ To get details on a single snapshot $snapshot = $dallas->snapshot('{snapshotId}'); +`Get the executable PHP script for this example `_ + To delete a snapshot -------------------- @@ -46,3 +52,5 @@ To delete a snapshot .. code-block:: php $snapshot->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/volume/volumes.rst b/doc/services/volume/volumes.rst index 6a35bb135..a84ed0421 100644 --- a/doc/services/volume/volumes.rst +++ b/doc/services/volume/volumes.rst @@ -19,6 +19,9 @@ parameters are optional: 'display_description' => 'Used for large object storage' )); +`Get the executable PHP script for this example `_ + + List volumes ------------ @@ -30,6 +33,8 @@ List volumes /** @param $volumeType OpenCloud\Volume\Resource\Volume */ } +`Get the executable PHP script for this example `_ + Get details on a single volume ------------------------------ @@ -42,6 +47,8 @@ information on the specified volume: $volume = $dallas->volume(''); echo $volume->size; +`Get the executable PHP script for this example `_ + To delete a volume ------------------ @@ -50,6 +57,8 @@ To delete a volume $volume->delete(); +`Get the executable PHP script for this example `_ + Attach a volume to a server --------------------------- From 82ba1ca9b84b617a36bf3a913c6c188fbe8bcc1e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 11:45:55 +0100 Subject: [PATCH 614/835] Add list CDN containers sample file --- samples/ObjectStore/list-cdn-containers.php | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 samples/ObjectStore/list-cdn-containers.php diff --git a/samples/ObjectStore/list-cdn-containers.php b/samples/ObjectStore/list-cdn-containers.php new file mode 100644 index 000000000..a71a8bed9 --- /dev/null +++ b/samples/ObjectStore/list-cdn-containers.php @@ -0,0 +1,40 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Object a CDN service object +$cdnService = $objectStoreService->getCdnService(); + +// 4. Get container list. +$containers = $cdnService->listContainers(); + +foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\CDNContainer **/ +} From 3b25e1a6fd01cfd2163947a8660f82555b71feed Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Mar 2015 10:09:13 -0700 Subject: [PATCH 615/835] Adding smoke test to apply security group to port. --- tests/OpenCloud/Smoke/Unit/Networking.php | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 8ccd30a94..02ed838bb 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -272,6 +272,30 @@ protected function testSecurityGroupOperations() $securityGroup = $this->getService()->getSecurityGroup($securityGroup->getId()); $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + + $network1 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_port_sg' + )); + $this->cleanupNetworkIds[] = $network1->getId(); + + $subnet1 = $this->getService()->createSubnet(array( + 'cidr' => '192.165.66.0/25', + 'networkId' => $network1->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet_for_test_port_sg' + )); + $this->cleanupSubnetIds[] = $subnet1->getId(); + + $port1 = $this->getService()->createPort(array( + 'networkId' => $network1->getId(), + 'name' => 'test_port_for_test_port_sg' + )); + $this->cleanupPortIds[] = $port1->getId(); + + $this->step('Apply security group to port'); + $port1->update(array( + 'securityGroups' => array($securityGroup->getId()) + )); } protected function testSecurityGroupRuleOperations() From ae17b8fd638142e9c239ce707925f7f9d2134137 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Mar 2015 10:21:52 -0700 Subject: [PATCH 616/835] Minor formatting fix so code block renders correctly. --- doc/getting-started-with-rackspace.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/getting-started-with-rackspace.rst b/doc/getting-started-with-rackspace.rst index 35317a3e6..73aa3a8d4 100644 --- a/doc/getting-started-with-rackspace.rst +++ b/doc/getting-started-with-rackspace.rst @@ -138,7 +138,7 @@ Okay, you're ready to spin up a server: .. code-block:: php -use Guzzle\Http\Exception\BadResponseException; + use Guzzle\Http\Exception\BadResponseException; $server = $compute->server(); From ac8030e5ad21fefff79887fd729eab721c50d987 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 13:10:35 +0100 Subject: [PATCH 617/835] Add deprecation and redirect links to existing docs --- doc/services/compute/servers.rst | 36 + docs/getting-started-openstack.md | 200 +----- docs/getting-started.md | 182 +---- docs/userguide/Autoscale/Config.md | 46 +- docs/userguide/Autoscale/Groups.md | 95 +-- docs/userguide/Autoscale/Policies.md | 67 +- docs/userguide/Autoscale/Webhooks.md | 51 +- docs/userguide/Clients.md | 148 +---- docs/userguide/CloudMonitoring/Agents.md | 148 +---- docs/userguide/CloudMonitoring/Alarms.md | 55 +- docs/userguide/CloudMonitoring/Changelogs.md | 13 +- docs/userguide/CloudMonitoring/Checks.md | 140 +--- docs/userguide/CloudMonitoring/Entities.md | 54 +- docs/userguide/CloudMonitoring/Metrics.md | 78 +-- .../CloudMonitoring/Notifications.md | 202 +----- docs/userguide/CloudMonitoring/Service.md | 15 +- docs/userguide/CloudMonitoring/Views.md | 18 +- docs/userguide/CloudMonitoring/Zones.md | 45 +- docs/userguide/Compute/Images.md | 64 +- docs/userguide/Compute/Keypair.md | 90 +-- docs/userguide/Compute/Server.md | 179 +---- docs/userguide/Compute/Service.md | 17 +- docs/userguide/DNS/Domains.md | 229 +------ docs/userguide/DNS/Limits.md | 57 +- docs/userguide/DNS/Records.md | 86 +-- docs/userguide/DNS/Reverse-DNS.md | 70 +- docs/userguide/DNS/Service.md | 10 +- docs/userguide/Database/README.md | 115 +--- docs/userguide/Debugging.md | 88 +-- docs/userguide/Identity/Roles.md | 67 +- docs/userguide/Identity/Service.md | 23 +- docs/userguide/Identity/Tenants.md | 20 +- docs/userguide/Identity/Tokens.md | 83 +-- docs/userguide/Identity/Users.md | 127 +--- docs/userguide/Image/Images.md | 96 +-- docs/userguide/Image/Schemas.md | 158 +---- docs/userguide/Image/Sharing.md | 109 +-- docs/userguide/Image/Tags.md | 22 +- docs/userguide/Iterators.md | 117 +--- docs/userguide/LoadBalancer/README.md | 79 +-- docs/userguide/LoadBalancer/USERGUIDE.md | 625 +----------------- docs/userguide/Networking/README.md | 75 +-- docs/userguide/Networking/USERGUIDE.md | 616 +---------------- docs/userguide/ObjectStore/Access.md | 65 +- docs/userguide/ObjectStore/Account.md | 27 +- docs/userguide/ObjectStore/CDN/Container.md | 77 +-- docs/userguide/ObjectStore/CDN/Object.md | 19 +- docs/userguide/ObjectStore/README.md | 64 +- .../ObjectStore/Storage/Container.md | 181 +---- .../ObjectStore/Storage/Migrating.md | 82 +-- docs/userguide/ObjectStore/Storage/Object.md | 274 +------- docs/userguide/ObjectStore/USERGUIDE.md | 624 +---------------- docs/userguide/Orchestration/README.md | 80 +-- docs/userguide/Orchestration/USERGUIDE.md | 505 +------------- docs/userguide/Queues/Claim.md | 122 +--- docs/userguide/Queues/Message.md | 206 +----- docs/userguide/Queues/Queue.md | 155 +---- docs/userguide/README.md | 73 +- docs/userguide/Services.md | 46 +- docs/userguide/accessip.md | 49 +- docs/userguide/caching-credentials.md | 38 +- docs/userguide/dbaas.md | 317 +-------- docs/userguide/flavors.md | 58 +- docs/userguide/networks.md | 101 +-- docs/userguide/servers.md | 186 +----- docs/userguide/volumes.md | 171 +---- 66 files changed, 166 insertions(+), 8169 deletions(-) diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 8a55ce57f..0709bda9a 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -204,6 +204,42 @@ Updatable attributes | accessIPv6 | The IP version 6 address. | +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Updating the access IP address(es) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example, you may have a private cloud with internal addresses in the +10.1.x range. However, you can access a server via a firewall device at +address 50.57.94.244. In this case, you can change the ``accessIPv4`` +attribute to point to the firewall: + +.. code-block:: php + + $server->update(array('accessIPv4' => '50.57.94.244')); + +When a client application retrieves the server’s information, it will +know that it needs to use the ``accessIPv4`` address to connect to the +server, and *not* the IP address assigned to one of the network +interfaces. + + +Retrieving the server’s IP address +---------------------------------- + +The ``Server::ip()`` method is used to retrieve the server’s IP address. +It has one optional parameter: the format (either IPv4 or IPv6) of the +address to return (by default, it returns the IPv4 address): + +.. code-block:: php + + // IPv4 + echo $server->ip(); + echo $server->ip(4); + + // IPv6 + echo $server->ip(6); + + Delete server ------------- diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index 6bb1ae260..24eda2779 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -1,201 +1,5 @@ # Installing the SDK -You must install through Composer, because this library has a few dependencies: +Our docs have moved! Please visit the below link: -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master -``` - -Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): - -```php -require 'vendor/autoload.php'; -``` - -And you're good to go! - -# Quick deep-dive: building some Nova instances - -In this example, you will write code that will create a Nova instance running Ubuntu. - -### 1. Setup the client and pass in your credentials - -To authenticate against Keystone: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( - 'username' => 'foo', - 'password' => 'bar', - 'tenantName' => 'baz' -)); -``` - -You will need to substitute in the public URL endpoint for your Keystone service, as well as your `username`, `password` -and `tenantName`. You can also specify your `tenantId` instead of `tenantName` if you prefer. - -### 2. Pick what service you want to use - -In this case, we want to use the Nova service: - -```php -$compute = $client->computeService('nova', 'regionOne'); -``` - -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. For OpenStack users, this must be retrieved and entered in your code. If you are unsure how to retrieve the service name, follow these steps: - -1. Setup the `$client` object, as above -2. Copy and run this code: - - ```php - $client->authenticate(); - - print_r($client->getCatalog()->getItems()); - ``` - -3. This will output all the items in your service catalog. Go through the outputted list and find your service, making note of the "name" field. This is the name you will need to enter as the first argument. You will also be able to see the available regions. - -The second argument is the region. The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. - -### 3. Select your server image - -Instances are based on "images", which are effectively just the type of operating system you want. Let's go through the -list and find an Ubuntu one: - -```php -$images = $compute->imageList(); -foreach ($images as $image) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntu = $image; - break; - } -} -``` - -Alternatively, if you already know the image ID, you can do this much easier: - -```php -$ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -``` - -## 4. Select your flavor - -There are different server specs - some which offer 1GB RAM, others which offer a much higher spec. The 'flavor' of an -instance is its hardware configuration. So if you want a 2GB instance but don't know the ID, you have to traverse the list: - -```php -$flavors = $compute->flavorList(); -foreach ($flavors as $flavor) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} -``` - -Again, it's much easier if you know the ID: - -```php -$twoGbFlavor = $compute->flavor('4'); -``` - -## 5. Thunderbirds are go! - -Okay, you're ready to spin up a server: - -```php -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntu, - 'flavor' => $twoGbFlavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf("Status: %s\nBody: %s\nHeaders: %s", $statusCode, $responseBody, implode(', ', $headers)); -} -``` - -As you can see, you're creating a server called "My lovely server" - this will take a few minutes for the build to -complete. You can always check the progress by logging into your Controller node and running: - -`nova list` - -You can also execute a polling function immediately after the `create` method that checks the build process: - -```php -use OpenCloud\Compute\Constants\ServerState; - -$callback = function($server) { - if (!empty($server->error)) { - var_dump($server->error); - exit; - } else { - echo sprintf( - "Waiting on %s/%-12s %4s%%", - $server->name(), - $server->status(), - isset($server->progress) ? $server->progress : 0 - ); - } -}; - -$server->waitFor(ServerState::ACTIVE, 600, $callback); -``` -So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. - -# More fun with Nova - -Once you've booted up your instance, you can use other API operations to monitor your Compute nodes. To list every -node on record, you can execute: - -```php -$servers = $compute->serverList(); - -foreach ($servers as $server) { - // do something with each server... - echo $server->name, PHP_EOL; -} -``` - -or, if you know a particular instance ID you can retrieve its details: - -```php -$server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); -``` - -allowing you to update its properties: - -```php -$server->update(array( - 'name' => 'New server name' -)); -``` - -or delete it entirely: - -```php -$server->delete(); -``` - -# Next steps - -Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services -you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or -[Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to contact us at https://developer.rackspace.com/support/ or open a Github issue with details. +https://doc.php-opencloud.com/en/latest/getting-started-with-openstack.html diff --git a/docs/getting-started.md b/docs/getting-started.md index e661e0ce8..af65271fe 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,183 +1,5 @@ # Installing the SDK -You must install through Composer, because this library has a few dependencies: +Our docs have moved! Please visit the below link: -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master -``` - -Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): - -```php -require 'vendor/autoload.php'; -``` - -And you're good to go! - -# Quick deep-dive: building some cloud servers - -In this example, you will write code that will create a Cloud Server running Ubuntu. - -### 1. Setup the client and pass in your credentials - -To authenticate against the Rackspace API and use its services: - -```php - 'foo', - 'apiKey' => 'bar' -)); -``` - -Alternatively, if you would like to validate against your own API, or just want to access OpenStack services: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://identity.my-openstack.com/v2.0/', array( - 'username' => 'foo', - 'password' => 'bar' -)); -``` - -You can see in the first example that the constant `Rackspace::US_IDENTITY_ENDPOINT` is just a string representation of -Rackspace's identity endpoint (`https://identity.api.rackspacecloud.com/v2.0/`). Another difference is that Rackspace -uses API key for authentication, whereas OpenStack uses a generic password. - -#### 1.2 Logger injection -As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. - -Prerequisities and usage example can be found in [the Clients userguide](/docs/userguide/Clients.md#12-logger-injection) - -### 2. Pick what service you want to use - -In this case, we want to use the Compute (Nova) service: - -```php -$compute = $client->computeService('cloudServersOpenStack', 'ORD'); -``` - -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. If in doubt, you can -leave blank and it will revert to the default name for the service. The second argument is the region; you may use: - -- __DFW__ (Dallas) -- __ORD__ (Chicago) -- __IAD__ (Virginia) -- __LON__ (London) -- __HKG__ (Hong Kong) -- __SYD__ (Sydney) - -The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. If you select `internalUrl` -all API traffic will use ServiceNet (internal IPs) and will receive a performance boost. - -### 3. Select your server image - -Servers are based on "images", which are effectively just the type of operating system you want. Let's go through the list -and find an Ubuntu one: - -```php -$images = $compute->imageList(); -while ($image = $images->next()) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntu = $image; - break; - } -} -``` - -Alternatively, if you already know the image ID, you can do this much easier: - -```php -$ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -``` - -## 4. Select your flavor - -There are different server specs - some which offer 1GB RAM, others which offer a much higher spec. The 'flavor' of a -server is its hardware configuration. So if you want a 2GB instance but don't know the ID, you have to traverse the list: - -```php -$flavors = $compute->flavorList(); -while ($flavor = $flavors->next()) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} -``` - -Again, it's much easier if you know the ID: - -```php -$twoGbFlavor = $compute->flavor('4'); -``` - -## 5. Thunderbirds are go! - -Okay, you're ready to spin up a server: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntu, - 'flavor' => $twoGbFlavor, - 'networks' => array( - $compute->network(Network::RAX_PUBLIC), - $compute->network(Network::RAX_PRIVATE) - ) - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf("Status: %s\nBody: %s\nHeaders: %s", $statusCode, $responseBody, implode(', ', $headers)); -} -``` - -As you can see, you're creating a server called "My lovely server", and you've inserted it in two networks: the Rackspace -private network (ServiceNet), and the Rackspace public network (for Internet connectivity). This will take a few -minutes for the build to complete. - -You can also call a polling function that checks on the build process: - -```php -use OpenCloud\Compute\Constants\ServerState; - -$callback = function($server) { - if (!empty($server->error)) { - var_dump($server->error); - exit; - } else { - echo sprintf( - "Waiting on %s/%-12s %4s%%", - $server->name(), - $server->status(), - isset($server->progress) ? $server->progress : 0 - ); - } -}; - -$server->waitFor(ServerState::ACTIVE, 600, $callback); -``` -So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. +https://doc.php-opencloud.com/en/latest/getting-started-with-rackspace.html diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md index a619bf277..02f2e891d 100644 --- a/docs/userguide/Autoscale/Config.md +++ b/docs/userguide/Autoscale/Config.md @@ -1,47 +1,5 @@ # Group configurations -##Intro +Our docs have moved! Please visit the below link: -There are two types of configuration associated with an Auto Scale group: - -- **Group configuration**. Outlines the basic elements of the Auto Scale configuration. The group configuration manages how many servers can participate in the scaling group. It sets a minimum and maximum limit for the number of entities that can be used in the scaling process. It also specifies information related to load balancers. - -- **Launch configuration**. Creates a blueprint for how new servers will be created. The launch configuration specifies what type of server image will be started on launch, what flavor the new server is, and which load balancer the new server connects to. - -## Setup - -To interact with the configuration of a scaling group, you will need to setup the group object beforehand: - -```php -$groupId = 'e41380ae-173c-4b40-848a-25c16d7fa83d'; -$group = $service->getGroup($groupId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [groups](). - -## Get group/launch configuration - -```php -$groupConfig = $group->getGroupConfig(); -$launchConfig = $group->getLaunchConfig(); -``` - -## Edit group/launch configuration - -```php -$groupConfig->update(array( - 'name' => 'New name!' -)); - -$launchConfig = $group->getLaunchConfig(); - -$server = $launchConfig->args->server; -$server->name = "BRAND NEW SERVER NAME"; - -$launchConfig->update(array - 'args' => array( - 'server' => $server, - 'loadBalancers' => $launchConfig->args->loadBalancers - ) -)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/group-config.html diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md index 98dfb43dc..7ec7aa269 100644 --- a/docs/userguide/Autoscale/Groups.md +++ b/docs/userguide/Autoscale/Groups.md @@ -1,96 +1,5 @@ #Auto Scale groups -## Intro +Our docs have moved! Please visit the below link: -The scaling group is at the heart of an Auto Scale deployment. The scaling group specifies the basic elements of the Auto Scale configuration. It manages how many servers can participate in the scaling group. It also specifies information related to load balancers if your configuration uses a load balancer. - -## Service setup - -Nothing special here; you setup your client and service objects in the same way as every other resource: - -```php -$service = $client->autoscaleService(); -``` - -Please consult the [client doc](docs/userguide/Client.md) for more information about creating clients. - -## List all groups - -```php -$groups = $service->groupList(); -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -## Retrieve one group by ID - -```php -$group = $service->group('605e13f6-1452-4588-b5da-ac6bb468c5bf'); -``` - -## Create a new group - -You can create a new scaling group in two ways: either pass in a JSON string (easier option); or instantiate a new object and manually set its properties (a lot more verbose) - -```php - -// Easy way: - -$jsonString = <<create($jsonString); - -// More granular (and verbose) way: - -$object = new \stdClass; - -// Set the config object for this autoscale group; contains all of properties -// which determine its behaviour -$object->groupConfiguration = new \stdClass; -$object->groupConfiguration->name = 'New autoscale group'; -$object->groupConfiguration->minEntities = 5; -$object->groupConfiguration->maxEntities = 25; -$object->groupConfiguration->cooldown = 60; - -// We need specify what is going to be launched. For now, we'll launch a new server -$object->launchConfiguration = new \stdClass; -$object->launchConfiguration->type = 'launch_server'; - -// To launch a new server, we need two `args` properties: `server` and `loadBalancer` -$server = new \stdClass; -$server->flavorRef = 3; -$server->name = 'webhead'; -$server->imageRef = "0d589460-f177-4b0f-81c1-8ab8903ac7d8"; - -$loadBalancer = new \stdClass; -$loadBalancer->loadBalancerId = 2200; -$loadBalancer->port = 8081; - -$object->launchConfiguration->args = new \stdClass; -$object->launchConfiguration->args->server = $server; -$object->launchConfiguration->args->loadBalancers = array($loadBalancer); - -// Do we want particular scaling policies? -$policy = new \stdClass; -$policy->name = "scale up by 10"; -$policy->change = 10; -$policy->cooldown = 5; -$policy->type = "webhook"; - -$object->scalingPolicies = array($policy); - -$group->create($object); -``` - -## Delete an autoscale group -```php -$group->delete(); -``` - -## Get the current state of the scaling group - -```php -$group->getState(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/groups.html diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md index 6f3d1b282..7a312af50 100644 --- a/docs/userguide/Autoscale/Policies.md +++ b/docs/userguide/Autoscale/Policies.md @@ -1,68 +1,5 @@ #Policies -## Setup +Our docs have moved! Please visit the below link: -To interact with the policies of a scaling group, you will need to setup the group object beforehand. - -```php -$groupId = 'foobar'; -$group = $service->getGroup($groupId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [Autoscale groups](). - -## Get all policies - -```php -$policies = $group->getPolicies(); - -while ($policy = $policies->next()) { - // do something - echo "{$policy->name} {($policy->type)}" . PHP_EOL; -} -``` - -## Create a new scaling policy - -Creating policies is achieved through passing an array to the `create` method. - -```php -$policy = new \stdClass; -$policy->name = "NEW NAME"; -$policy->change = 1; -$policy->cooldown = 150; -$policy->type = "webhook"; - -$group->getPolicy()->create(array($policy)); - -// or even: - -$group->getPolicy()->create(array( - (object) array( - 'name' => 'NEW NAME', - 'change' => 1, - 'cooldown' => 150, - 'type' => 'webhook' - ), - // etc. -)); -``` - -## Get, update and delete a scaling policy - -```php -$policyId = 'foobar'; -$policy = $group->getPolicy($policyId); - -$policy->update(array( - 'name' => 'More relevant name' -)); - -$policy->delete(); -``` - -## Execute a scaling policy - -```php -$policy->execute(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/policies.html diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md index e634cc073..c5e6453ed 100644 --- a/docs/userguide/Autoscale/Webhooks.md +++ b/docs/userguide/Autoscale/Webhooks.md @@ -1,52 +1,5 @@ # Webhooks -## Setup +Our docs have moved! Please visit the below link: -To interact with the webhooks of a group's scaling policy, you will need to setup the group and policy objects beforehand. - -```php -$groupId = 'foo'; -$policyId = 'bar'; - -$group = $service->getGroup($groupId); -$policy = $group->getPolicy($policyId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [Autoscale groups](). - -## Get all webhooks - -```php -$webhooks = $policy->getWebookList(); -``` - -## Create a new webhook - -```php -$policy->getWebhook()->create(array( - (object) array( - 'name' => 'Alice', - 'metadata' => array( - 'firstKey' => 'foo', - 'secondKey' => 'bar' - ) - ) -)); -``` - -## Get, update and delete an individual webhook - -```php -$webhookId = 'baz'; -$webhook = $policy->getWebhook($webhookId); - -// Update the metadata -$metadata = $webhook->metadata; -$metadata->thirdKey = 'blah'; -$webhook->update(array( - 'metadata' => $metadata -)); - -// Delete it -$webhook->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/webhooks.html diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index c7270d69e..184b601f4 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -1,149 +1,5 @@ # Clients -## Overview +Our docs have moved! Please visit the below link: -A Client is the object responsible for issuing HTTP requests and receiving responses from the API. In short, it forms the core of the SDK because it controls how functionality is executed. All services depend on the client to work. - -Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\Rackspace`. The latter extends the former, meaning that much of the functionality is shared between them. The OpenStack client extends functionality from other base classes, that trace all the way back to Guzzle's root class: - -1. `Guzzle\Http\Client` -2. `OpenCloud\Common\Http\Client` -3. `OpenCloud\OpenStack` -4. `OpenCloud\Rackspace` - -## 1. Initializing a client - -### Rackspace - -First, you need to select the Identity endpoint you want to authenticate against. If you're using Rackspace, you can either use the UK or US endpoints. There are class constants defined for your convenience: - -- `OpenCloud\Rackspace::US_IDENTITY_ENDPOINT` (https://identity.api.rackspacecloud.com/v2.0) -- `OpenCloud\Rackspace::UK_IDENTITY_ENDPOINT` (https://lon.identity.api.rackspacecloud.com/v2.0) - -Then you need to find your username and apiKey. Your username will be visible at the top right of the Rackspace Control panel; and your API key can be retrieved by going to Account Settings. Once this is done: - -```php -use OpenCloud\OpenStack; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' -)); -``` - -### OpenStack - -To initialize an OpenStack client, the process is the same: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( - 'username' => 'foo', - 'password' => 'bar' -)); -``` - -#### 1.2 Logger injection -As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. - -Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. - -To inject a `LoggerInterface` compatible logger into a new `Client`: - -```php -use Monolog\Logger; -use OpenCloud\OpenStack; - -// create a log channel -$log = new Logger('name'); - -$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( - 'username' => 'foo', - 'password' => 'bar' -), array( - 'logger' => $log, -)); -``` - -## 2. Authentication - -The Client does not automatically authenticate against the API on object creation - it waits for an API call. When this happens, it checks whether the current "token" has expired, and (re-)authenticates if necessary. - -You can force authentication, by calling: - -```php -$client->authenticate(); -``` - -If the credentials are incorrect, a `401` error will be returned. If credentials are correct, a `200` status is returned with your Service Catalog. - -## 3. Service Catalog - -The Service Catalog is returned on successful authentication, and is composed of all the different API services available to the current tenant. All of this functionality is encapsulated in the `Catalog` object, which allows you greater control and interactivity. - -```php -/** @var OpenCloud\Common\Service\Catalog */ -$catalog = $client->getCatalog(); - -// Return a list of OpenCloud\Common\Service\CatalogItem objects -foreach ($catalog->getItems() as $catalogItem) { - - $name = $catalogItem->getName(); - $type = $catalogItem->getType(); - - if ($name == 'cloudServersOpenStack' && $type == 'compute') { - break; - } - - // Array of OpenCloud\Common\Service\Endpoint objects - $endpoints = $catalogItem->getEndpoints(); - foreach ($endpoints as $endpoint) { - if ($endpoint->getRegion() == 'DFW') { - echo $endpoint->getPublicUrl(); - } - } -} -``` - -As you can see, you have access to each Service's name, type and list of endpoints. Each endpoint provides access to the specific region, along with its public and private endpoint URLs. - -## 4. Default HTTP headers - -To set default HTTP headers: - -```php -$client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); -``` - -## User agents - -php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: - -> User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz - -where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: - -```php -$client->setUserAgent('MyCustomUserAgent'); -``` - -which will result in: - -> User-Agent: MyCustomUserAgent - -If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: - -```php -$client->setUserAgent('MyPrefix', true); -``` - -which will result in: - -> User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz - -where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. - -## 5. Other functionality - -For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md index 7e67e7d0b..b316d004c 100644 --- a/docs/userguide/CloudMonitoring/Agents.md +++ b/docs/userguide/CloudMonitoring/Agents.md @@ -1,149 +1,5 @@ # Agents -## Intro +Our docs have moved! Please visit the below link: -The Monitoring Agent resides on the host server being monitored. The agent allows you to gather on-host metrics based on agent checks and push them to Cloud Monitoring where you can analyze them, use them with the Cloud Monitoring infrastructure (such as alarms), and archive them. - -For more information about this feature, including a brief overview of its core design principles and security layers, see the [official API documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/service-agent.html). - -## Setup - -```php -$agentId = '00-agent.example.com'; -$agent = $service->getAgent($agentId); -``` - -You can view the [service page](Service.md) for more information about setting up the Cloud Monitoring service. - -## List agents - -```php -$agents = $service->getAgents(); - -foreach ($agents as $agent) { - echo $agent->getLastConnected(); -} -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## List connections - -```php -$connections = $agent->getConnections(); -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Get connection -```php -/** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ -$connection = $agent->getConnection('cntl4qsIbA'); -``` - -### Agent Connection properties - -Name|Description|Type|Method -----|-----------|----|------ -id|-|-|`getId()` -guid|-|-|`getGuid()` -agent_id|-|-|`getAgentId()` -endpoint|-|-|`getEndpoint()` -process_version|-|-|`getProcessVersion()` -bundle_version|-|-|`getBundleVersion()` -agent_ip|-|-|`getAgentIp()` - - -# Agent tokens - -## Intro - -Agent tokens are used to authenticate Monitoring agents to the Monitoring Service. Multiple agents can share a single token. - -## Setup -```php -$tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; -$agentToken = $service->getAgentToken($tokenId); -``` - -## Create agent token -```php -$newToken = $service->getAgentToken(); -$newToken->create(array('label' => 'Foobar')); -``` - -## List agent tokens -```php -$agentTokens = $service->getAgentTokens(); - -foreach ($agentTokens as $token) { - echo $token->getLabel(); -} -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Update and delete Agent Token -```php -// Update -$token->update(array( - 'label' => 'New label' -)); - -// Delete -$token->delete(); -``` - -# Agent Host Information - -## Info - -An agent can gather host information, such as process lists, network configuration, and memory usage, on demand. You can use the host-information API requests to gather this information for use in dashboards or other utilities. - -## Setup -```php -$host = $monitoringService->getAgentHost(); -``` - -## Get some metrics -```php -$cpuInfo = $host->info('cpus'); -$diskInfo = $host->info('disks'); -$filesystemInfo = $host->info('filesystems'); -$memoryInfo = $host->info('memory'); -$networkIntInfo = $host->info('network_interfaces'); -$processesInfo = $host->info('processes'); -$systemInfo = $host->info('system'); -$userInfo = $host->info('who'); - -// What CPU models do we have? -foreach ($cpuInfo as $cpuMetric) { - echo $cpuMetric->model, PHP_EOL; -} - -// How many disks do we have? -echo $diskInfo->count(); - -// What's the available space on our ext4 filesystem? -foreach ($filesystemInfo as $filesystemMetric) { - if ($filesystemMetric->sys_type_name == 'ext4') { - echo $filesystemMetric->avail; - } -} -``` - -# Agent targets - -## Info - -Each agent check type gathers data for a related set of target devices on the server where the agent is installed. For example, `agent.network` gathers data for network devices. The actual list of target devices is specific to the configuration of the host server. By focusing on specific targets, you can efficiently narrow the metric data that the agent gathers. - -### List agent targets -```php -$targets = $service->getAgentTargets(); - -foreach ($targets as $target) { - echo $target->getType(); -} - -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/agents.html diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md index bf4a75652..b08caf4cd 100644 --- a/docs/userguide/CloudMonitoring/Alarms.md +++ b/docs/userguide/CloudMonitoring/Alarms.md @@ -1,56 +1,5 @@ # Alarms -## Info +Our docs have moved! Please visit the below link: -Alarms bind alerting rules, entities, and notification plans into a logical unit. Alarms are responsible for determining a state (```OK```, ```WARNING``` or ```CRITICAL```) based on the result of a Check, and executing a notification plan whenever that state changes. You create alerting rules by using the alarm DSL. For information about using the alarm language, refer to the [reference documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/alerts-language.html). - -## Setup - -Alarms are sub-resources of Entities: - -```php -$alarmId = 'alAAAA'; -$alarm = $check->getAlarm(); -``` - -For more information about working with Checks, please see the [appropriate documentation](Checks.md). - -## Attributes - -Name|Description|Required?|Method ----|---|---|--- -check_id|The ID of the check to alert on.|Required|`getCheckId()` -notification_plan_id|The ID of the notification plan to execute when the state changes.|Optional|`getNotificationPlanId()` -criteria|The alarm DSL for describing alerting conditions and their output states.|Optional|`getCriteria()` -disabled|Disable processing and alerts on this alarm|Optional|`isDisabled()` <`bool`> -label|A friendly label for an alarm.|Optional|`getLabel()` -metadata|Arbitrary key/value pairs.|Optional|`getMetadata()` - -## Create Alarm -```php -$alarm->create(array( - 'check_id' => 'chAAAA', - 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', - 'notification_plan_id' => 'npAAAAA' -)); -``` - -## List Alarms -```php -$alarms = $entity->getAlarms(); - -foreach ($alarms as $alarm) { - echo $alarm->getId(); -} -``` - -## Update and delete Alarm -```php -// Update -$alarm->update(array( - 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' -)); - -// Delete -$alarm->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/alarms.html diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md index 0f8fe48d0..ff943e592 100644 --- a/docs/userguide/CloudMonitoring/Changelogs.md +++ b/docs/userguide/CloudMonitoring/Changelogs.md @@ -1,14 +1,5 @@ # Changelogs -## Info +Our docs have moved! Please visit the below link: -The monitoring service records changelogs for alarm statuses. Changelogs are accessible as a Time Series Collection. By default the API queries the last 7 days of changelog information. - -## Working with Changelogs -```php -$changelog = $service->getChangelog(); - -foreach ($changelog as $item) { - $entity = $item->getEntityId(); -} -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/changelogs.html diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md index 265c0675d..5710aa465 100644 --- a/docs/userguide/CloudMonitoring/Checks.md +++ b/docs/userguide/CloudMonitoring/Checks.md @@ -1,141 +1,5 @@ # Checks -## Info +Our docs have moved! Please visit the below link: -A check is one of the foundational building blocks of the monitoring system. The check determines the parts or pieces of the entity that you want to monitor, the monitoring frequency, how many monitoring zones are originating the check, and so on. When you create a new check in the monitoring system, you specify the following information: - -- A name for the check -- The check's parent entity -- The type of check you're creating -- Details of the check -- The monitoring zones that will launch the check - -The check, as created, will not trigger alert messages until you create an alarm to generate notifications, to enable the creation of a single alarm that acts upon multiple checks (e.g. alert if any of ten different servers stops responding) or multiple alarms off of a single check. (e.g. ensure both that a HTTPS server is responding and that it has a valid certificate). - -## Setup - -Checks are sub-resources of Entities: -```php -$checkId = 'chAAAA'; -$check = $entity->getCheck($checkId); -``` - -## Attributes - -Name|Description|Required?|Data type ----|---|---|--- -type|The type of check.|Required|Valid check type. String (1..25 chars) -details|Details specific to the check type.|Optional|Array -disabled|Disables the check.|Optional|Boolean -label|A friendly label for a check.|Optional|String (1..255 chars) -metadata|Arbitrary key/value pairs.|Optional|Array -period|The period in seconds for a check. The value must be greater than the minimum period set on your account.|Optional|Integer (30..1800) -timeout|The timeout in seconds for a check. This has to be less than the period.|Optional|Integer (2..1800) - -###Attributes used for remote Checks - -Name|Description|Required?|Data type ----|---|---|--- -monitoring_zones_poll|List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks|Optional|Array -target_alias|A key in the entity's `ip_addresses` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target_hostname.|Optional|String (1..64 chars) -target_hostname|The hostname this check should target. This parameter is mutually exclusive with `target_alias`.|Optional|Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). -target_resolver|Determines how to resolve the check target.|Optional|`IPv4` or `IPv6` - -## Test parameters (before create) -```php -$params = array( - 'type' => 'remote.http', - 'details' => array( - 'url' => 'http://example.com', - 'method' => 'GET' - ), - 'monitoring_zones_poll' => array('mzlon'), - 'period' => '100', - 'timeout' => '30', - 'target_alias' => 'default', - 'label' => 'Website check 1' -); - -// You can do a test to see what would happen -// if a Check is launched with these params -$response = $entity->testNewCheckParams($params); - -echo $response->timestamp; // When was it executed? -echo $response->available; // Was it available? -echo $response->status; // Status code -``` -## Create a Check -```php -$entity->createCheck($params); -``` - -## Test existing Check -```php -// Set arg to TRUE for debug information -$response = $check->test(true); - -echo $response->debug_info; -``` - -## List Checks -```php -$checks = $entity->getChecks(); - -foreach ($checks as $check) { - echo $check->getId(); -} -``` - -### Update and delete Check -```php -// Update -$check->update(array('period' => 500)); - -// Delete -$check->delete(); -``` - -# Check types - -## Info - -Each check within the Rackspace Cloud Monitoring has a designated check type. The check type instructs the monitoring system how to check the monitored resource. **Note:** Users cannot create, update or delete check types. - -Check types for commonly encountered web protocols, such as HTTP (```remote.http```), IMAP (```remote.imap-banner```) , SMTP (```remote.stmp```), and DNS (```remote.dns```) are provided. Monitoring commonly encountered infrastructure servers like MySQL (```remote.mysql-banner```) and PostgreSQL (```remote.postgresql-banner```) are also available. Monitoring custom server uptime can be accomplished with the remote.tcp banner check to check for a protocol-defined banner at the beginning of a connection. Gathering metrics from server software to create alerts against can be accomplished using the remote.http check type and the 'extract' attribute to define the format. - -In addition to the standard Cloud Monitoring check types, you can also use agent check types if the Monitoring Agent is installed on the server you are monitoring. For a list of available check types, see the [official API documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/appendix-check-types.html). - -Checks generate metrics that alarms will alert based upon. The metrics generated often times depend on the check's parameters. For example, using the 'extract' attribute on the remote.http check, however the default metrics will always be present. To determine the exact metrics available, the Test Check API is provided. - -## Setup - -If you want to see the type for an existing Check: - -```php -/** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ -$checkType = $check->getCheckType(); -``` - -Alternatively, you can retrieve a specific type based on its ID: - -```php -$checkTypeId = 'remote.dns'; -$checkType = $service->getCheckType($checkTypeId); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|--- -type|The name of the supported check type.|String|`getType()` -fields|Check type fields.|Array|`getFields()` -supported_platforms|Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime|Array|`getSupportedPlatforms()` - -## List all possible check types -```php -$checkTypes = $service->getCheckTypes(); - -foreach ($checkTypes as $checkType) { - echo $checkType->getId(); -} -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/checks.html diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md index c9885604b..beed807a2 100644 --- a/docs/userguide/CloudMonitoring/Entities.md +++ b/docs/userguide/CloudMonitoring/Entities.md @@ -1,55 +1,5 @@ # Entities -## Info +Our docs have moved! Please visit the below link: -An entity is the target of what you are monitoring. For example, you can create an entity to monitor your website, a particular web service, or your Rackspace server. Note that an entity represents only one item in the monitoring system -- if you wanted to monitor each server in a cluster, you would create an entity for each of the servers. You would not create a single entity to represent the entire cluster. - -An entity can have multiple checks associated with it. This allows you to check multiple services on the same host by creating multiple checks on the same entity, instead of multiple entities each with a single check. - -## Setup - -```php -$entity = $service->getEntity(); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [services](Service.md). - -## Attributes - -Name|Description|Required?|Data type|Method ----|---|---|---|--- -label|Defines a name for the entity.|Required|String (1..255 chars)|`getLabel()` -agent_id|Agent to which this entity is bound to.|Optional|String matching the regex: `/^[-\.\w]{1,255}$/`|`getAgentId()` -ip_addresses|Hash of IP addresses that can be referenced by checks on this entity.|Optional|Array|`getIpAddresses()` -metadata|Arbitrary key/value pairs that are passed during the alerting phase.|Optional|`OpenCloud\Common\Metadata`|`getMetadata()` - -## Create Entity -```php -$service->createEntity(array( - 'label' => 'Brand New Entity', - 'ip_addresses' => array( - 'default' => '127.0.0.4', - 'b' => '127.0.0.5', - 'c' => '127.0.0.6', - 'test' => '127.0.0.7' - ), - 'metadata' => array( - 'all' => 'kinds', - 'of' => 'stuff', - 'can' => 'go', - 'here' => 'null is not a valid value' - ) -)); -``` - - -## Update and delete Entity -```php -// Update -$entity->update(array( - 'label' => 'New label for my entity' -)); - -// Delete -$entity->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/entities.html diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md index 6799f8881..0b275c97e 100644 --- a/docs/userguide/CloudMonitoring/Metrics.md +++ b/docs/userguide/CloudMonitoring/Metrics.md @@ -1,79 +1,5 @@ # Metrics -## Info +Our docs have moved! Please visit the below link: -When Monitoring checks run, they generate metrics. These metrics are stored as full resolution data points in the Cloud Monitoring system. Full resolution data points are periodically rolled up (condensed) into coarser data points. - -Depending on your needs, you can use the metrics API to fetch individual data points (fine-grained) or rolled up data points (coarse-grained) over a period of time. - -## Data Granularity - -Cloud Monitoring supports several granularities of data: full resolution data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. - -When you fetch metrics data points, you specify several parameters to control the granularity of data returned: - -- A time range for the points -- Either the number of points you want returned OR the resolution of the data you want returned - -When you query by points, the API selects the resolution that will return you the number of points you requested. The API makes the assumption of a 30 second frequency, performs the calculation, and selects the appropriate resolution. - -**Note:** Because the API performs calculations to determine the points returned for a particular resolution, the number of points returned may differ from the specific number of points you request. - -Consider that you want to query data for a 48-hour time range between the timestamps `from=1354647221000` and `to=1358794421000` ( **specified in Unix time, based on the number of milliseconds that have elapsed since January 1, 1970** ). The following table shows the number of points that the API returns for a given resolution. - -#### Specifying resolution to retrieve data in 48 hour period - -You specify resolution...|API returns points... ----|--- -FULL|5760 -MIN5|576 -MIN20|144 -MIN60|48 -MIN240|12 -MIN1440|2 - -#### Specifying number of points to retrieve data in 48 hour period - -You specify points in the range...|API calculates resolution ----|--- -3168-∞|FULL -360-3167|MIN5 -96-359|MIN20 -30-95|MIN60 -7-29|MIN240 -0-6|MIN1440 - -#### Data Point Expiration - -Cloud Monitoring expires data points according to the following schedule: - -Resolution|Expiration ----|--- -FULL|2 days -MIN5|7 days -MIN20|15 days -MIN60|30 days -MIN240|60 days -MIN1440|365 days - -## Setup - -Metrics are sub-resources of Checks. For more information about working with Checks, please see the [relevant documentation](Checks.md). - -## List all metrics -```php -$metrics = $check->getMetrics(); - -foreach ($metrics as $metric) { - echo $metric->getName(); -} -``` - -## Fetch data points -```php -$data = $check->fetchDataPoints('mzdfw.available', array( - 'resolution' => 'FULL', - 'from' => 1369756378450, - 'to' => 1369760279018 -)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/metrics.html diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md index 81a890f56..129881fae 100644 --- a/docs/userguide/CloudMonitoring/Notifications.md +++ b/docs/userguide/CloudMonitoring/Notifications.md @@ -1,203 +1,5 @@ # Notifications -## Info +Our docs have moved! Please visit the below link: -A notification is a destination to send an alarm; it can be a variety of different types, and will evolve over time. - -For instance, with a webhook type notification, Cloud Monitoring posts JSON formatted data to a user-specified URL on an alert condition (Check goes from `OK` -> `CRITICAL` and so on). - -## Setup - -```php -$id = 'ntAAAA'; -$notification = $service->getNotification($id); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|---|--- -details|A hash of notification specific details based on the notification type.|Array|`getDetails()` -label|Friendly name for the notification.|String (1..255 chars)|`getLabel()` -type|The notification type to send.|String. Either `webhook`, `email`, or `pagerduty`|`getType()` - -## Test parameters -```php -$params = array( - 'label' => 'My webhook #1', - 'type' => 'webhook', - 'details' => array( - 'url' => 'http://example.com' - ) -); - -// Test it -$response = $notification->testParams($params); - -if ($response->status == 'Success') { - echo $response->message; -} -``` - -## Create Notification - -```php -$notification->create($params); -``` - -## Test existing notification -```php -$response = $notification->testExisting(true); -echo $response->debug_info; -``` - -## List Notifications -```php -$notifications = $service->getNotifications(); - -foreach ($notifications as $notification) { - echo $notification->getId(); -} -``` - -## Update and delete Notifications -```php -// Update -$notification->update(array( - 'label' => 'New notification label' -)); - -// Delete -$notification->delete(); -``` - -# Notification types - -## Info - -Pretty self-explanatory. Rackspace Cloud Monitoring currently supports the following notification types: - -#### Webhook - -Industry-standard web hooks, where JSON is posted to a configurable URL. It has these attributes: - -Name|Description|Data type ----|---|--- -address|Email address to send notifications to|Valid email - -#### Email - -Email alerts where the message is delivered to a specified address. It has these attributes: - -Name|Description|Data type ----|---|--- -url|An HTTP or HTTPS URL to POST to|Valid URL - -## Setup - -If you've already set up a main Notification object, and want to access functionality for this Notification's particular Notification Type, you can access its property: - -```php -$type = $notification->getNotificationType(); -``` - -Alternatively, you can retrieve an independent resource using the ID: - -```php -$typeId = 'pagerduty'; -$type = $service->getNotificationType($typeId); -``` - -## List all possible notification types -```php -$types = $service->getNotificationTypes(); - -foreach ($types as $type) { - echo sprintf('%s %s', $type->getName(), $type->getDescription()); -} -``` - -# Notification plans - -## Info - -A notification plan contains a set of notification actions that Rackspace Cloud Monitoring executes when triggered by an alarm. Rackspace Cloud Monitoring currently supports webhook and email notifications. - -Each notification state can contain multiple notification actions. For example, you can create a notification plan that hits a webhook/email to notify your operations team if a warning occurs. However, if the warning escalates to an Error, the notification plan could be configured to hit a different webhook/email that triggers both email and SMS messages to the operations team. The notification plan supports the following states: - -- Critical -- Warning -- OK - -A notification plan, `npTechnicalContactsEmail`, is provided by default which will email all of the technical contacts on file for an account whenever there is a state change. - -## Setup - -```php -$planId = 'npAAAA'; -$plan = $service->getNotificationPlan(); -``` - -## Attributes - -Name|Description|Required?|Data type|Method ----|---|---|---|--- -label|Friendly name for the notification plan.|Required|String (1..255 chars)|`getLabel()` -critical_state|The notification list to send to when the state is `CRITICAL`.|Optional|Array|`getCriticalState()` -ok_state|The notification list to send to when the state is `OK`.|Optional|Array|`getOkState()` -warning_state|The notification list to send to when the state is `WARNING`.|Optional|Array|`getWarningState()` - -## Create Notification Plan -```php -$plan->create(array( - 'label' => 'New Notification Plan', - 'critical_state' => array('ntAAAA'), - 'ok_state' => array('ntBBBB'), - 'warning_state' => array('ntCCCC') -)); -``` - -## Update and delete Notification Plan - -```php -// Update -$plan->update(array( - 'label' => 'New label for my plan' -)); - -// Delete -$plan->delete(); -``` - -# Alarm Notification History - -## Info - -The monitoring service keeps a record of notifications sent for each alarm. This history is further subdivided by the check on which the notification occurred. Every attempt to send a notification is recorded, making this history a valuable tool in diagnosing issues with unreceived notifications, in addition to offering a means of viewing the history of an alarm's statuses. - -Alarm notification history is accessible as a Time Series Collection. By default alarm notification history is stored for 30 days and the API queries the last 7 days of information. - -## Setup - -Notification History is a sub-resource of an Alarm. For more information about working with Alarms, please consult the relevant [documentation](Alarms.md). - -## Discover which Checks have a Notification History - -This operation list checks for which alarm notification history is available: - -```php -$checks = $alarm->getRecordedChecks(); -``` - -## List Alarm Notification History for a particular Check -```php -$checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); -``` - -## Get a particular Notification History item -```php -$checkId = 'chAAAA'; -$itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; - -$singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/notifications.html diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md index 5647eedc2..6445de039 100644 --- a/docs/userguide/CloudMonitoring/Service.md +++ b/docs/userguide/CloudMonitoring/Service.md @@ -1,16 +1,5 @@ # Cloud Monitoring Service -Initializing the Cloud Monitoring is easy - and can be done in a similar way to all other Rackspace services: +Our docs have moved! Please visit the below link: -1. Create client and pass in auth details. For more information about creating clients, please consult the [Client documentation](../Clients.md). -2. Use the factory method, specifying additional parameters where necessary: - -```php -$service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); -``` - -All three parameters are optional - if not specified, it will revert to the service's default values which are: - -- Name = `cloudMonitoring` -- Region = `DFW` -- URL type = `publicURL` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/index.html diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md index b6b505efc..ac0017db0 100644 --- a/docs/userguide/CloudMonitoring/Views.md +++ b/docs/userguide/CloudMonitoring/Views.md @@ -1,19 +1,5 @@ # Views -## Info +Our docs have moved! Please visit the below link: -Views contain a combination of data that usually includes multiple, different objects. The primary purpose of a view is to save API calls and make data retrieval more efficient. Instead of doing multiple API calls and then combining the result yourself, you can perform a single API call against the view endpoint. - -## List all Views - -```php -$views = $service->getViews(); - -foreach ($views as $view) { - $entity = $view->getEntity(); - - echo $view->getTimestamp(); -} - ``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/views.html diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md index 338bb1053..fcad4b900 100644 --- a/docs/userguide/CloudMonitoring/Zones.md +++ b/docs/userguide/CloudMonitoring/Zones.md @@ -1,46 +1,5 @@ # Zones -## Info +Our docs have moved! Please visit the below link: -A monitoring zone is a location that Rackspace Cloud Monitoring collects data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". It is an abstraction for a general location from which data is collected. - -An "endpoint," also known as a "collector," collects data from the monitoring zone. The endpoint is mapped directly to an individual machine or a virtual machine. A monitoring zone contains many endpoints, all of which will be within the IP address range listed in the response. The opposite is not true, however, as there may be unallocated IP addresses or unrelated machines within that IP address range. - -A check references a list of monitoring zones it should be run from. - -## Setup -```php -$zoneId = 'mzAAAAA'; -$zone = $monitoringService->getMonitoringZone($zoneId); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|---|--- -country_code|Country Code|String longer than 2 characters|`getCountryCode()` -label|Label|String|`getLabel()` -source_ips|Source IP list|Array|`getSourceIps()` - -## List all zones - -```php -$zones = $service->getMonitoringZones(); -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Perform a traceroute - -```php -$traceroute = $zone->traceroute(array( - 'target' => 'http://test.com', - 'target_resolver' => 'IPv4' -)); - -// How many hops? -echo count($traceroute); - -// What was the first hop's IP? -echo $traceroute[0]->ip; -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/zones.html diff --git a/docs/userguide/Compute/Images.md b/docs/userguide/Compute/Images.md index d09b0b0eb..6cc06d37f 100644 --- a/docs/userguide/Compute/Images.md +++ b/docs/userguide/Compute/Images.md @@ -1,65 +1,5 @@ # Compute Images -## Intro +Our docs have moved! Please visit the below link: -An image is a collection of files for a specific operating system that you use to create or rebuild a server. Rackspace provides prebuilt images. You can also create custom images from servers that you have launched. - -In addition to creating images manually, you can also schedule images of your server automatically. Please consult the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/scheduled_images.html) for more information about this extension, including enabling and disabling scheduled images and showing scheduled images. - -With standard servers, the entire disk (OS and data) is captured in the image. With Performance servers, only the system disk is captured in the image. The data disks should be backed up using Cloud Backup or Cloud Block Storage to ensure availability in case you need to rebuild or restore a server. - -## Setup - -You first need to setup a Compute service. For information, please consult the [Compute service](Service.md) documentation. - -## List images - -```php -$images = $service->imageList(); - -foreach ($images as $image) { - -} -``` - -For more information about [iterators](docs/userguide/Iterators.md), please consult the official documentation. - -### Query parameters - -You can also refine the list of images returned by providing specific URL parameters: - -|Field name|Description| -|---|---| -|server|Filters the list of images by server. Specify the server reference by ID or by full URL.| -|name|Filters the list of images by image name.| -|status|Filters the list of images by status. In-flight images have a status of `SAVING` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the `OpenCloud\Compute\Constants\ImageState` class. Images with an `ACTIVE` status are available for use.| -|changes-since|Filters the list of images to those that have changed since the changes-since time. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ChangesSince.html) for more information.| -|marker|The ID of the last item in the previous list. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Paginated_Collections-d1e664.html) for more information.| -|limit|Sets the page size. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Paginated_Collections-d1e664.html) for more information.| -|type|Filters base Rackspace images or any custom server images that you have created. Can either be `BASE` or `SNAPSHOT`.| - -### Example - -You can return more information about each image by setting the `$details` argument to `true`. The second argument can be an array of query parameters: - -```php -use OpenCloud\Compute\Constants\ImageState; - -$list = $service->imageList(true, array( - 'server' => 'fooBar', - 'status' => ImageState::ACTIVE -)); -``` - -## Get an image - -```php -$imageId = '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001'; -$image = $service->image($imageId); -``` - -## Delete an image - -```php -$image->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/images.html diff --git a/docs/userguide/Compute/Keypair.md b/docs/userguide/Compute/Keypair.md index e314db32c..37e5080fe 100644 --- a/docs/userguide/Compute/Keypair.md +++ b/docs/userguide/Compute/Keypair.md @@ -1,91 +1,5 @@ # Keypairs -## Generate new keypair +Our docs have moved! Please visit the below link: -This operation creates a new keypair under a provided name; the public key value is automatically generated for you. - -```php -$keypair = $service->keypair(); - -$keypair->create(array( - 'name' => 'jamie_keypair_1' -)); - -echo $keypair->getPublicKey(); - -// Save private key to a file so you can use it to SSH into -// your server later. -$sshPrivateKeyFilename = 'jamie_keypair_1_rsa'; -$privateKey = $keypair->getPrivateKey(); -file_put_contents($sshPrivateKeyFilename, $privateKey); -chmod($sshPrivateKeyFilename, 0600); -``` - -## Upload existing keypair - -This operation creates a new keypair under a provided name using a provided public key value. This public key will probably exist on your local filesystem, and so provide easy access to your server when uploaded. - -```php -$keypair = $service->keypair(); - -$key = <<create(array( - 'name' => 'jamie_macbook', - 'publicKey' => $key -)); - -``` - -## List keypairs - -To list all existing keypairs: - -```php -$keys = $service->listKeypairs(); - -foreach ($keys as $key) { - // ... -} -``` - -For more information about iterators, please see [the docs](../Iterators.md). - -## Delete keypairs - -To delete a specific keypair: - -```php -$keypair->delete(); -``` - -## Creating a server with a keypair - -In order to spawn an instance with a saved keypair (allowing you to SSH in without passwords), you create your server -using the same operation as usual, with one extra parameter: - -```php -use Guzzle\Http\Exception\BadResponseException; -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'New server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor, - 'networks' => array( - $compute->network(Network::RAX_PUBLIC), - $compute->network(Network::RAX_PRIVATE) - ), - 'keypair' => 'jamie_macbook' - )); -} catch (BadResponseException $e) { - // error... -} -``` - -So, as you can see, you specify the **name** of an existing keypair that you previously created on the API. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/keypairs.html diff --git a/docs/userguide/Compute/Server.md b/docs/userguide/Compute/Server.md index 8cc0d17e7..e43e1cc42 100644 --- a/docs/userguide/Compute/Server.md +++ b/docs/userguide/Compute/Server.md @@ -1,180 +1,5 @@ # Servers -## Intro +Our docs have moved! Please visit the below link: -A server is a virtual machine instance in the Cloud Servers environment. - -## Setup - -Server objects are instantiated from the Compute service. For more details, see the [Service](Service.md) docs. - -## Get server - -The easiest way to retrieve a specific server is by its unique ID: - -```php -$serverId = 'ef08aa7a-b5e4-4bb8-86df-5ac56230f841'; -$server = $service->server($serverId); -``` - -## List servers - -You can list servers in two different ways: - -* return an _overview_ of each server (ID, name and links) -* return _detailed information_ for each server - -Knowing which option to use might help save unnecessary bandwidth and reduce latency. - -```php -// overview -$servers = $service->serverList(); - -// detailed -$servers = $service->serverList(true); -``` - -### URL parameters for filtering servers - -Name|Description|Type ----|---|--- -image|The image ID|string -flavor|The flavor ID|string -name|The server name|string -status|The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult `OpenCloud\Compute\Constants\ServerState`|string -changes-since|Value for checking for changes since a previous request|A valid ISO 8601 dateTime (2011-01-24T17:08Z) -RAX-SI:image_schedule|If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule.|bool - -## Create server - -### Using an image - -There are a few parameter requirements when creating a server using an image: - -* **name** - needs to be a string; -- **flavor** - a `OpenCloud\Compute\Resource\Flavor` object, that is populated with the values of a real API flavor; -* **image** - a `OpenCloud\Compute\Resource\Image` object, that is populated with the values of a real API image; - -Firstly we need to find our flavor and image using their UUIDs. For more information about these concepts, including how to find flavor/image UUIDs, please consult §§ 3-4 in the [Getting Started guide](https://github.com/rackspace/php-opencloud/blob/master/docs/getting-started.md#3-select-your-server-image). - -```php -$ubuntuImage = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -$twoGbFlavor = $compute->flavor('4'); -``` - -Now we're ready to create our instance: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf('Status: %s\nBody: %s\nHeaders: %s', $statusCode, $responseBody, implode(', ', $headers); -} -``` - -It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. - -### Using a bootable volume - -There are a few parameter requirements when creating a server using a bootable volume: - -* **name** - needs to be a string; -* **flavor** - a `OpenCloud\Compute\Resource\Flavor` object, that is populated with the values of a real API flavor; -* **volume** - a `OpenCloud\Volume\Resource\Volume` object, that is populated with the values of a real API volume; - -Firstly we need to find our flavor and volume using their IDs. - -```php -$volumeService = $client->volumeService(); -$bootableVolume = $volumeService->volume(''); -$flavor = $compute->flavor(''); -``` - -Now we're ready to create our instance: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'volume' => $bootableVolume, - 'flavor' => $flavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - // No! Something failed. Let's find out: - echo $e->getRequest() . PHP_EOL . PHP_EOL; - echo $e->getResponse(); -} -``` - -It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. - -### Create parameters - -Name|Description|Type|Required ----|---|---|--- -name|The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync.|string|Yes -flavor|A populated `OpenCloud\Compute\Resource\Flavor` object representing your chosen flavor|object|Yes -image|A populated `OpenCloud\Compute\Resource\Image` object representing your chosen image|object|No, if volume is specified -volume|A populated `OpenCloud\Volume\Resource\Volume` object representing your chosen bootable volume|object|No, if image is specified -volumeDeleteOnTermination|`true` if the bootable volume should be deleted when the server is terminated; `false`, otherwise|boolean|No; default = `false` -OS-DCF:diskConfig|The disk configuration value. You can use two options: `AUTO` or `MANUAL`.

          `AUTO` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. `AUTO` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.

          `MANUAL` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration.|string|No -networks|An array of populated `OpenCloud\Compute\Resource\Network` objects that indicate which networks your instance resides in.|array|No -metadata|An array of arbitrary data (key-value pairs) that adds additional meaning to your server.|array|No -keypair|You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication.|array|No -personality|Files that you can upload to your newly created instance's filesystem.|array|No - -### Creating a server with keypairs - -Please see the [Keypair](Keypair.md) docs for more information. - -### Creating a server with personality files - -Before you execute the create operation, you can add "personality" files to your `OpenCloud\Compute\Resource\Server` object. These files are structured as a flat array. - -```php -$server->addFile('/var/test_file', 'FILE CONTENT'); -``` - -As you can see, the first parameter represents the filename, and the second is a string representation of its content. When the server is created these files will be created on its local filesystem. For more information about server personality files, please consult the [official documentation](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Server_Personality-d1e2543.html). - -## Update server - -You can update certain attributes of an existing server instance. These attributes are detailed in the next section. - -```php -$server->update(array( - 'name' => 'NEW SERVER NAME' -)); -``` - -### Updatable attributes - -name|description ----|--- -name|The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. -accessIPv4|The IP version 4 address. -accessIPv6|The IP version 6 address. - -## Delete server - -```php -$server->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/Compute/Service.md b/docs/userguide/Compute/Service.md index faff788bb..ba49ff4e4 100644 --- a/docs/userguide/Compute/Service.md +++ b/docs/userguide/Compute/Service.md @@ -1,18 +1,5 @@ # Compute service -## Setup +Our docs have moved! Please visit the below link: -To instantiate a Compute service object, you first need to setup a Rackspace/OpenStack client. To do this, or for more -information, please consult the [Clients documentation](../Clients.md). - -```php -$service = $client->computeService(); -``` - -If no arguments are provided to the above method, certain values are set to their default values: - -|Param|Default value| -|---|---| -|`$name`|cloudServersOpenStack| -|`$region`|DFW| -|`$urltype`|publicURL| \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index f0d94efee..d8d3a12d3 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -1,230 +1,5 @@ # Domains -A domain is an entity/container of all DNS-related information containing one or more records. +Our docs have moved! Please visit the below link: -## Setup - -Limit methods will be called on the DNS service, an instance of `OpenCloud\DNS\Service`. Please see the [DNS service](Service.md) documentation for setup instructions. - -## Get domain - -To retrieve a specific domain, you will need the domain's **id**, not its domain name. - -```php -$domain = $service->domain(12345); -``` - -If you are having trouble remembering or accessing the domain ID, you can do a domain list search for your domain and then access its ID. - -## List domains - -These calls provide a list of all DNS domains manageable by a given account. The resulting list is flat, and does not break the domains down hierarchically by subdomain. All representative domains are included in the list, even if a domain is conceptually a subdomain of another domain in the list. - -```php -$domains = $service->domainList(); - -# Return detailed information for each domain -$domains = $service->domainList(true); -``` - -Please consult the [iterator documentation](/docs/userguide/Iterators.md) for more information about iterators. - -### Filter parameters - -You can filter the aforementioned search by using the `name` parameter in a key/value array supplied as a method argument. For example, providing `array('name' => 'hoola.com')` will return hoola.com and similar names such as main.hoola.com and sub.hoola.com. - -```php -$hoolaDomains = $service->domainList(array( - 'name' => 'hoola.com' -)); -``` - -Filter criteria may consist of: - -- Any letter (A-Za-z) -- Numbers (0-9) -- Hyphen ("-") -- 1 to 63 characters - -Filter criteria should not include any of the following characters: - -> ' + , | ! " £ $ % & / ( ) = ? ^ * ç ° § ; : _ > ] [ @ à, é, ò - -### Finding a domain ID - -If you know a domain's name, but not its unique identifier, you can do this: - -```php -$domains = $service->domainList(array( - 'name' => 'foo.com' -)); - -foreach ($domains as $domain) { - $id = $domain->id; -} -``` - -## List domain changes - -This call shows all changes to the specified domain since the specified date/time. The since parameter is optional and defaults to midnight of the current day. - -```php -$changes = $domain->changes(); - -# Changes since last week -$since = date('c', strtotime('last week')); -$changes = $domain->changes($since); - -foreach ($changes->changes as $change) { - printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); - - foreach ($change->changeDetails as $detail) { - printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); - } -} -``` - -## Export domain - -This call provides the BIND (Berkeley Internet Name Domain) 9 formatted contents of the requested domain. This call is for a single domain only, and as such, does not traverse up or down the domain hierarchy for details (that is, no subdomain information is provided). - -```php -$asyncResponse = $domain->export(); -$body = $asyncResponse->waitFor('COMPLETED'); -echo $body['contents']; -``` - -## Create domain - -A domain is composed of DNS records (e.g. `A`, `CNAME` or `MX` records) and an optional list of sub-domains. You will need to specify these before creating the domain itself: - -```php -// get empty object -$domain = $service->domain(); - -// add A record -$aRecord = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 -)); -$domain->addRecord($aRecord); - -// add optional C record -$cRecord = $domain->record(array( - 'type' => 'CNAME', - 'name' => 'www.example.com', - 'data' => 'example.com', - 'ttl' => 3600 -)); -$domain->addRecord($cRecord); - -// add optional MX record -$mxRecord = $domain->record(array( - 'type' => 'MX', - 'data' => 'mail.example.com', - 'name' => 'example.com', - 'ttl' => 3600, - 'priority' => 5 -)); -$domain->addRecord($mxRecord); - -// add optional NS records -$nsRecord1 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns1.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 -)); -$domain->addRecord($nsRecord1); - -$nsRecord2 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns2.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 -)); -$domain->addRecord($nsRecord2); - -// add optional subdomains -$sub1 = $domain->subdomain(array( - 'emailAddress' => 'foo@example.com', - 'name' => 'dev.example.com', - 'comment' => 'Dev portal' -)); -$domain->addSubdomain($sub1); - -// send to API -$domain->create(array( - 'emailAddress' => 'webmaster@example.com', - 'ttl' => 3600, - 'name' => 'example.com', - 'comment' => 'Optional comment' -)); -``` - -## Clone domain - -This call will duplicate a single existing domain configuration with a new domain name for the specified Cloud account. By default, all records and, optionally, subdomain(s) are duplicated as well. - -The method signature you will need to use is: - -```php -cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) -``` - -Name|Data type|Default|Description ----|---|---|--- -`$newDomainName`|`string`|-|The new name for your cloned domain -`$subdomains`|`bool`|`true`|Set to `TRUE` to clone all the subdomains for this domain -`$comments`|`bool`|`true`|Set to `TRUE` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. -`$email`|`bool`|`true`|Set to `TRUE` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. - -For example: - -```php -$asyncResponse = $domain->cloneDomain('new-name.com', true); -``` - -## Import domain - -This call provisions a new DNS domain under the account specified by the BIND 9 formatted file configuration contents defined in the request object. - -You will need to ensure that the BIND 9 formatted file configuration contents are valid by adhering to the following rules: - -- Each record starts on a new line and on the first column. If a record will not fit on one line, use the BIND_9 line continuation convention where you put a left parenthesis and continue the one record on the next line and put a right parenthesis when the record ends. For example, - - > example2.net. 3600 IN SOA dns1.stabletransit.com. ( - sample@rackspace.com. 1308874739 3600 3600 3600 3600) - -- The attribute values of a record must be separated by a single blank or tab. No other white space characters. - -- If there are any NS records, the data field should not be dns1.stabletransit.com or dns2.stabletransit.com. They will result in "duplicate record" errors. - -For example: - -```php -$bind9Data = <<import($bind9Data); -``` - -## Modify domain - -This call modifies DNS domain(s) attributes only. Only the TTL, email address and comment attributes of a domain can be modified. Records cannot be added, modified, or removed through this API operation - you will need to use the [add records](/docs/userguide/DNS/Records.md#add-record), [modify records](/docs/userguide/DNS/Records.md#modify-record) or [remove records](/docs/userguide/DNS/Records.md#delete-record) operations respectively. - -```php -$domain->update(array( - 'ttl' => ($domain->ttl + 100), - 'emailAddress' => 'new_dev@foo.com' -)); -``` - -## Remove domain - -```php -$domain->delete(); -``` +https://doc.php-opencloud.com/en/latest/services/dns/domains.html diff --git a/docs/userguide/DNS/Limits.md b/docs/userguide/DNS/Limits.md index 6e7aeb042..70fac6504 100644 --- a/docs/userguide/DNS/Limits.md +++ b/docs/userguide/DNS/Limits.md @@ -1,58 +1,5 @@ # Limits -## Setup +Our docs have moved! Please visit the below link: -Limit methods will be called on the DNS service, an instance of `OpenCloud\DNS\Service`. Please see the [DNS service](Service.md) documentation for setup instructions. - -## List all limits - -This call provides a list of all applicable limits for the specified account. - -```php -$limits = $service->limits(); -``` - -### Absolute limits - -There are some absolute limits imposed on your account - such as how many domains you can create and how many records you can create for each domain: - -```php -$absoluteLimits = $limits->absolute; - -# Domain limit -echo $absoluteLimits->domains; - -# Record limit per domain -echo $absoluteLimits->{'records per domain'}; -``` - -## List limit types - -To find out the different limit types you can query, run: - -```php -$limitTypes = $service->limitTypes(); -``` - -will return: - -``` -array(3) { - [0] => - string(10) "RATE_LIMIT" - [1] => - string(12) "DOMAIN_LIMIT" - [2] => - string(19) "DOMAIN_RECORD_LIMIT" -} -``` - -## Query a specific limit - -```php -$limit = $service->limits('DOMAIN_LIMIT'); - -echo $limit->absolute->limits->value; - ->>> 500 -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/limits.html diff --git a/docs/userguide/DNS/Records.md b/docs/userguide/DNS/Records.md index b8f71bda2..52998b955 100644 --- a/docs/userguide/DNS/Records.md +++ b/docs/userguide/DNS/Records.md @@ -1,87 +1,5 @@ # Records -A DNS record belongs to a particular domain and is used to specify information about the domain. +Our docs have moved! Please visit the below link: -There are several types of DNS records. Examples include mail exchange (MX) records, which specify the mail server for a particular domain, and name server (NS) records, which specify the authoritative name servers for a domain. - -It is represented by the `OpenCloud\DNS\Resource\Record` class. Records belong to a [Domain](Domains.md). - -## Get record - -In order to retrieve details for a specific DNS record, you will need its **id**: - -```php -$record = $domain->record('NS-1234567'); -``` - -If you do not have this ID at your disposal, you can traverse the record collection and do a string comparison (detailed below). - -## List records - -This call lists all records configured for the specified domain. - -```php -$records = $domain->recordList(); - -foreach ($records as $record) { - printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -### Query parameters - -You can pass in an array of query parameters for greater control over your search: - -Name|Data type|Default|Description ----|---|---|--- -`type`|`string`|The record type -`name`|`string`|The record name -`data`|`string`|Data for this record - -### Find a record ID from its name - -For example: - -```php -$records = $domain->recordList(array( - 'name' => 'imap.example.com', - 'type' => 'MX' -)); - -foreach ($records as $record) { - $recordId = $record->id; -} -``` - -## Add record - -This call adds a new record to the specified domain: - -```php -$record = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 -)); - -$record->create(); -``` - -Please be aware that records that are added with a different hostname than the parent domain might fail silently. - -## Modify record - -```php -$record = $domain->record(123456); -$record->ttl -= 100; -$record->update(); -``` - -## Delete record - -```php -$record->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/records.html diff --git a/docs/userguide/DNS/Reverse-DNS.md b/docs/userguide/DNS/Reverse-DNS.md index a269a0057..e930431c1 100644 --- a/docs/userguide/DNS/Reverse-DNS.md +++ b/docs/userguide/DNS/Reverse-DNS.md @@ -1,71 +1,5 @@ # Reverse DNS -DNS usually determines an IP address associated with a domain name. Reverse DNS is the opposite process: resolving a domain name from an IP address. This is usually achieved with a domain name pointer. +Our docs have moved! Please visit the below link: -## Get PTR record - -PTR records refer to a parent device: either a Cloud Server or a Cloud Load Balancer with a public virtual IP address. You must supply a fully formed resource object in order to retrieve either one's PTR record: - -```php -/** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ - -$ptr = $service->ptrRecord(array( - 'parent' => $parent -)); -``` - -So, in the above example, a `$parent` could be an instance of `OpenCloud\Compute\Resource\Server` or `OpenCloud\LoadBalancer\Resource\LoadBalancer` - because they both implement `OpenCloud\DNS\Resource\HadPtrRecordsInterface`. Please consult the [server documentation](../Compute/Server.md) and [load balancer documentation](../LoadBalancer/USERGUIDE.md) for more detailed usage instructions. - -## List PTR records - -```php -/** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ - -$ptrRecords = $service->ptrRecordList($parent); - -foreach ($ptrRecords as $ptrRecord) { - -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -## Add PTR record - -```php -$parent = $computeService->server('foo-server-id'); - -$ptr = $dnsService->ptrRecord(array( - 'parent' => $parent, - 'ttl' => 3600, - 'name' => 'example.com', - 'type' => 'PTR', - 'data' => '192.0.2.7' -)); - -$ptr->create(); -``` - -Here is a table that explains the above attributes: - -Name|Description|Required ----|---|--- -type|Specifies the record type as "PTR".|Yes -name|Specifies the name for the domain or subdomain. Must be a valid domain name.|Yes -data|The data field for PTR records must be a valid IPv4 or IPv6 IP address.|Yes -ttl|If specified, must be greater than 300. Defaults to 3600 if no TTL is specified.|No -comment|If included, its length must be less than or equal to 160 characters.|No - -## Modify PTR record - -```php -$ptr->update(array( - 'ttl' => $ptr->ttl * 2 -)); -``` - -## Delete PTR record - -```php -$ptr->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/reverse-dns.html diff --git a/docs/userguide/DNS/Service.md b/docs/userguide/DNS/Service.md index ccabb1242..3404ed7c7 100644 --- a/docs/userguide/DNS/Service.md +++ b/docs/userguide/DNS/Service.md @@ -1,11 +1,5 @@ # DNS Service -To instantiate a Compute service object, you first need to setup a -Rackspace/OpenStack client. To do this, or for more information, please consult -the [Clients documentation](../Clients.md). +Our docs have moved! Please visit the below link: -You will then need to run: - -```php -$service = $client->dnsService(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/index.html diff --git a/docs/userguide/Database/README.md b/docs/userguide/Database/README.md index 9a5822e5b..199d049e4 100644 --- a/docs/userguide/Database/README.md +++ b/docs/userguide/Database/README.md @@ -1,116 +1,5 @@ # Databases -A **cloud database** is a MySQL relational database service that allows -customers to programatically provision database instances of varying virtual -resource sizes without the need to maintain and/or update MySQL. +Our docs have moved! Please visit the below link: -## Getting started - -### 1. Instantiate a Rackspace client. - -```php -use OpenCloud\Rackspace; -use OpenCloud\Common\Constants\State; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### 2. Create a database server instance. - -```php -$databaseService = $client->databaseService('cloudDatabases', 'DFW'); - -$twoGbFlavor = $databaseService->flavor(3); - -$dbInstance = $databaseService->instance(); -$dbInstance->name = 'Demo database instance'; -$dbInstance->volume = new stdClass(); -$dbInstance->volume->size = 20; // GB -$dbInstance->flavor = $twoGbFlavor; -$dbInstance->create(); - -$dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { - - printf("Database instance build status: %s\n", $dbInstance->status); - -}); -``` - -The example above creates a database server instance with 20GB of disk space and -2GB of memory, then waits for it to become ACTIVE. - -### 3. Create a database on the database server instance. - -```php -$db = $dbInstance->database(); -$db->name = 'demo_db'; - -$db->create(); -``` - -The example above creates a database named `demo_db` on the database server -instance created in the previous step. - -### 4. Create database user and give it access to database. - -```php -$user = $dbInstance->user(); -$user->name = 'demo_user'; -$user->password = 'h@X0r!'; -$user->databases = array('demo_db'); - -$user->create(); -``` - -The example above creates a database user named `demo_user`, sets its password -and gives it access to the `demo_db` database created in the previous step. - -### 5. Optional step: Create a load balancer to allow access to the database from the Internet. - -The database created in the previous step can only be accessed from the Rackspace -private network (aka `SERVICENET`). If you have a cloud server instance in the same -region as the database server instance, you will be able to connect to the database -from that cloud server instance. - -If, however, you would like to access the database from the Internet, you will -need to create a load balancer with an IP address that is routable from the -Internet and attach the database server instance as a back-end node of this load -balancer. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -$loadBalancer = $loadBalancerService->loadBalancer(); - -$loadBalancer->name = 'Load balancer - DB'; -$loadBalancer->addNode($dbInstance->hostname, 3306); -$loadBalancer->port = 3306; -$loadBalancer->protocol = 'MYSQL'; -$loadBalancer->addVirtualIp('PUBLIC'); - -$loadBalancer->create(); - -$loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { - printf("Load balancer build status: %s\n", $lb->status); -}); - -foreach ($loadBalancer->virtualIps as $vip) { - if ($vip->type == 'PUBLIC') { - printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); - } -} -``` - -In the example above, a load balancer is created with the database server -instance as its only back-end node. Further, this load balancer is configured -to listen for MySQL connections on port 3306. Finally a virtual IP address (VIP) -is configured in the `PUBLIC` network address space so that this load balancer -may receive connections from the Internet. - -Once the load balancer is created and becomes `ACTIVE`, it's Internet-accessible -IP addresses are printed out. If you connect to any of these IP addresses on port -3306 using the MySQL protocol, you will be connected to the database created in -step 3. +https://doc.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/Debugging.md b/docs/userguide/Debugging.md index 0fc970c0a..0c6afa4d6 100644 --- a/docs/userguide/Debugging.md +++ b/docs/userguide/Debugging.md @@ -1,89 +1,5 @@ # Debugging -There are two important debugging strategies to use when encountering problems -with HTTP transactions. +Our docs have moved! Please visit the below link: -## Strategy 1: Meaningful exception handling - -If the API returns a `4xx` or `5xx` status code, it indicates that there was an -error with the sent request, meaning that the transaction cannot be adequately -completed. - -The Guzzle HTTP component, which forms the basis of our SDK's transport layer, -utilizes [numerous exception classes](https://github.com/guzzle/guzzle/tree/master/src/Guzzle/Http/Exception) -to handle this error logic. - -The two most common exception classes are: - -* `Guzzle\Http\Exception\ClientErrorResponseException`, which is thrown when a -`4xx` response occurs - -* `Guzzle\Http\Exception\ServerErrorResponseException`, which is thrown when a -`5xx` response occurs - -Both of these classes extend the base `BadResponseException` class. - -This provides you with the granularity you need to debug and handle exceptions. - -### An example with Swift - -If you're trying to retrieve a Swift resource, such as a Data Object, and you're -not completely certain that it exists, it makes sense to wrap your call in a -try/catch block: - -```php -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - return $service->getObject('foo.jpg'); -} catch (ClientErrorResponseException $e) { - // Okay, the resource probably does not exist - return false; -} catch (\Exception $e) { - // Some other exception was thrown, probably critical - $this->logException($e); - $this->alertDevs(); -} -``` - -Both `ClientErrorResponseException` and `ServerErrorResponseException` have -two methods that allow you to access the HTTP transaction: - -```php -// Find out the faulty request -$request = $e->getRequest(); - -// Display everything by casting as string -echo (string) $request; - -// Find out the HTTP response -$response = $e->getResponse(); - -// Output that too -echo (string) $response; -``` - -## Strategy 2: Wire logging - -Guzzle provides a [Log plugin](http://docs.guzzlephp.org/en/latest/plugins/log-plugin.html) -that allows you to log everything over the wire, which is useful if you don't -know what's going on. - -Here's how you enable it: - -#### Install the plugin - -```bash -php composer.phar require guzzle/plugin-log:~3.8 -``` - -#### Add to your client - -```php -use Guzzle\Plugin\Log\LogPlugin; - -$client->addSubscriber(LogPlugin::getDebugPlugin()); -``` - -The above will add a generic logging subscriber to your client, which will be -notified every time a relevant HTTP event is fired off. +https://doc.php-opencloud.com/en/latest/debugging.html diff --git a/docs/userguide/Identity/Roles.md b/docs/userguide/Identity/Roles.md index e2d49e71d..333d53c81 100644 --- a/docs/userguide/Identity/Roles.md +++ b/docs/userguide/Identity/Roles.md @@ -1,68 +1,5 @@ # Roles -## Intro +Our docs have moved! Please visit the below link: -A role is a personality that a user assumes when performing a specific set of operations. A role includes a set of rights and privileges. A user assuming a role inherits the rights and privileges associated with the role. A token that is issued to a user includes the list of roles the user can assume. When a user calls a service, that service determines how to interpret a user's roles. A role that grants access to a list of operations or resources within one service may grant access to a completely different list when interpreted by a different service. - -## Setup - -Role objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Getter|Setter ----|---|--- -id|`getId()`|`setId()` -name|`getName()`|`setName()` -description|`getDescription()`|`setDescription()` - -## List roles - -This call lists the global roles available within a specified service. - -```php -$roles = $service->getRoles(); - -foreach ($roles as $role) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). - -## Get role - -This call lists detailed information (id, name, description) for a specified role. - -```php -$roleId = '123abc'; -$role = $service->getRole($roleId); -``` - -## Add/delete user roles - -To add/remove user roles, you must first instantiate a [user](Users.md) object: - -```php -$roleId = '123abc'; - -// add role to user -$user->addRole($roleId); - -// remove role from user -$user->removeRole($roleId); -``` - -## List user global roles - -This call returns a list of global roles associated with a user: - -```php -$roles = $user->getRoles(); - -foreach ($roles as $role) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/roles.html diff --git a/docs/userguide/Identity/Service.md b/docs/userguide/Identity/Service.md index 6cc80eb5a..579cf2a9f 100644 --- a/docs/userguide/Identity/Service.md +++ b/docs/userguide/Identity/Service.md @@ -1,24 +1,5 @@ # Identity service -## Intro +Our docs have moved! Please visit the below link: -The Identity service is regionless, so you do not need to specify a region when instantiating the service object. Although this was primarily based on Rackspace's implementation of Cloud Identity, it should also work for OpenStack Keystone. - -## A note on object creation - -Normally, when services are created the client handles authenticates automatically. But because Keystone/Identity is fundamental to the authentication process itself, it proves difficult to do this procedure as its normally done. For this reason, you have two options when creating the service object: - -1: Use the client's factory method - -```php -$identity = $client->identityService(); -``` - -2: Authenticate manually - -```php -use OpenCloud\Identity\Service as IdentityService; - -$identity = IdentityService::factory($client); -$identity->getClient()->authenticate(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/index.html diff --git a/docs/userguide/Identity/Tenants.md b/docs/userguide/Identity/Tenants.md index b30cb661d..ae9bcdcb0 100644 --- a/docs/userguide/Identity/Tenants.md +++ b/docs/userguide/Identity/Tenants.md @@ -1,21 +1,5 @@ # Tenants -## Intro +Our docs have moved! Please visit the below link: -A tenant is a container used to group or isolate resources and/or identity objects. Depending on the service operator, a tenant may map to a customer, account, organization, or project. - -## Setup - -Tenant objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## List tenants - -```php -$tenants = $service->getTenants(); - -foreach ($tenants as $tenant) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/tenants.html diff --git a/docs/userguide/Identity/Tokens.md b/docs/userguide/Identity/Tokens.md index 04a3ec327..a60c57ab5 100644 --- a/docs/userguide/Identity/Tokens.md +++ b/docs/userguide/Identity/Tokens.md @@ -1,84 +1,5 @@ # Tokens -## Intro +Our docs have moved! Please visit the below link: -A token is an opaque string that represents an authorization to access cloud resources. Tokens may be revoked at any time and are valid for a finite duration. - -## Setup - -Token objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Description|Getter|Setter ----|---|---|--- -id|The unique ID of the token|`getId()`|`setId()` -expires|Timestamp of when the token will expire|`getExpires()` or `hasExpired()`|`setExpires()` - -## Create token (authenticate) - -In order to generate a token, you must pass in the JSON template that is sent to the API. This is because Rackspace's operation expects a slightly different entity body than OpenStack Keystone. - -Request body for Rackspace's generate token operation: - -```json -{ - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "foo", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1100111" - } -} -``` - -Request body for Keystone's generate token operation: - -```json -{ - "auth": { - "passwordCredentials":{ - "username":"demoauthor", - "password":"theUsersPassword" - }, - "tenantId": "12345678" - } -} -``` - -The only real differences you'll notice is the name of the object key (`RAX-KSKEY:apiKeyCredentials`/`passwordCredentials`) and the secret (`apiKey`/`password`). The `tenantId` property in both templates are optional. You can also add `tenantName` too. - -```php -use OpenCloud\Common\Http\Message\Formatter; - -$template = sprintf( - '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', - 'my_username', - 'my_api_key' -); - -$response = $service->generateToken($template); - -$body = Formatter::decode($response); - -// service catalog -$catalog = $body->access->serviceCatalog; - -// token -$token = $body->access->token; - -// user -$user = $body->access->user; -``` - -As you will notice, these variables will be stdClass objects - for fully fledged functionality, let the client authenticate by itself because it ends up stocking the necessary models for you. - -To see the response body structure, consult the [official docs](http://docs.rackspace.com/auth/api/v2.0/auth-client-devguide/content/POST_authenticate_v2.0_tokens_Token_Calls.html). - -## Revoke token (destroy session) - -```php -$tokenId = '1234567'; -$service->revokeToken($tokenId); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/tokens.html diff --git a/docs/userguide/Identity/Users.md b/docs/userguide/Identity/Users.md index 286aa6007..e387cbee5 100644 --- a/docs/userguide/Identity/Users.md +++ b/docs/userguide/Identity/Users.md @@ -1,128 +1,5 @@ # Users -## Intro +Our docs have moved! Please visit the below link: -A user is a digital representation of a person, system, or service who consumes cloud services. Users have credentials and may be assigned tokens; based on these credentials and tokens, the authentication service validates that incoming requests are being made by the user who claims to be making the request, and that the user has the right to access the requested resources. Users may be directly assigned to a particular tenant and behave as if they are contained within that tenant. - -## Setup - -User objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Description|Getter|Setter ----|---|---|--- -id|The unique ID for this user|`getId()`|`setId()` -username|Username for this user|`getUsername()`|`setUsername()` -email|User's email address|`getEmail()`|`setEmail()` -enabled|Whether or not this user can consume API functionality|`getEnabled()` or `isEnabled()`|`setEnabled()` -password|Either a user-defined string, or an automatically generated one, that provides security when authenticating.|`getPassword()` only valid on creation|`setPassword()` to set local property only. To set password on API (retention), use `updatePassword()`. -defaultRegion|Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region.|`getDefaultRegion()`|`setDefaultRegion()` -domainId|Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account.|`getDomainId()`|`setDomainId()` - -## List users - -```php -$users = $service->getUsers(); - -foreach ($users as $user) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). - -## Get user - -There are various ways to get a specific user: by name, ID and email address. - -```php -use OpenCloud\Identity\Constants\User as UserConst; - -// Get user by name -$user1 = $service->getUser('jamie'); - -// Get user by ID -$user2 = $service->getUser(123456, UserConst::MODE_ID); - -// Get user by email -$user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); -``` - -## Create user - -There are a few things to remember when creating a user: - -* This operation is available only to users who hold the `identity:user-admin` role. This admin can create a user who holds the `identity:default` user role. - -* The created user **will** have access to APIs but **will not** have access to the Cloud Control Panel. - -* Within an account, a maximum of 100 account users can be added. - -* If you attempt to add a user who already exists, an HTTP error 409 results. - -The `username` and `email` properties are required for creating a user. Providing a `password` is optional; if omitted, one will be automatically generated and provided in the response. - -```php -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - // execute operation - $user = $service->createUser(array( - 'username' => 'newUser', - 'email' => 'foo@bar.com' - )); -} catch (ClientErrorResponseException $e) { - // catch 4xx HTTP errors - echo $e->getResponse()->toString(); -} - -// show generated password -echo $user->getPassword(); -``` - -## Update user - -When updating a user, specify which attribute/property you want to update: - -```php -$user->update(array( - 'email' => 'new_email@bar.com' -)); -``` - -### Updating a user password - -Updating a user password requires calling a distinct method: -```php -$user->updatePassword('password123'); -``` - -## Delete user - -```php -$user->delete(); -``` - -## List credentials - -This operation allows you to see your non-password credential types for all authentication methods available. - -```php -$creds = $user->getOtherCredentials(); -``` - -## Get user API key - -```php -echo $user->getApiKey(); -``` - -## Reset user API key - -When resetting an API key, a new one will be automatically generated for you: - -```php -$user->resetApiKey(); -echo $user->getApiKey(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/users.html diff --git a/docs/userguide/Image/Images.md b/docs/userguide/Image/Images.md index c0efee9a1..1f593f17a 100644 --- a/docs/userguide/Image/Images.md +++ b/docs/userguide/Image/Images.md @@ -1,97 +1,5 @@ # Images -A virtual machine image is a single file which contains a virtual disk that has -an installed bootable operating system. In the Cloud Images API, an image is -represented by a JSON-encoded data structure (the image schema) and its raw -binary data (the image file). +Our docs have moved! Please visit the below link: -An Image is represented by the `OpenCloud\Image\Resource\Image` class. - -## Setup - -You instantiate an Image object from its `OpenCloud\Image\Service` class, which -is available from the OpenStack/Rackspace client: - -```php -$service = $client->imageService('cloudImages', 'IAD'); -``` - -View the guides for more information about [clients](../Clients.md) or -[services](../Services.md). - -## List images - -```php -$images = $service->listImages(); - -foreach ($images as $image) { - /** @param $image OpenCloud\Image\Resource\Image */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](../Iterators.md). - -## Get image details - -```php -/** @param $image OpenCloud\Image\Resource\Image */ -$image = $service->getImage(''); -``` - -### A note on schema classes - -Both `OpenCloud\Image\Resource\Image` and `OpenCloud\Image\Resource\Member` -extend the `AbstractSchemaResource` abstract class, which offers some unique -functionality. - -Because these resources are inherently dynamic - i.e. they are modelled on -dynamic JSON schema - you need to access their state in a way different than -conventional getter/setter methods, and even class properties. For this reason, -they implement SPL's native -[`ArrayAccess`](http://www.php.net/manual/en/class.arrayaccess.php) -interface which allows you to access their state as a conventional array: - -```php -$image = $service->getImage(''); - -$id = $image['id']; -$tags = $image['tags']; -``` - -## Update image - -You can only update your own custom images - you cannot update or delete base -images. The way in which you may update your image is dictated by its -[schema](Schemas.md). - -Although you should be able to add new and replace existing properties, always -prepare yourself for a situation where it might be forbidden: - -```php -use OpenCloud\Common\Exceptions\ForbiddenOperationException; - -try { - $image->update(array( - 'name' => 'foo', - 'newProperty' => 'bar' - )); -} catch (ForbiddenOperationException $e) { - // A 403 Forbidden was returned -} -``` - -There are three operations that can take place for each Image property: - -* If a `false` or `null` value is provided, a `REMOVE` operation will occur, -removing the property from the JSON document -* If a non-false value is provided and the property does not exist, an `ADD` -operation will add it to the document -* If a non-false value is provided and the property does exist, a `REPLACE` -operation will modify the property in the document - -## Delete image - -```php -$image->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/images.html diff --git a/docs/userguide/Image/Schemas.md b/docs/userguide/Image/Schemas.md index b634b5705..b37222b84 100644 --- a/docs/userguide/Image/Schemas.md +++ b/docs/userguide/Image/Schemas.md @@ -1,159 +1,5 @@ # JSON schemas -The Cloud Images API supplies json documents describing the JSON-encoded data -structures that represent domain objects, so that a client knows exactly what -to expect in an API response. +Our docs have moved! Please visit the below link: -A JSON Schema is represented by the `OpenCloud\Image\Resource\Schema\Schema` -class. - -## Schema types - -There are currently four types of schema: Images schema, Image schema, Members -schema, and Member schema. - -## Example response from the API - -A sample response from the API, for an Images schema might be: - -```json -{ - "name": "images", - "properties": { - "images": { - "items": { - "type": "array", - "name": "image", - "properties": { - "id": {"type": "string"}, - "name": {"type": "string"}, - "visibility": {"enum": ["public", "private"]}, - "status": {"type": "string"}, - "protected": {"type": "boolean"}, - "tags": { - "type": "array", - "items": {"type": "string"} - }, - "checksum": {"type": "string"}, - "size": {"type": "integer"}, - "created_at": {"type": "string"}, - "updated_at": {"type": "string"}, - "file": {"type": "string"}, - "self": {"type": "string"}, - "schema": {"type": "string"} - }, - "additionalProperties": {"type": "string"}, - "links": [ - {"href": "{self}", "rel": "self"}, - {"href": "{file}", "rel": "enclosure"}, - {"href": "{schema}", "rel": "describedby"} - ] - } - }, - "schema": {"type": "string"}, - "next": {"type": "string"}, - "first": {"type": "string"} - }, - "links": [ - {"href": "{first}", "rel": "first"}, - {"href": "{next}", "rel": "next"}, - {"href": "{schema}", "rel": "describedby"} - ] -} -``` - -The top-level schema is called `images`, and contains an array of links and -a properties object. Inside this properties object we see the structure of this -top-level `images ` object. So we know that it will take this form: - -```json -{ - "images": [something...] -} -``` - -Within this object, we can see that it contains an array of anonymous objects, -each of which is called `image` and has its own set of nested properties: - -```json -{ - "images": [ - { - [object 1...] - }, - { - [object 2...] - }, - { - [object 3...] - } - ] -} -``` - -The structure of these nested objects are defined as another schema - i.e. a -*subschema*. We know that each object has an ID property (string), a name -property (string), a visibility property (can either be `private` or `public`), etc. - -```json -{ - "images": [ - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - }, - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - }, - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - } - ] -} -``` - -Each nested property of a schema is represented by the -`OpenCloud\Image\Resource\Schema\Property` class. - -If you would like to find out more about schemas, Guzzle has good documentation -about [service descriptions](http://docs.guzzlephp.org/en/latest/webservice-client/guzzle-service-descriptions.html), -which is fairly analogous. - -## JSON Patch - -The Glance API has a unique way of updating certain dynamic resources: they use -JSON Patch method, as outlined in [RFC 6902](http://tools.ietf.org/html/rfc6902). - -Requests need to use the `application/openstack-images-v2.1-json-patch` -content-type. - -In order for the operation to occur, the request entity body needs to contain a -very particular structure: - -``` -[ - {"op": "replace", "path": "/name", "value": "Fedora 17"}, - {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} -] -``` - -The `op` key refers to the type of Operation (see -`OpenCloud\Image\Enum\OperationType` for a full list). - -The `path` key is a JSON pointer to the document property you want to modify or -insert. JSON pointers are defined in [RFC 6901](http://tools.ietf.org/html/rfc6901). - -The `value` key is the value. - -Because this is all handled for you behind the scenes, we will not go into exhaustive depth -about how this operation is handled. You can browse the source code, consult -the various RFCs and the [official documentation](http://docs.rackspace.com/images/api/v2/ci-devguide/content/patch-method.html) -for additional information. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/schemas.html diff --git a/docs/userguide/Image/Sharing.md b/docs/userguide/Image/Sharing.md index e83470073..91515b9fa 100644 --- a/docs/userguide/Image/Sharing.md +++ b/docs/userguide/Image/Sharing.md @@ -1,110 +1,5 @@ # Sharing images -Images can be created and deleted by image producers, updated by image -consumers, and listed by both image producers and image consumers: +Our docs have moved! Please visit the below link: -Operation|Producer can?|Consumer can? ----|---|--- -Created|Yes|No -Deleted|Yes|No -Updated|No|Yes -Listed|Yes|Yes - -The producer shares an image with the consumer by making the consumer a *member* -of that image. The consumer then accepts or rejects the image by changing the -member status. Once accepted, the image appears in the consumer's image list. - -## Typical workflow - -1. The producer posts the availability of specific images on a public website. - -2. A potential consumer provides the producer with his/her tenant ID and email -address. - -3. The producer [creates a new Image Member]() with the consumer's details - -4. The producer notifies the consumer via email that the image has been shared -and provides the image's ID. - -5. If the consumer wishes the image to appear in his/her image list, the -consumer [updates their own Member status]() to `ACCEPTED`. - -### Additional notes - -* If the consumer subsequently wishes to hide the image, the consumer can change -their Member status to `REJECTED`. - -* If the consumer wishes to hide the image, but is open to the possibility of -being reminded by the producer that the image is available, the consumer can -change their Member status to `PENDING`. - -* Image producers add or remove image members, but may not modify the member -status of an image member. - -* Image consumers change their own member status, but may not add or remove -themselves as an image member. - -* Image consumers can boot from any image shared by the image producer, -regardless of the member status, as long as the image consumer knows the image ID. - -## Setup - -All member operations are executed against an [Image](Images.md), so you will -need to set this up first. - -## List image members - -This operation is available for both producers and consumers. - -```php -$members = $image->listMembers(); - -foreach ($members as $member) { - /** @param $member OpenCloud\Image\Resource\Member */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](../Iterators.md). - -## Create image member - -This operation is only available for producers. - -```php -$tenantId = 12345; - -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->createMember($tenantId); -``` - -## Delete image member - -This operation is only available for producers. - -```php -$tenantId = 12345; - -/** @param $member OpenCloud\Image\Resource\Member */ -$member = $image->getMember($tenantId); - -$member->delete(); -``` - -## Update image member status - -This operation is only available for consumers. - -```php -use OpenCloud\Images\Enum\MemberStatus; - -$tenantId = 12345; - -/** @param $member OpenCloud\Image\Resource\Member */ -$member = $image->getMember($tenantId); - -$member->updateStatus(MemberStatus::ACCEPTED); -``` - -The acceptable states you may pass in are made available to you through the -constants defined in the `OpenCloud\Images\Enum\MemberStatus` class. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/sharing.html diff --git a/docs/userguide/Image/Tags.md b/docs/userguide/Image/Tags.md index 019e336e5..fc8bcd8aa 100644 --- a/docs/userguide/Image/Tags.md +++ b/docs/userguide/Image/Tags.md @@ -1,23 +1,5 @@ # Image tags -An image tag is a string of characters used to identify a specific image or -images. +Our docs have moved! Please visit the below link: -## Setup - -All member operations are executed against an [Image](Images.md), so you will -need to set this up first. - -## Add image tag - -```php -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->addTag('jamie_dev'); -``` - -## Delete image tag - -```php -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->deleteTag('jamie_dev'); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/tags.html diff --git a/docs/userguide/Iterators.md b/docs/userguide/Iterators.md index 553507937..114982668 100644 --- a/docs/userguide/Iterators.md +++ b/docs/userguide/Iterators.md @@ -1,118 +1,5 @@ # Iterators -## Intro +Our docs have moved! Please visit the below link: -Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: - -- **ResourceIterator**. The standard iterator class that implements SPL's standard [Iterator](http://php.net/manual/en/class.iterator.php), [ArrayAccess](http://www.php.net/manual/en/class.arrayaccess.php) and [Countable](http://php.net/manual/en/class.countable.php) interfaces. In short, this allows you to traverse this object (using `foreach`), count its internal elements like an array (using `count` or `sizeof`), and access its internal elements like an array (using `$iterator[1]`). - - -- **PaginatedIterator**. This is a child of ResourceIterator, and as such inherits all of its functionality. The difference however is that when it reaches the end of the current collection, it attempts to construct a URL to access the API based on predictive paginated collection templates. - -## Common behaviour - -```php -$iterator = $computeService->flavorList(); -``` - -There are two ways to traverse an iterator. The first is the longer, more traditional way: - -```php -while ($iterator->valid()) { - $flavor = $iterator->current(); - - // do stuff.. - echo $flavor->id; - - $iterator->next(); -} -``` - -There is also a shorter and more intuitive version: - -```php -foreach ($iterator as $flavor) { - // do stuff... - echo $flavor->id; -} -``` - -Because the iterator implements PHP's native `Iterator` interface, it can inherit all the native functionality of traversible data structures with `foreach`. - -## Very important note - -Until now, users have been expected to do this: - -```php -while ($flavor = $iterator->next()) { - // ... -} -``` - -which is **incorrect**. The single responsibility of `next` is to move the internal pointer forward. It is the job of `current` to retrieve the current element. - -For your convenience, these two Iterator classes are fully backward compatible: they exhibit all the functionality you'd expect from a correctly implemented iterator, but they also allow previous behaviour. - -## Using paginated collections - -For large collections, such as retrieving DataObjects from CloudFiles/Swift, you need to use pagination. Each resource will have a different limit per page; so once that page is traversed, there needs to be another API call to retrieve to *next* page's resources. - -There are two key concepts: - -- **limit** is the amount of resources returned per page -- **marker** is the way you define a starting point. It is some form of identifier that allows the collection to begin from a specific resource - -### Resource classes - -When the iterator returns a current element in the internal list, it populates the relevant resource class with all the data returned to the API. In most cases, a `stdClass` object will become an instance of `OpenCloud\Common\PersistentObject`. - -In order for this instantiation to happen, the `resourceClass` option must correspond to some method in the parent class that creates the resource. For example, if we specify 'ScalingPolicy' as the `resourceClass`, the parent object (in this case `OpenCloud\Autoscale\Group`, needs to have some method will allows the iterator to instantiate the child resource class. These are all valid: - -1. `Group::scalingGroup($data);` - -2. `Group::getScalingGroup($data);` - -3. `Group::resource('ScalingGroup', $data);` - -where `$data` is the standard object. This list runs in order of precedence. - -## Setting up a PaginatedIterator - -```php -use OpenCloud\Common\Collection\PaginatedIterator; - -$service = $client->computeService(); - -$flavors = PaginatedIterator::factory($service, array( - 'resourceClass' => 'Flavor', - 'baseUrl' => $service->getUrl('flavors') - 'limit.total' => 350, - 'limit.page' => 100, - 'key.collection' => 'flavors' -)); - -foreach ($flavors as $flavor) { - echo $flavor->getId(); -} -``` - -As you can see, there are a lot of configuration parameters to pass in - and getting it right can be quite fiddly, involving a lot of API research. For this reason, using the convenience methods like `flavorList` is recommended because it hides the complexity. - -### PaginatedIterator options - -There are certain configuration options that the paginated iterator needs to work. These are: - -Name|Description|Type|Required|Default| ----|---|---|---|---| -resourceClass|The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator.|string|Yes|- -baseUrl|The base URL that is used for making new calls to the API for new pages|`Guzzle\Http\Url`|Yes|- -limit.total|The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list|int|No|10000 -limit.page|The amount of resources each page contains|int|No|100 -key.links|Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is `images_links`.|string|No|links -key.collection|The top-level key for the array of resources. For example, servers are returned with this data structure: `{"servers": [...]}`. The **key.collection** value in this case would be `servers`.|string|No|`null` -key.collectionElement|Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: `{"keypairs": [ {"keypair": {...}} ] }`. So in this case the collectionElement key would be `keypair`.|string|No|`null` -key.marker|The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is `id` or `name`.|string|No|name -request.method|The HTTP method used when making API calls for new pages|string|No|GET -request.headers|The HTTP headers to send when making API calls for new pages|array|No|`array()` -request.body|The HTTP entity body to send when making API calls for new pages|`Guzzle\Http\EntityBody`|No|`null` -request.curlOptions|Additional cURL options to use when making API calls for new pages|array|No|`array()` +https://doc.php-opencloud.com/en/latest/iterators.html diff --git a/docs/userguide/LoadBalancer/README.md b/docs/userguide/LoadBalancer/README.md index 03364f103..34008ab6c 100644 --- a/docs/userguide/LoadBalancer/README.md +++ b/docs/userguide/LoadBalancer/README.md @@ -1,80 +1,5 @@ # Load Balancers -A **load balancer** is a device that distributes incoming network traffic amongst -multiple back-end systems. These back-end systems are called the **nodes** of -the load balancer. +Our docs have moved! Please visit the below link: -## Getting started - -### 1. Instantiate a Rackspace client. - -```php - -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### 2. Retrieve the server instances you want to add as nodes of the load balancer. - -```php -$computeService = $client->computeService('cloudServersOpenStack', 'DFW'); - -$serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); -$serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); -``` - -The example above uses two server instances that have already been created. It -retrieves the server instances using their IDs. See also: [creating server instances](). - -### 3. Obtain a Load Balancer service object from the client. - -This object will be used to first define the load balancer nodes and later create the load balancer itself. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); -``` - -### 4. Define a load balancer node for each server. - -```php -$loadBalancer = $loadBalancerService->loadBalancer(); - -$serverOneNode = $loadBalancer->node(); -$serverOneNode->address = $serverOne->addresses->private[0]->addr; -$serverOneNode->port = 8080; -$serverOneNode->condition = 'ENABLED'; - -$serverTwoNode = $loadBalancer->node(); -$serverTwoNode->address = $serverTwo->addresses->private[0]->addr; -$serverTwoNode->port = 8080; -$serverTwoNode->condition = 'ENABLED'; -``` - -In the example above, each node runs a service that listens on port 8080. Further, -each node will start out as `ENABLED`, which means it will be ready to receive -network traffic from the load balancer as soon as it is created. - -### 5. Create the load balancer with the two nodes. - -```php -$loadBalancer->addVirtualIp('PUBLIC'); -$loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) -)); -``` - -In the example above, the load balancer will have a virtual IP address accessible -from the public Internet. Also notice that the port the load balancer listens -on (80) does not need to match the ports of its nodes (8080). - -## Next steps - -Once you have created a load balancer, there is a lot you can do with it. See -the [complete user guide for load balancers](USERGUIDE.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 0c15f0b29..0a5c3b15d 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -1,626 +1,5 @@ # The Complete User Guide to Load Balancers -## Prerequisites +Our docs have moved! Please visit the below link: -### Client -To use the load balancers service, you must first instantiate a `Rackspace` -client object. - -```php -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### Load Balancer Service -All operations on load balancers are done via a load balancer service object. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); -``` - -### Cloud Servers -Many of the examples in this document use two cloud servers as nodes for -the load balancer. The variables `$serverOne` and `$serverTwo` refer to these -two cloud servers. - -## Load Balancers - -A **load balancer** is a device that distributes incoming network traffic amongst -multiple back-end systems. These back-end systems are called the **nodes** of -the load balancer. - -### Create Load Balancer - -```php -$loadBalancer = $loadBalancerService->loadBalancer(); - -$serverOneNode = $loadBalancer->node(); -$serverOneNode->address = $serverOne->addresses->private[0]->addr; -$serverOneNode->port = 8080; -$serverOneNode->condition = 'ENABLED'; - -$serverTwoNode = $loadBalancer->node(); -$serverTwoNode->address = $serverTwo->addresses->private[0]->addr; -$serverTwoNode->port = 8080; -$serverTwoNode->condition = 'ENABLED'; - -$loadBalancer->addVirtualIp('PUBLIC'); -$loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) -)); -``` - -### List Load Balancer Details - -You can retrieve a single load balancer's details by using its ID. - -```php -$loadBalancer = $loadBalancerService->loadBalancer('254889'); - -/** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ -``` - -### List Load Balancers - -You can retrieve a list of all your load balancers. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$loadBalancers = $loadBalancerService->loadBalancerList(); -foreach ($loadBalancers as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ -} -``` - -### Update Load Balancer Attributes - -You can update one or more of the following load balancer attributes: - -* `name`: The name of the load balancer -* `algorithm`: The algorithm used by the load balancer to distribute traffic amongst its nodes. See also: [Load balancing algorithms](#algorithms). -* `protocol`: The network protocol used by traffic coming in to the load balancer. See also: [Protocols](#protocols). -* `port`: The network port on which the load balancer listens for incoming traffic. -* `halfClosed`: Enable or Disable Half-Closed support for the load balancer. -* `timeout`: The timeout value for the load balancer to communicate with its nodes. -* `httpsRedirect`: Enable or disable HTTP to HTTPS redirection for the load balancer. When enabled, any HTTP request will return status code 301 (Moved Permanently), and the requestor will be redirected to the requested URL via the HTTPS protocol on port 443. For example, http://example.com/page.html would be redirected to https:// example.com/page.html. Only available for HTTPS protocol (`port` = 443), or HTTP Protocol with a properly configured SSL Termination (`secureTrafficOnly=true, securePort=443). See also: [SSL Termination](#ssl-termination). - -#### Updating a single attribute of a load balancer -```php -$loadBalancer->update(array( - 'name' => 'New name' -)); -``` - -#### Updating multiple attributes of a load balancer -```php -$loadBalancer->update(array( - 'name' => 'New name', - 'algorithm' => 'ROUND_ROBIN' -)); -``` - -### Remove Load Balancer - -When you no longer have a need for the load balancer, you can remove it. - -```php -$loadBalancer->delete(); -``` - -## Nodes - -A **node** is a backend device that provides a service on specified IP and port. An example of a load balancer node might be a web server serving HTTP traffic on port 8080. - -A load balancer typically has multiple nodes attached to it so it can distribute incoming network traffic amongst them. - -### List Nodes - -You can list the nodes attached to a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$nodes = $loadBalancer->nodeList(); -foreach ($nodes as $node) { - /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ -} -``` - -### Add Nodes - -You can attach additional nodes to a load balancer. Assume `$loadBalancer` already has two nodes attached to it - `$serverOne` and `$serverTwo` - and you want to attach a third node to it, say `$serverThree`, which provides a service on port 8080. - -**Important:** Remember to call `$loadBalancer->addNodes()` after all the calls to `$loadBalancer->addNode()` as shown below. - -```php -$address = $serverThree->addresses->private[0]->addr; -$loadBalancer->addNode($address, 8080); -$loadBalancer->addNodes(); -``` - -The `addNode` method accepts three more optional parameters, in addition to the two shown above: - -| Position | Description | Data type | Required? | Default value | -| ----------- | --------------- | --------------| -------------- | ----------------- | -| 1 | IP address of node | String | Yes | - | -| 2 | Port used by node's service | Integer | Yes | - | -| 3 | Starting condition of node:
          • `ENABLED` – Node is ready to receive traffic from the load balancer.
          • `DISABLED` – Node should not receive traffic from the load balancer.
          • `DRAINING` – Node should process any traffic it is already receiving but should not receive any further traffic from the load balancer.
          | String | No | `ENABLED` | -| 4 | Type of node to add:
          • `PRIMARY` – Nodes defined as PRIMARY are in the normal rotation to receive traffic from the load balancer.
          • `SECONDARY` – Nodes defined as SECONDARY are only in the rotation to receive traffic from the load balancer when all the primary nodes fail.
          | String | No | `PRIMARY` | -| 5 | Weight, between 1 and 100, given to node when distributing traffic using either the `WEIGHTED_ROUND_ROBIN` or the `WEIGHTED_LEAST_CONNECTIONS` load balancing algorithm. | Integer | No | 1 | - -### Modify Nodes -You can modify one or more of the following node attributes: - -* `condition`: The condition of the load balancer: - * `ENABLED` – Node is ready to receive traffic from the load balancer. - * `DISABLED` – Node should not receive traffic from the load balancer. - * `DRAINING` – Node should process any traffic it is already receiving but should not receive any further traffic from the load balancer. -* `type`: The type of the node: - * `PRIMARY` – Nodes defined as PRIMARY are in the normal rotation to receive traffic from the load balancer. - * `SECONDARY` – Nodes defined as SECONDARY are only in the rotation to receive traffic from the load balancer when all the primary nodes fail. -* `weight`: The weight, between 1 and 100, given to node when distributing traffic using either the `WEIGHTED_ROUND_ROBIN` or the `WEIGHTED_LEAST_CONNECTIONS` load balancing algorithm. - -#### Modifying a single attribute of a node -```php -use OpenCloud\LoadBalancer\Enum\NodeCondition; - -$node->update(array( - 'condition' => NodeCondition::DISABLED -)); -``` - -#### Modifying multiple attributes of a node -```php -use OpenCloud\LoadBalancer\Enum\NodeCondition; -use OpenCloud\LoadBalancer\Enum\NodeType; - -$node->update(array( - 'condition' => NodeCondition::DISABLED, - 'type' => NodeType::SECONDARY -)); -``` - -### Remove Nodes - -There are two ways to remove a node. - -#### Given an `OpenCloud\LoadBalancer\Resource\Node` instance -```php -$node->delete(); -``` - -#### Given an `OpenCloud\LoadBalancer\Resource\LoadBalancer` instance and a node ID -```php -$loadBalancer->removeNode(490639); -``` - -The `removeNode` method, as shown above, accepts the following arguments: - -|Position| Description | Data type | Required? | Default value | -|----------- | --------------- | -------------- |-------------- | ----------------- | -| 1 | ID of node | Integer | Yes | - | - -### View Node Service Events -You can view events associated with the activity between a node and a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$nodeEvents = $loadBalancer->nodeEventList(); -foreach ($nodeEvents as $nodeEvent) { - /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ -} -``` - -## Virtual IPs - -A **virtual IP (VIP)** makes a load balancer accessible by clients. The load balancing service supports either a public VIP address (`PUBLIC`), routable on the public Internet, or a ServiceNet VIP address (`SERVICENET`), routable only within the region in which the load balancer resides. - -### List Virtual IPs - -You can list the VIPs associated with a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$vips = $loadBalancer->virtualIpList(); -foreach ($vips as $vip) { - /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ -} -``` - -### Add Virtual IPv6 - -You can add additional IPv6 VIPs to a load balancer. - -```php -use OpenCloud\LoadBalancer\Enum\IpType; - -$loadBalancer->addVirtualIp(IpType::PUBLIC, 6); -``` - -The `addVirtualIp` method, as shown above, accepts the following arguments: - -| Position | Description | Data type | Required? | Default value | -| ----------- | --------------- | -------------- |-------------- | ----------------- | -| 1 | Type of VIP:
          • `PUBLIC` – An IP address that is routable on the public Internet.
          • `SERVICENET` – An IP address that is routable only on ServiceNet.
          | String | No | `PUBLIC` | -| 2 | IP version: Must be `6` | Integer | Yes | - | - - -### Remove Virtual IPs - -You can remove a VIP from a load balancer. - -```php -$vip->remove(); -``` - -Please note that a load balancer must have at least one VIP associated with it. If you try to remove a load balancer's last VIP, a `ClientErrorResponseException` will be thrown. - -## Algorithms - -Load balancers use an **algorithm** to determine how incoming traffic is distributed amongst the back-end nodes. - -### List Load Balancing Algorithms - -You can list all supported load balancing algorithms using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$algorithms = $loadBalancerService->algorithmList(); -foreach ($algorithms as $algorithm) { - /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ -} -``` - -## Protocols - -When a load balancer is created a network protocol must be specified. This network protocol should be based on the network protocol of the back-end service being load balanced. - -### List Load Balancing Protocols - -You can list all supported network protocols using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$protocols = $loadBalancerService->protocolList(); -foreach ($protocols as $protocol) { - /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ -} -``` - -## Session Persistence - -**Session persistence** is a feature of the load balancing service that forces multiple requests, of the same protocol, from clients to be directed to the same node. This is common with many web applications that do not inherently share application state between back-end servers. - -There are two types (or modes) of session persistence: - -| Name | Description | -| -------- | --------------- | -| `HTTP_COOKIE` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | -| `SOURCE_IP` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | - -### List Session Persistence Configuration - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistenceType = $sessionPersistence->persistenceType; - -/** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ -``` - -In the example above: - -* If session persistence is enabled, the value of `$sessionPersistenceType` is the type of session persistence: either `HTTP_COOKIE` or `SOURCE_IP`. -* If session persistence is disabled, the value of `$sessionPersistenceType` is `null`. - -### Enable Session Persistence - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistence->update(array( - 'persistenceType' => 'HTTP_COOKIE' -)); -``` - -### Disable Session Persistence - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistence->delete(); -``` - -## Connection Logging - -The **connection logging** feature allows logs to be delivered to a Cloud Files account every hour. For HTTP-based protocol traffic, these are Apache-style access logs. For all other traffic, this is connection and transfer logging. - -### Check Logging Configuration - -```php -/** @var $connectionLogging bool **/ - -$connectionLogging = $loadBalancer->hasConnectionLogging(); -``` -In the example above: - -* If connection logging is enabled, the value of `$connectionLogging` is `true`. -* If connection logging is disabled, the value of `$connectionLogging` is `false`. - -### Enable Connection Logging - -```php -$loadBalancer->enableConnectionLogging(true); -``` - -### Disable Connection Logging - -```php -$loadBalancer->enableConnectionLogging(false); -``` - -## Error Page - -An **error page** is the html file that is shown to the end user when an error in the service has been thrown. By default every virtual server is provided with the default error file. It is also possible to set a custom error page for a load balancer. - -### View Error Page Content - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPageContent = $errorPage->content; - -/** @var $errorPageContent string **/ -``` - -In the example above the value of `$errorPageContent` is the HTML for that page. This could either be the HTML of the default error page or of your custom error page. - -### Set Custom Error Page - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPage->update(array( - 'content' => '' -)); -``` - -### Delete Custom Error Page - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPage->delete(); -``` - -## Allowed Domains - -**Allowed domains** are a restricted set of domain names that are allowed to add load balancer nodes. - -### List Allowed Domains - -You can list all allowed domains using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$allowedDomains = $loadBalancerService->allowedDomainList(); -foreach ($allowedDomains as $allowedDomain) { - /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ -} -``` - -## Access Lists - -**Access Lists** allow fine-grained network access to a load balancer's VIP. Using access lists, network traffic to a load balancer's VIP can be allowed or denied from a single IP address, multiple IP addresses or entire network subnets. - -Note that `ALLOW` network items will take precedence over `DENY` network items in an access list. - -To reject traffic from all network items except those with the `ALLOW` type, add a `DENY` network item with the address of `0.0.0.0/0`. - -### View Access List - -You can view a load balancer's access list. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$accessList = $loadBalancer->accessList(); -foreach ($accessList as $networkItem) { - /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ -} -``` - -### Add Network Items To Access List - -You can add network items to a load balancer's access list very easily: - -```php -$loadBalancer->createAccessList(array( - (object) array( - 'type' => 'ALLOW', - 'address' => '206.160.165.1/24' - ), - (object) array( - 'type' => 'DENY', - 'address' => '0.0.0.0/0' - ) -)); -``` - -In the above example, we allowed access for 1 IP address, and used the "0.0.0.0" - wildcard to blacklist all other traffic. - -### Remove Network Item From Access List - -You an remove a network item from a load balancer's access list. - -```php -$networkItem->delete(); -``` - -## Content Caching - -When **content caching** is enabled on a load balancer, recently-accessed files are stored on the load balancer for easy retrieval by web clients. Requests to the load balancer for these files are serviced by the load balancer itself, which reduces load off its back-end nodes and improves response times as well. - -### Check Content Caching Configuration - -```php -/** @var $contentCaching bool **/ - -$contentCaching = $loadBalancer->hasContentCaching(); -``` -In the example above: - -* If content caching is enabled, the value of `$contentCaching` is `true`. -* If content caching is disabled, the value of `$contentCaching` is `false`. - -### Enable Content Caching - -```php -$loadBalancer->enableContentCaching(true); -``` - -### Disable Content Caching - -```php -$loadBalancer->enableContentCaching(false); -``` - -## SSL Termination - -The SSL Termination feature allows a load balancer user to terminate SSL traffic at the load balancer layer versus at the web server layer. A user may choose to configure SSL Termination using a key and an SSL certificate or an (Intermediate) SSL certificate. - -When SSL Termination is configured on a load balancer, a secure shadow server is created that listens only for secure traffic on a user-specified port. This shadow server is only visible to and manageable by the system. Existing or updated attributes on a load balancer with SSL Termination will also apply to its shadow server. For example, if Connection Logging is enabled on an SSL load balancer, it will also be enabled on the shadow server and Cloud Files logs will contain log files for both. - -### View current SSL termination config - -```php -/** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ - -$sslConfig = $loadBalancer->SSLTermination(); -``` - -### Update SSL termination config - -```php -$sslConfig->update(array( - 'enabled' => true, - 'securePort' => 443, - 'privateKey' => $key, - 'certificate' => $cert -)); -``` - -For a full list, with explanations, of required and optional attributes, please consult the [official documentation](http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/SSLTermination-d1e2479.html) - -### Delete SSL termination config - -```php -$sslConfig->delete(); -``` - -## Metadata - -Metadata can be associated with each load balancer and each node for the client's personal use. It is defined using key-value pairs where the key and value consist of alphanumeric characters. A key is unique per load balancer. - -### List metadata - -```php -$metadataList = $loadBalancer->metadataList(); - -foreach ($metadataList as $metadataItem) { - printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -### Add metadata - -```php -$metadataItem = $loadBalancer->metadata(); -$metadataItem->create(array( - 'key' => 'foo', - 'value' => 'bar' -)); -``` - -### Modify metadata - -```php -$metadataItem = $loadBalancer->metadata('foo'); -$metadataItem->update(array( - 'value' => 'baz' -)); -``` - -### Remove metadata - -```php -$metadataItem->delete(); -``` - -## Monitors - -The load balancing service includes a health monitoring operation which periodically checks your back-end nodes to ensure they are responding correctly. If a node is not responding, it is removed from rotation until the health monitor determines that the node is functional. In addition to being performed periodically, the health check also is performed against every node that is added to ensure that the node is operating properly before allowing it to service traffic. Only one health monitor is allowed to be enabled on a load balancer at a time. - -```php -/** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ - -$healthMonitor = $loadBalancer->healthMonitor(); - -printf( - "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", - $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout -); -``` - -For a full list, with explanations, of required and optional attributes, please consult the [official documentation](http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/Monitor_Connections-d1e3536.html) - -### Update or delete - -```php -// Update -$healthMonitor->update(array( - 'delay' => 120, - 'timeout' => 60, - 'type' => 'CONNECT' - 'attemptsBeforeDeactivation' => 3 -)); - -// Delete -$healthMonitor->delete(); -``` - -## Statistics - -You can retrieve detailed stats about your load balancer, including the following information: - -- `connectTimeOut` – Connections closed by this load balancer because the 'connect_timeout' interval was exceeded. -- `connectError` – Number of transaction or protocol errors in this load balancer. -- `connectFailure` – Number of connection failures in this load balancer. -- `dataTimedOut` – Connections closed by this load balancer because the 'timeout' interval was exceeded. -- `keepAliveTimedOut` – Connections closed by this load balancer because the 'keepalive_timeout' interval was exceeded. -- `maxConn` – Maximum number of simultaneous TCP connections this load balancer has processed at any one time. - -```php -/** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ - -$stats = $loadBalancer->stats(); -``` - -## Usage Reports - -The load balancer usage reports provide a view of all transfer activity, average number of connections, and number of virtual IPs associated with the load balancing service. Current usage represents all usage recorded within the preceding 24 hours. Values for both incomingTransfer and outgoingTransfer are expressed in bytes transferred. - -The optional startTime and endTime parameters can be used to filter all usage. If the startTime parameter is supplied but the endTime parameter is not, then all usage beginning with the startTime will be provided. Likewise, if the endTime parameter is supplied but the startTime parameter is not, then all usage will be returned up to the endTime specified. - -```php -# View billable LBs -$billable = $service->billableLoadBalancerList(); - -foreach ($billable as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - - # View usage - /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ - $usage = $loadBalancer->usage(); - - echo $usage->averageNumConnections, PHP_EOL; -} -``` +https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md index ee1de981e..1778646ad 100644 --- a/docs/userguide/Networking/README.md +++ b/docs/userguide/Networking/README.md @@ -1,76 +1,5 @@ # Networking -**Networking** is a service that you can use to create virtual networks and attach cloud devices such as servers to these networks. +Our docs have moved! Please visit the below link: -## Concepts - -## Concepts - -To use the Networking service effectively, you should understand the following key concepts: - -* **Network**: A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. - -* **Subnet**: A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. - -* **Port**: A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. - - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -To use the Networking service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Networking service object from the client. -All Networking operations are done via an _networking service object_. To -instantiate this object, call the `networkingService` method on the `$client` -object. This method takes two arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$networkingService = $client->networkingService(null, $region); -``` - -Any networks, subnets, and ports created with this `$networkingService` instance will -be stored in the cloud region specified by `$region`. - -### 3. Create a network. -```php -$network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] - -## Next steps - -Once you have created a network, there is more you can do with it. See [complete user guide for networking](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 5a7beb07f..376daa093 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -1,617 +1,5 @@ # Complete User Guide for the Networking Service -Networking is a service that you can use to create virtual networks and attach -cloud devices such as servers to these networks. +Our docs have moved! Please visit the below link: -This user guide introduces you the entities in the Networking service — -networks, subnets, and ports — and shows you how to create and manage -these entities. - -## Table of contents - * [Concepts](#concepts) - * [Prerequisites](#prerequisites) - * [Client](#client) - * [Networking service](#networking-service) - * [Networks](#networks) - * [Create a network](#create-a-network) - * [Create multiple networks](#create-multiple-networks) - * [List networks](#list-networks) - * [Get a network](#get-a-network) - * [Update a network](#update-a-network) - * [Delete a network](#delete-a-network) - * [Subnets](#subnets) - * [Create a subnet](#create-a-subnet) - * [Create multiple subnets](#create-multiple-subnets) - * [List subnets](#list-subnets) - * [Get a subnet](#get-a-subnet) - * [Update a subnet](#update-a-subnet) - * [Delete a subnet](#delete-a-subnet) - * [Ports](#ports) - * [Create a port](#create-a-port) - * [Create multiple ports](#create-multiple-ports) - * [List ports](#list-ports) - * [Get a port](#get-a-port) - * [Update a port](#update-a-port) - * [Delete a port](#delete-a-port) - * [Security Groups](#security-groups) - * [Create a security group](#create-a-security-group) - * [List security groups](#list-security-groups) - * [Get a security group](#get-a-security-group) - * [Delete a security group](#delete-a-security-group) - * [Security group rule Rules](#security-group-rules) - * [Create a security group rule](#create-a-security-group-rule) - * [List security group rules](#list-security-group-rules) - * [Get a security group rule](#get-a-security-group-rule) - * [Delete a security group rule](#delete-a-security-group-rule) - -## Concepts - -To use the Networking service effectively, you should understand the following -key concepts: - -* **Network**: An isolated virtual layer-2 broadcast domain that is typically -reserved for the tenant who created it unless it is configured to be shared. The -network is the main entity in the Networking service. Ports and subnets are -always associated with a network. - -* **Subnet**: An IP address block that can be used to assign IP addresses to -virtual instances (such as servers created using the Compute service). Each -subnet must have a CIDR and must be associated with a network. - -* **Port**: A virtual switch port on a logical network switch. Virtual instances -(such as servers created using the Compute service) attach their interfaces into -ports. The port also defines the MAC address and the IP address or addresses to -be assigned to the interfaces plugged into them. When IP addresses are -associated with a port, this also implies the port is associated with a subnet -because the IP address is taken from the allocation pool for a specific subnet. - -* **Security Group**: A named container for security group rules. - -* **Security Group Rule**: Provide users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. - -## Prerequisites - -### Client -To use the Networking service, you must first instantiate a `OpenStack` or -`Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an -`OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate an -`OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Networking service - -All Networking operations are done via a _networking service object_. To -instantiate this object, call the `networkingService` method on the `$client` -object. This method takes the following arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$networkingService = $client->networkingService(null, $region); -``` - -Any networks, subnets, and ports created with this `$networkingService` instance -are stored in the cloud region specified by `$region`. - -## Networks - -A network is an isolated virtual layer-2 broadcast domain that is typically -reserved for the tenant who created it unless it is configured to be shared. The -network is the main entity in the Networking service. Ports and subnets are -always associated with a network. - -### Create a network - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My private backend network` | -| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | -| `tenantId` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | `123456` | - -You can create a network as shown in the following example: - -```php -$network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' -)); -/** @var $network OpenCloud\Networking\Resource\Network **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] - -### Create multiple networks - -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the preceding table](#create-a-network). - -You can create multiple networks as shown in the following example: - -```php -$networks = $networkingService->createNetworks(array( - array( - 'name' => 'My private backend network #1' - ), - array( - 'name' => 'My private backend network #2' - ) -)); - -foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-networks.php) ] - -### List networks - -You can list all the networks to which you have access as shown in the following example: - -```php -$networks = $networkingService->listNetworks(); -foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-networks.php) ] - -### Get a network - -You can retrieve a specific network by using that network's ID, as shown in the following example: - -```php -$network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); -/** @var $network OpenCloud\Networking\Resource\Network **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-network.php) ] - -### Update a network - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My updated private backend network` | -| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | - -You can update a network as shown in the following example: - -```php -$network->update(array( - 'name' => 'My updated private backend network' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-network.php) ] - -### Delete a network - -You can delete a network as shown in the following example: - -```php -$network->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-network.php) ] - -## Subnets - -A subnet represents an IP address block that can be used to assign IP addresses -to virtual instances (such as servers created using the Compute service). Each -subnet must have a CIDR and must be associated with a network. - -### Create a subnet - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `networkId` | Network this subnet is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `ipVersion` | IP version | Integer (`4` or `6`) | Yes | - | `4` | -| `cidr` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | `192.168.199.0/25` | -| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | -| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `allocationPools` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | `array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))` | -| `hostRoutes` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | -| `tenantId` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | `123456` | - -You can create a subnet as shown in the following example: - -```php -$subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25' -)); -/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-subnet.php) ] - -### Create multiple subnets - -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the preceding table](#create-a-subnet). - -You can create multiple subnets as shown in the following example: - -```php -$subnets = $networkingService->createSubnets(array( - array( - 'name' => 'My subnet #1' - ), - array( - 'name' => 'My subnet #2' - ) -)); - -foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-subnets.php) ] - -### List subnets - -You can list all the subnets to which you have access as shown in the following -example: - -```php -$subnets = $networkingService->listSubnets(); -foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-subnets.php) ] - -### Get a subnet - -You can retrieve a specific subnet by using that subnet's ID, as shown in the -following example: - -```php -$subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); -/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-subnet.php) ] - -### Update a subnet - -This operation takes one parameter, an associative array, with the following -keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My updated subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.62.155` | -| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `hostRoutes` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | - -You can update a subnet as shown in the following example: - -```php -$subnet->update(array( - 'name' => 'My updated subnet', - 'hostRoutes' => array( - array( - 'destination' => '1.1.1.0/24', - 'nexthop' => '192.168.17.19' - ) - ), - 'gatewayIp' => '192.168.62.155' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-subnet.php) ] - -### Delete a subnet - -You can delete a subnet as shown in the following example: - -```php -$subnet->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-subnet.php) ] - -## Ports - -A port represents a virtual switch port on a logical network switch. Virtual -instances (such as servers created using the Compute service) attach their -interfaces into ports. The port also defines the MAC address and the IP address -or addresses to be assigned to the interfaces plugged into them. When IP -addresses are associated with a port, this also implies the port is associated -with a subnet because the IP address is taken from the allocation pool for a -specific subnet. - -### Create a port - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `networkId` | Network this port is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | -| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `macAddress` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | `0F:5A:6F:70:E9:5C` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))` | -| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | -| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | -| `tenantId` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | `123456` | - -You can create a port as shown in the following example: - -```php -$port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' -)); -/** @var $port OpenCloud\Networking\Resource\Port **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-port.php) ] - -### Create multiple ports - -This operation takes one parameter, an indexed array. Each element of this -array must be an associative array with the keys shown in -[the preceding table](#create-a-port). - -You can create multiple ports as shown in the following example: - -```php -$ports = $networkingService->createPorts(array( - array( - 'name' => 'My port #1', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ), - array( - 'name' => 'My port #2', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ) -)); - -foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-ports.php) ] - -### List ports - -You can list all the ports to which you have access as shown in the following -example: - -```php -$ports = $networkingService->listPorts(); -foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-ports.php) ] - -### Get a port - -You can retrieve a specific port by using that port's ID, as shown in the -following example: - -```php -$port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); -/** @var $port OpenCloud\Networking\Resource\Port **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-port.php) ] - -### Update a port - -This operation takes one parameter, an associative array, with the following -keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | -| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))` | -| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | -| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | - -You can update a port as shown in the following example: - -```php -$port->update(array( - 'fixedIps' => array( - array( - 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', - 'ipAddress' => '192.168.199.59' - ) - ) -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-port.php) ] - -### Delete a port - -You can delete a port as shown in the following example: - -```php -$port->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] - -## Security Groups - -A security group is a named container for [security group rules](#security-group-rules). - -### Create a security group - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | `new-webservers` | -| `description` | Description of the security group. | String | No | `null` | `security group for webservers` | - -You can create a security group as shown in the following example: - -```php -$securityGroup = $networkingService->createSecurityGroup(array( - 'name' => 'new-webservers', - 'description' => 'security group for webservers' -)); -/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-security-group.php) ] - -### List security groups - -You can list all the security groups to which you have access as shown in the following -example: - -```php -$securityGroups = $networkingService->listSecurityGroups(); -foreach ($securityGroups as $securityGroup) { - /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-security-groups.php) ] - -### Get a security group - -You can retrieve a specific security group by using that security group's ID, as shown in the -following example: - -```php -$securityGroup = $networkingService->getSecurityGroup('2076db17-a522-4506-91de-c6dd8e837028'); -/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-security-group.php) ] - -### Delete a security group - -You can delete a security group as shown in the following example: - -```php -$securityGroup->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group.php) ] - -## Security Group Rules - -A security group rule provides users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. - -### Create a security group rule - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `securityGroupId` | The security group ID to associate with this security group rule. | String | Yes | - | `2076db17-a522-4506-91de-c6dd8e837028` | -| `direction` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (`ingress` or `egress`) | Yes | - | `ingress` | -| `ethertype` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (`IPv4` or `IPv6`) | No | `IPv4` | `IPv6` | -| `portRangeMin` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the `portRangeMax` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | -| `portRangeMax` | The maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | -| `protocol` | The protocol that is matched by the security group rule. | String (`tcp`, `udp`, `icmp`) | No | `null` | `tcp` | -| `remoteGroupId` | The remote group ID to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `85cc3048-abc3-43cc-89b3-377341426ac5` | -| `remoteIpPrefix` | The remote IP prefix to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `192.168.5.0` | - -You can create a security group rule as shown in the following example: - -```php -$securityGroupRule = $networkingService->createSecurityGroupRule(array( - 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', - 'direction' => 'egress', - 'ethertype' => 'IPv4', - 'portRangeMin' => 80, - 'portRangeMax' => 80, - 'protocol' => 'tcp', - 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' -)); -/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-security-group-rule.php) ] - -### List security group rules - -You can list all the security group rules to which you have access as shown in the following -example: - -```php -$securityGroupRules = $networkingService->listSecurityGroupRules(); -foreach ($securityGroupRules as $securityGroupRule) { - /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-security-group-rules.php) ] - -### Get a security group rule - -You can retrieve a specific security group rule by using that security group rule's ID, as shown in the -following example: - -```php -$securityGroupRule = $networkingService->getSecurityGroupRule(''); -/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-security-group-rule.php) ] - -### Delete a security group rule - -You can delete a security group rule as shown in the following example: - -```php -$securityGroupRule->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group-rule.php) ] +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md index 0b15a2d19..8e224392a 100644 --- a/docs/userguide/ObjectStore/Access.md +++ b/docs/userguide/ObjectStore/Access.md @@ -1,66 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want -``` - -## Temporary URLs - -Temporary URLs allow you to create time-limited Internet addresses that allow you to grant access to your Cloud Files -account. Using Temporary URL, you may allow others to retrieve or place objects in your containers - regardless of -whether they're CDN-enabled. - -### Set "temporary URL" metadata key - -You must set this "secret" value on your account, where it can be used in a global state: - -```php -$account = $service->getAccount(); -$account->setTempUrlSecret('my_secret'); - -echo $account->getTempUrlSecret(); -``` - -The string argument of `setTempUrlSecret()` is optional - if left out, the SDK will generate a random hashed secret -for you. - -### Create a temporary URL - -Once you've set an account secret, you can create a temporary URL for your object. To allow GET access to your object -for 1 minute: - -```php -$object->getTemporaryUrl(60, 'GET'); -``` - -To allow PUT access for 1 hour: - -```php -$object->getTemporaryUrl(360, 'PUT'); -``` - -## Hosting websites on CloudFiles - -To host a static (i.e. HTML) website on CloudFiles, you must follow these steps: - -1. CDN-enable a container -2. Upload all HTML content. You can use nested directory structures. -3. Tell CloudFiles what to use for your default index page like this: - -```php -$container->setStaticIndexPage('index.html'); -``` - -4. (Optional) Tell CloudFiles which error page to use by default: - -```php -$container->setStaticErrorPage('error.html'); -``` - -Bear in mind that steps 3 & 4 do not upload content, but rather specify a reference to an existing page/CloudFiles object. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/access.html diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md index edd26c2c1..fc60751a8 100644 --- a/docs/userguide/ObjectStore/Account.md +++ b/docs/userguide/ObjectStore/Account.md @@ -1,28 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles'); -``` - -## View Account Details - -To see how many containers you have in your account (X-Account-Container-Count), how many objects are in your account -(X-Account-Object-Count), and how many total bytes your account uses (X-Account-Bytes-Used): - -```php -$account = $service->getAccount(); - -// Either return the full Metadata object -$details = $account->getDetails(); - -// or individual values -$account->getContainerCount(); -$account->getObjectCount(); -$account->getBytesUsed(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/account.html diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 642ee968d..9f3b4a5ca 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -1,78 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles'); -``` - -To access the CDN functionality of a particular container: - -```php -$container = $service->getContainer('foo_bar'); - -$cdn = $container->getCdn(); -``` - -## List CDN-enabled container - -To list CDN-only containers, follow the same operation for Storage which lists all containers. The only difference is -which service object you execute the method on: - -```php -$cdnService = $service->getCdnService(); -$cdnContainers = $cdnService->listContainers(); - -foreach ($cdnContainers as $cdnContainer) { - -} -``` - -## CDN-enable and -disable a container - -Before a container can be CDN-enabled, it must exist in the storage system. When a container is CDN-enabled, any objects -stored in it are publicly accessible over the Content Delivery Network by combining the container's CDN URL with the -object name. - -Any CDN-accessed objects are cached in the CDN for the specified amount of time called the TTL. The default TTL value is -259200 seconds, or 72 hours. Each time the object is accessed after the TTL expires, the CDN refetches and caches the -object for the TTL period. - -```php -$container->enableCdn(); -$container->disableCdn(); -``` - -## Serving containers through SSL - -```php -$cdn->getCdnSslUri(); -``` - -## Streaming CDN-enabled containers - -```php -$cdn->getCdnStreamingUri(); -``` - -## iOS streaming - -The Cloud Files CDN allows you to stream video to iOS devices without needing to convert your video. Once you -CDN-enable your container, you have the tools necessary for streaming media to multiple devices. - -```php -$cdn->getIosStreamingUri(); -``` - -## CDN logging - -To enable and disable logging for your CDN: - -```php -$cdn->enableCdnLogging(); -$cdn->disableCdnLogging(); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md index 79e3d4cdc..9f3b4a5ca 100644 --- a/docs/userguide/ObjectStore/CDN/Object.md +++ b/docs/userguide/ObjectStore/CDN/Object.md @@ -1,20 +1,5 @@ ## Setup -You will need to instantiate the container object and access its CDN functionality as -[documented here](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/CDN/Container.md). +Our docs have moved! Please visit the below link: -## Purge CDN-enabled objects - -To remove a CDN object from public access: - -```php -$object->purge(); -``` - -You can also provide an optional e-mail address (or comma-delimeted list of e-mails), which the API will send a -confirmation message to once the object has been completely purged: - -```php -$object->purge('jamie.hannaford@rackspace.com'); -$object->purge('hello@example.com,hallo@example.com'); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index 33ad706dd..ea43fcdf7 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -1,65 +1,5 @@ # Object Store -**Object Store** is an object-based storage system that stores content and metadata as objects in a cloud. +Our docs have moved! Please visit the below link: -Specifically, a cloud is made up of one or more regions. Each region can have several **containers**, created by a user. Each container can container several **objects** (sometimes referred to as files), uploaded by the user. - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -Choose one of the following two options: - -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Object Store service object from the client. -```php -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); -``` - -In the example above, you are connecting to the ``DFW`` region of the cloud. Any containers and objects created with this `$objectStoreService` instance will be stored in that cloud region. - -### 3. Create a container for your objects (also referred to as files). - -```php -$container = $objectStoreService->createContainer('logos'); -``` - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### 4. Upload an object to the container. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/quickstart.php) ] - -## Next steps - -There is a lot more you can do with containers and objects. See -the [complete user guide to the Object Store service](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index 5f75de9d1..e9fd5af14 100644 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -1,182 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -// Create a client object to communicate with various Rackspace Cloud services. -$client = new Rackspace(RACKSPACE_US, array( - 'username' => 'Replace this with your Rackspace Cloud user name', - 'apiKey' => 'Replace this with your Rackspace Cloud API key' -)); - -// Create a service object to use the object store service. The sample code -// creates the object store in the 'DFW' region. -$service = $client->objectStoreService('cloudFiles', 'DFW'); -``` - -## Create container - -To create a new container, you just need to define its name: - -```php -$container = $service->createContainer('my_amazing_container'); -``` - -If the response returned is `FALSE`, there was an API error - most likely due to the fact you have a naming collision. - -Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -## List containers - -### Return a list of containers - -```php -$containerList = $service->listContainers(); - -while ($container = $containerList->next()) { - // Do stuff; some examples below - printf("Container name: %s\n", $container->name); - printf("Number of objects within container: %d\n", $container->getObjectCount()); -} -``` - -Container names are sorted based on a binary comparison, a single built-in collating sequence that compares string -data using SQLite's memcmp() function, regardless of text encoding. - -The list is limited to 10,000 containers at a time. See 1.3 for ways to limit and navigate this list. - -### Return a formatted list of containers - -Currently, the SDK only supports JSON-formatted responses. - -### Controlling a large list of containers - -You may limit and control this list of results by using the `marker` and `end_marker` parameters. The former parameter -(`marker`) tells the API where to begin the list, and the latter (`end_marker`) tells it where to end the list. You may -use either of them independently or together. You may also use the `limit` parameter to fix the number of containers -returned. - -To list a set of containers between two fixed points: - -```php -$someContainers = $service->listContainers(array( - 'marker' => 'container_55', - 'end_marker' => 'container_2001' -)); -``` - -Or to return a limited set: - -```php -$someContainers = $service->listContainers(array('limit' => 560)); -``` - -## Get container - -To retrieve a certain container, either to access its object or metadata: - -```php -$container = $service->getContainer('container_name'); - -echo $container->getObjectCount(); -echo $container->getBytesUsed(); -``` - -## Delete container - -Deleting a container is easy: -```php -$container->delete(); -``` - -Please bear mind that you must delete all objects inside a container before deleting it. This is done for you if you -set the `$deleteObjects` parameter to `TRUE` like so: - -```php -$container->delete(TRUE); -``` - -You can also do it manually: - -```php -$container->deleteAllObjects(); -$container->delete(); -``` - -## Create or update container metadata - -```php -$container->saveMetadata(array( - 'Author' => 'Virginia Woolf', - 'Published' => '1931' -)); -``` - -Please bear in mind that this action will set metadata to this array - overriding existing values and wiping those left -out. To _append_ values to the current metadata: - -```php -$metadata = $container->appendToMetadata(array( - 'Publisher' => 'Hogarth' -)); -``` - -If you only want to set the metadata to the local object, and not immediately retain these values on the API, you can -use a standard setter method - which can contribute to eventual actions like an update: - -```php -$container->setMetadata(array('Foo' => 'Bar')); -``` - -## Container quotas - -The container_quotas middleware implements simple quotas that can be imposed on Cloud Files containers by a user. -Setting container quotas can be useful for limiting the scope of containers that are delegated to non-admin users, -exposed to formpost uploads, or just as a self-imposed sanity check. - -To set quotas for a container: - -```php -use OpenCloud\Common\Constants\Size; - -$container->setCountQuota(1000); -$container->setBytesQuota(2.5 * Size::GB); -``` - -And to retrieve them: - -```php -echo $container->getCountQuota(); -echo $container->getBytesQuota(); -``` - -## Access log delivery - -To view your object access, turn on Access Log Delivery. You can use access logs to analyze the number of people who -access your objects, where they come from, how many requests for each object you receive, and time-based usage patterns -(such as monthly or seasonal usage). - -```php -$container->enableLogging(); -$container->disableLogging(); -``` - -## Syncing containers - -You can synchronize local directories with your CloudFiles/Swift containers very easily. When you do this, the container -will mirror exactly the nested file structure within your local directory: - -```php -$container->uploadDirectory('/home/Jamie/blog'); -``` - -There are four scenarios you should be aware of: - -Local|Remote|Comparison|Action ----|---|---|--- -File exists|File exists|Identical checksum|No action -File exists|File exists|Different checksum|Local file overwrites remote -File exists|File does not exist|-|Local file created in Swift -Files does not exist|File exists|-|Remote file deleted \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/containers.html diff --git a/docs/userguide/ObjectStore/Storage/Migrating.md b/docs/userguide/ObjectStore/Storage/Migrating.md index d92528cbf..8d12c382a 100644 --- a/docs/userguide/ObjectStore/Storage/Migrating.md +++ b/docs/userguide/ObjectStore/Storage/Migrating.md @@ -1,83 +1,5 @@ # Migrating containers (across regions) -## Introduction +Our docs have moved! Please visit the below link: -Currently, there exists no single API operation to copy containers across geographic endpoints. Although the API offers -a `COPY` operation for individual files, this does not work for cross-region copying. The SDK, however, does offer this -functionality. - -You **will** be charged for bandwidth between regions, so it's advisable to use ServiceNet where possible (which is -free). - -## Requirements - -* You must install the full Guzzle package, so that the process can take advantage of Guzzle's batching functionality -(it allows parallel requests to be batched for greater efficiency). You can do this by running: - -```bash -php composer.phar install --dev -``` - -* Depending on the size and number of transfer items, you will need to raise PHP's memory limit: - -```php -ini_set('memory_limit', '512M'); -``` - -* You will need to enact some kind of backoff/retry strategy for rate limits. Guzzle comes with a convenient feature -that just needs to be added as a normal subscriber: - -```php -use Guzzle\Plugin\Backoff\BackoffPlugin; - -$client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); -``` - -This tells the client to retry up to `10` times for failed requests have resulted in these HTTP status codes: `500`, -`503` or `408`. - -## Setup - -You can access all this functionality by executing: - -```php -$ordService = $client->objectStoreService('cloudFiles', 'ORD'); -$iadService = $client->objectStoreService('cloudFiles', 'IAD'); - -$oldContainer = $ordService->getContainer('old_container'); -$newContainer = $iadService->getContainer('new_container'); - -$iadService->migrateContainer($oldContainer, $newContainer); -``` - -It's advisable to do this process in a Cloud Server in one of the two regions you're migrating to/from. This allows you -to use `privateURL` as the third argument in the `objectStoreService` methods like this: - -```php -$client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); -``` - -This will ensure that traffic between your server and your new IAD container will be held over the internal Rackspace -network which is free. - -## Options - -You can pass in an array of arguments to the method: - -```php -$options = array( - 'read.batchLimit' => 100, - 'read.pageLimit' => 100, - 'write.batchLimit' => 50 -); - -$iadService->migrateContainer($oldContainer, $newContainer, $options); -``` - -### Options explained - -Name|Description|Default ----|---|--- -`read.pageLimit`|When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional `objectList` method, which calls the `PaginatedIterator`. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API)|10,000 -`read.batchLimit`|After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together.|1,000 -`write.batchLimit`|Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together.|100 \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/migrating-containers.html diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 4127b2610..0c58ca9fb 100644 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -1,275 +1,5 @@ ## Setup -Conceptually, a container contains objects (also known as files). In order to work with -objects, you will need to instantiate a container object first as [documented here](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/Storage/Container.md). +Our docs have moved! Please visit the below link: -## Note on object properties - -Please be aware that you cannot directly access the properties of DataObject anymore, you __must__ use appropriate getter/ -setter methods: - -|Property|Method| -|--------|------| -|Parent container|`getContainer`| -|Name|`getName`| -|Body of file|`getContent`| -|Size of file|`getContentLength`| -|Type of file|`getContentType`| -|ETag checksum|`getEtag`| -|Last modified date|`getLastModified`| - -## Create an object - -There are three ways to upload a new file, each of which has different business needs. - -> **Note:** Unlike previous versions, you do not need to manually specify your object's content type. The API will do this -for you. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### To upload a single/basic file: - -```php -use OpenCloud\ObjectStore\Resource\DataObject; - -$data = fopen('/path/to/sample.mp3', 'r+'); - -// alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) - -$meta = array( - 'Author' => 'Camera Obscura', - 'Origin' => 'Glasgow' -); - -$metaHeaders = DataObject::stockHeaders($meta); -$customHeaders = array(); -$allHeaders = $metaHeaders + $customHeaders; - -$container->uploadObject('sample.mp3', $data, $allHeaders); -``` - -### To upload multiple small-to-mid sized files: - -```php -$files = array( - array( - 'name' => 'apache.log', - 'path' => '/etc/httpd/logs/error_log' - ), - array( - 'name' => 'mysql.log', - 'body' => fopen('/tmp/mysql.log', 'r+') - ), - array( - 'name' => 'to_do_list.txt', - 'body' => 'PHONE HOME' - ) -); - -$container->uploadObjects($files); -``` - -As you can see, the `name` key is required for every file. You must also specify _either_ a path key (to an existing -file), or a `body`. The `body` can either be a PHP resource or a string representation of the content you want to upload. - -### To upload large files - -For files over 5GB, you will need to use the `OpenCloud\ObjectStore\Upload\TransferBuilder` factory to build your transfer, -upon which you can execute your upload functionality. For your convenience, the Container resource object contains a -simple method to do this heavy lifting for you: - -```php -$transfer = $container->setupObjectTransfer(array( - 'name' => 'video.mov', - 'path' => '/home/jamie/video.mov', - 'metadata' => array( - 'Author' => 'Jamie' - ), - 'concurrency' => 4, - 'partSize' => 1.5 * Size::GB -)); - -$transfer->upload(); -``` - -You can specify how many concurrent cURL connections are used to upload parts of your file. The file is fragmented into -chunks, each of which is uploaded individually as a separate file (the filename of each part will indicate that it's a -segment rather than the full file). After all parts are uploaded, a manifest is uploaded. When the end-user accesses -the 5GB by its true filename, it actually references the manifest file which concatenates each segment into a streaming -download. - -## List objects in a container - -To return a list of objects: - -```php -$files = $container->objectList(); - -foreach ($files as $file) { - // ... do something -} -``` - -By default, 10,000 objects are returned as a maximum. To get around this, you can construct a query which refines your -result set. For a full specification of query parameters relating to collection filtering, see the [official docs](http://docs.openstack.org/api/openstack-object-storage/1.0/content/list-objects.html). - -```php -$container->objectList(array('prefix' => 'logFile_')); -``` - -## Get object - -To retrieve a specific file from Cloud Files: - -```php -$file = $container->getObject('summer_vacation.mp4'); -``` - -### Conditional requests - -You can also perform conditional requests according to [RFC 2616 specification](http://www.ietf.org/rfc/rfc2616.txt) -(§§ 14.24-26). Supported headers are `If-Match`, `If-None-Match`, `If-Modified-Since` and `If-Unmodified-Since`. - -So, to retrieve a file's contents only if it's been recently changed - -```php -$file = $container->getObject('error_log.txt', array( - 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' -)); - -if ($file->getContentLength()) { - echo 'Has been changed since the above date'; -} else { - echo 'Has not been changed'; -} -``` - -Retrieve a file only if it has NOT been modified (and expect a 412 on failure): - -``` -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( - 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' - )); -} catch (ClientErrorResponseException $e) { - echo 'This file has been modified...'; -} -``` - -Finally, you can specify a range - which will return a subset of bytes from the file specified. To return the last 20B -of a file: - -```php -$snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); -``` - -## Update an existing object - -Updating content is easy: - -```php -$file->setContent(fopen('/path/to/new/content', 'r+')); -$file->update(); -``` - -Bear in mind that updating a file name will result in a new file being generated (under the new name). You will need to -delete the old file. - -## Copy object - -To copy a file to another location, you need to specify a string-based destination path: - -```php -$object->copy('/container_2/new_object_name'); -``` - -## Delete object - -```php -$object->delete(); -``` - -## Get object metadata -You can fetch just the object metadata without fetching the full content: -```php -$container->getPartialObject('summer_vacation.mp4'); -``` - -In order to access the metadata on a partial or complete object, use: - -```php -$object->getMetadata(); -``` - -You can turn a partial object into a full object to get the content after looking at the metadata: -```php -$object->refresh(); -``` - -You can also update to get the latest metadata: - -```php -$object->retrieveMetadata(); -``` - -## Update object metadata - -Similarly, with setting metadata there are two options: you can update the metadata values of the local object (i.e. no -HTTP request) if you anticipate you'll be executing one soon (an update operation for example): - -```php -// There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation -$object->setMetadata(array( - 'Author' => 'Hemingway' -)); - -// ... code here - -$object->update(); -``` - -Alternatively, you can update the API straight away - so that everything is retained: - -```php -$object->saveMetadata(array( - 'Author' => 'Hemingway' -)); -``` - -Please be aware that these methods override and wipe existing values. If you want to append values to your metadata, use -the correct method: - -```php -$metadata = $object->appendToMetadata(array( - 'Author' => 'Hemingway' -)); - -$object->saveMetadata($metadata); -``` - -## Extract archive - -CloudFiles provides you the ability to extract uploaded archives to particular destinations. The archive will be extracted -and its contents will populate the particular area specified. To upload file (which might represent a directory structure) -into a particular container: - -```php -use OpenCloud\ObjectStore\Constants\UrlType; - -$service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); -``` - -You can also omit the container name (i.e. provide an empty string as the first argument). If you do this, the API will -create the containers necessary to house the extracted files - this is done based on the filenames inside the archive. - -## Bulk delete - -Bulk delete a set of paths: - -```php -$pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - -$service->bulkDelete($pathsToBeDeleted); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/objects.html diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index fa04bdeaf..6881ee3e6 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -1,625 +1,5 @@ # The Complete User Guide to the Object Store Service -**Object Store** is an object-based storage system that stores content and metadata as objects in a cloud. +Our docs have moved! Please visit the below link: -## Prerequisites - -### Client -To use the object store service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Object Store Service -All operations on the object store are done via an object store service object. - -```php -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); -``` - -In the example above, you are connecting to the ``DFW`` region of the cloud. Any containers and objects created with this `$objectStoreService` instance will be stored in that cloud region. - -## Containers -A **container** defines a namespace for **objects**. An object with the same name in two different containers represents two different objects. - -For example, you may create a container called `logos` to hold all the image files for your blog. - -A container may contain zero or more objects in it. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### Create Container - -```php -$container = $objectStoreService->createContainer('logos'); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/create-container.php) ] - -### Get Container Details -You can retrieve a single container's details by using its name. An instance of `OpenCloud\ObjectStore\Resource\Container` is returned. - -```php -$container = $objectStoreService->getContainer('logos'); - -/** @var $container OpenCloud\ObjectStore\Resource\Container **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container.php) ] - -### List Containers -You can retrieve a list of all your containers. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$containers = $objectStoreService->listContainers(); -foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-containers.php) ] - -### Set or Update Container Metadata -You can set metadata on a container. - -```php -$container->saveMetadata(array( - 'author' => 'John Doe' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-metadata.php) ] - -### Get Container Metadata -You can retrieve the metadata for a container. - -```php -$containerMetadata = $container->getMetadata(); - -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-metadata.php) ] - -### Delete Container -When you no longer have a need for the container, you can remove it. - -If the container is empty (that is, it has no objects in it), you can remove it as shown below: - -```php -$container->delete(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/delete-container.php) ] - -If the container is not empty (that is, it has objects in it), you have two choices in how to remove it: - -* [Individually remove each object](#delete-object) in the container, then remove the container itself as shown above, or - -* Remove the container and all the objects within it as shown below: - - ```php - $container->delete(true); - ``` - [ [Get the executable PHP script for this example](/samples/ObjectStore/delete-container-recursive.php) ] - -### Get Object Count - -You can quickly find out how many objects are in a container. - -```php -$containerObjectCount = $container->getObjectCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-object-count.php) ] - -In the example above, `$containerObjectCount` will contain the number of objects in the container represented by `$container`. - -### Get Bytes Used - -You can quickly find out the space used by a container, in bytes. - -```php -$containerSizeInBytes = $container->getBytesUsed(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-bytes-used.php) ] - -In the example above, `$containerSizeInBytes` will contain the space used, in bytes, by the container represented by `$container`. - -### Container Quotas - -#### Set Quota for Number of Objects - -You can set a quota for the maximum number of objects that may be stored in a container. - -```php -$maximumNumberOfObjectsAllowedInContainer = 25; -$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-count-quota.php) ] - -#### Set Quota for Total Size of Objects - -You can set a quota for the maximum total space (in bytes) used by objects in a container. - -```php -use OpenCloud\Common\Constants\Size; - -$maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; -$container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-bytes-quota.php) ] - -#### Get Quota for Number of Objects - -You can retrieve the quota for the maximum number of objects that may be stored in a container. - -```php -$maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-count-quota.php) ] - -#### Get Quota for Total Size of Objects - -You can retrieve the quota for the maximum total space (in bytes) used by objects in a container. - -```php -$maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-bytes-quota.php) ] - -## Objects - -An **object** (sometimes referred to as a file) is the unit of storage in an Object Store. An object is a combination of content (data) and metadata. - -For example, you may upload an object named `php-elephant.jpg`, a JPEG image file, to the `logos` container. Further, you may assign metadata to this object to indicate that the author of this object was someone named Jane Doe. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### Upload Object - -Once you have created a container, you can upload objects to it. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$object = $container->uploadObject($remoteFileName, $fileData); -/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object.php) ] - -In the example above, an image file from the local filesystem (`path/to/local/php-elephant.jpg`) is uploaded to a container in the Object Store. - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -It is also possible to upload an object and associate metadata with it. - -```php -use OpenCloud\ObjectStore\Resource\DataObject; - -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); -$metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData, $allHeaders); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object-with-metadata.php) ] - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -#### Pseudo-hierarchical Folders -Although you cannot nest directories in an Object Store, you can simulate a hierarchical structure within a single container by adding forward slash characters (`/`) in the object name. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'languages/php/elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/pseudo-hierarchical-folders.php) ] - -In the example above, an image file from the local filesystem (`/path/to/local/php-elephant.jpg`) is uploaded to a container in the Object Store. Within that container, the filename is `languages/php/elephant.jpg`, where `languages/php/` is a pseudo-hierarchical folder hierarchy. - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -#### Upload Multiple Objects -You can upload more than one object at a time to a container. - -```php -$objects = array( - array( - 'name' => 'php-elephant.jpg', - 'path' => '/path/to/local/php-elephant.jpg' - ), - array( - 'name' => 'python-snake.jpg', - 'path' => '/path/to/local/python-snake.jpg' - ), - a -); - -$responses = $container->uploadObjects($objects); -foreach ($responses as $response) { - /** @var $response \Guzzle\Http\Message\Response **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects.php) ] - -In the above example, the contents of two files present on the local filesystem are uploaded as objects to the container referenced by `$container`. - -Instead of specifying the `path` key in an element of the `$objects` array, you can specify a `body` key whose value is a string or a stream representation. - -You can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. - -``` -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); -$metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; - -$container->uploadObjects($objects, $allHeaders); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects-with-metadata.php) ] - -In the example above, every object referenced within the `$objects` array will be uploaded with the same metadata. - -Finally, if you want the `uploadObjects` method to return an array of `OpenCloud\ObjectStore\Resource\DataObject` objects instead of an array of `Guzzle\Http\Message\Response` objects, you can specify a third parameter to the `uploadObjects` method: - -``` -use OpenCloud\ObjectStore\Enum\ReturnType; - -$dataObjects = $container->uploadObjects($objects, $allHeaders, ReturnType::DATA_OBJECT_ARRAY); -foreach ($dataObjects as $dataObject) { - /** @var $dataObject OpenCloud\ObjectStore\Resource\DataObject **/ -} -``` - -### Large Objects - -If you want to upload objects larger than 5GB in size, you must use a different upload process. - -```php -$options = array( - 'name' => 'san_diego_vacation_video.mp4', - 'path' => '/path/to/local/videos/san_diego_vacation.mp4' -); -$objectTransfer = $container->setupObjectTransfer($options); -$objectTransfer->upload(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-large-object.php) ] - -The process shown above will automatically partition your large object into small chunks and upload them concurrently to the container represented by `$container`. - -You can tune the parameters of this process by specifying additional options in the `$options` array. Here is a complete listing of keys that can be specified in the `$options` array: - -| Key name | Description | Data Type | Required? | Default Value | Example | -| -------------- | --------------- | ------------- | -------------- | ------------------ | ----------- | -| `name` | Name of large object in container | String | Yes | - | `san_diego_vacation_video.mp4` | -| `path` | Path to file containing object data on local filesystem | String | One of `path` or `body` must be specified | - | `/path/to/local/videos/san_diego_vacation.mp4` | -| `body` | String or stream representation of object data | String \| Stream | One of `path` or `body` must be specified | - | `... lots of data ...` | -| `metadata` | Metadata for the object | Associative array of metadata key-value pairs | No | `array()` | `array( "Author" => "Jane Doe" )` | -| `partSize` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | `1073741824` (1GB) | `52428800` (50MB) | -| `concurrency` | The number of concurrent transfers to execute as part of the upload | Integer | No | `1` (no concurrency; upload chunks serially) | `10` | -| `progress` | A [callable function or method](http://us1.php.net/manual/en/language.types.callable.php) which is called to report progress of the the upload. See [`CURLOPT_PROGRESSFUNCTION` documentation](http://us2.php.net/curl_setopt) for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | `reportProgress` | - -### Auto-extract Archive Files - -You can upload a tar archive file and have the Object Store service automatically extract it into a container. - -```php -use OpenCloud\ObjectStore\Constants\UrlType; - -$localArchiveFileName = '/path/to/local/image_files.tar.gz'; -$remotePath = 'images/'; - -$fileData = fopen($localArchiveFileName, 'r'); -$objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/auto-extract-archive-files.php) ] - -In the above example, a local archive file named `image_files.tar.gz` is uploaded to an Object Store container named `images` (defined by the `$remotePath` variable). - -Note that while we call `fopen` to open a file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `bulkExtract` call. - -The third parameter to `bulkExtract` is the type of the archive file being uploaded. The acceptable values for this are: - -* `UrlType::TAR` for tar archive files, *or*, -* `UrlType:TAR_GZ` for tar archive files that are compressed with gzip, *or* -* `UrlType::TAR_BZ` for tar archive file that are compressed with bzip - -Note that the value of `$remotePath` could have been a (pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as `images/blog` as well. - -### List Objects in a Container -You can list all the objects stored in a container. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$objects = $container->objectList(); -foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects.php) ] - -You can list only those objects in the container whose names start with a certain prefix. - -```php -$options = array( - 'prefix' => 'php' -); - -$objects = $container->objectList($options); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects-with-prefix.php) ] - -In general, the `objectList()` method described above takes an optional parameter (`$options` in the example above). This parameter is an associative array of various options. Here is a complete listing of keys that can be specified in the `$options` array: - -| Key name | Description | Data Type | Required? | Default Value | Example | -| -------------- | --------------- | ------------- | -------------- | ------------------ | ----------- | -| `prefix` | Given a string x, limits the results to object names beginning with x. | String | No | | `php` | -| `limit` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | -| `marker` | Given a string x, returns object names greater than the specified marker. | String | No | | `php-elephant.jpg` | -| `end_marker` | Given a string x, returns object names less than the specified marker. | String | No | | `python-snakes.jpg` | - -### Retrieve Object -You can retrieve an object and its metadata, given the object's container and name. - -```php -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); - -/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - -$objectContent = $object->getContent(); - -/** @var $objectContent Guzzle\Http\EntityBody **/ - -// Write object content to file on local filesystem. -$objectContent->rewind(); -$stream = $objectContent->getStream(); -$localFilename = tempnam("/tmp", 'php-opencloud-'); -file_put_contents($localFilename, $stream); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object.php) ] - -In the example above, `$object` is the object named `php-elephant.jpg` in the container represented by `$container`. Further, `$objectContent` represents the contents of the object. It is of type [`Guzzle\Http\EntityBody`](http://api.guzzlephp.org/class-Guzzle.Http.EntityBody.html). - -### Retrieve Object Metadata -You can retrieve just an object's metadata without retrieving its contents. - -```php -$objectName = 'php-elephant.jpg'; -$object = $container->getPartialObject($objectName); -$objectMetadata = $object->getMetadata(); - -/** @var $objectMetadata \OpenCloud\Common\Metadata **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object-metadata.php) ] - -In the example above, while `$object` is an instance of `OpenCloud\ObjectStore\Resource\DataObject`, that instance is only partially populated. Specifically, only properties of the instance relating to object metadata are populated. - -### Temporary URLs - -The Temporary URL feature allows you to create limited-time Internet addresses that allow you to grant limited access to your Object Store account. Using this feature, you can allow others to retrieve or place objects in your Object Store account for a specified amount of time. Access to the temporary URL is independent of whether or not your account is [CDN-enabled](#cdn-containers). Even if you do not CDN-enable a container, you can still grant temporary public access through a temporary URL. - -First, you must set the temporary URL secret on your account. This is a one-time operation; you only need to perform it the very first time you wish to use the temporary URLs feature. - -```php -$account->setTempUrlSecret(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-account-temp-url-secret.php) ] - -Note that this operation is carried out on `$account`, which is an instance of `OpenCloud\ObjectStore\Resource\Account`, a class representing [your object store account](#accounts). - -The above operation will generate a random secret and set it on your account. Instead of a random secret, if you wish to provide a secret, you can supply it as a parameter to the `setTempUrlSecret` method. - -```php -$account->setTempUrlSecret(''); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-account-temp-url-secret-specified.php) ] - -Once a temporary URL secret has been set on your account, you can generate a temporary URL for any object in your Object Store. - -```php -$expirationTimeInSeconds = 3600; // one hour from now -$httpMethodAllowed = 'GET'; -$tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object-temporary-url.php) ] - -In the example above, a temporary URL for the object is generated. This temporary URL will provide public access to the object for an hour (3600 seconds), as specified by the `$expirationTimeInSeconds` variable. Further, only GET HTTP methods will be allowed on this URL, as specified by the `$httpMethodAllowed` variable. The other value allowed for the `$httpMethodAllowed` variable would be `PUT`. - -You can also retrieve the temporary URL secret that has been set on your account. - -```php -$tempUrlSecret = $account->getTempUrlSecret(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-temp-url-secret.php) ] - -### Update Object - -You can update an object's contents (as opposed to [updating its metadata](#update-object-metadata)) by simply re-[uploading the object](#upload-object) to its container using the same object name as before. - -### Update Object Metadata - -You can update an object's metadata after it has been uploaded to a container. - -```php -$object->saveMetadata(array( - 'author' => 'John Doe' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/update-object-metadata.php) ] - -### Copy Object - -You can copy an object from one container to another, provided the destination container already exists. - -```php -$object->copy('logos_copy/php.jpg'); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/copy-object.php) ] - -In the example above, both the name of the destination container (`logos_copy`)and the name of the destination object (`php.jpg`) have to be specified, separated by a `/`. - -### Delete Object - -When you no longer need an object, you can delete it. - -```php -$object->delete(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/delete-object.php) ] - -### Bulk Delete - -While you can delete individual objects as shown above, you can also delete objects and empty containers in bulk. - -```php -$objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/bulk-delete.php) ] - -In the example above, two objects (`some_container/object_a.png`, `some_other_container/object_z.png`) and one empty container (`some_empty_container`) are all being deleted in bulk via a single command. - -## CDN Containers - -Note: The functionality described in this section is available only on the Rackspace cloud. It will not work as described when working with a vanilla OpenStack cloud. - -Any container can be converted to a CDN-enabled container. When this is done, the objects within the container can be accessed from anywhere on the Internet via a URL. - -### Enable CDN Container - -To take advantage of CDN capabilities for a container and its objects, you must CDN-enable that container. - -```php -$container->enableCdn(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/enable-container-cdn.php) ] - -### Public URLs - -Once you have CDN-enabled a container, you can retrieve a publicly-accessible URL for any of its objects. There are four types of publicly-accessible URLs for each object. Each type of URL is meant for a different purpose. The sections below describe each of these URL types and how to retrieve them. - -#### HTTP URL -You can use this type of URL to access the object over HTTP. - -``` -$httpUrl = $object->getPublicUrl(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-http-url.php) ] - -#### Secure HTTP URL -You can use this type of URL to access the object over HTTP + TLS/SSL. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$httpsUrl = $object->getPublicUrl(UrlType::SSL); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-https-url.php) ] - -#### Streaming URL -You can use this type of URL to stream a video or audio object using Adobe's HTTP Dynamic Streaming. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$streamingUrl = $object->getPublicUrl(UrlType::STREAMING); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-streaming-url.php) ] - -#### IOS Streaming URL - -You can use this type of URL to stream an audio or video object to an iOS device. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-ios-streaming-url.php) ] - -### Update CDN Container TTL -You can update the TTL of a CDN-enabled container. - -```php -$cdnContainer = $container->getCdn(); -$cdnContainer->setTtl(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-cdn-container-ttl.php) ] - -### Disable CDN Container - -If you no longer need CDN capabilities for a container, you can disable them. - -```php -$container->disableCdn(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/disable-container-cdn.php) ] - -## Accounts -An **account** defines a namespace for **containers**. An account can have zero or more containers in it. - -### Retrieve Account -You must retrieve the account before performing any operations on it. - -```php -$account = $objectStoreService->getAccount(); -``` - -### Get Container Count - -You can quickly find out how many containers are in your account. - -```php -$accountContainerCount = $account->getContainerCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-container-count.php) ] - -### Get Object Count - -You can quickly find out how many objects are in your account. - -```php -$accountObjectCount = $account->getObjectCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-object-count.php) ] - -In the example above, `$accountObjectCount` will contain the number of objects in the account represented by `$account`. - -### Get Bytes Used - -You can quickly find out the space used by your account, in bytes. - -```php -$accountSizeInBytes = $account->getBytesUsed(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-bytes-used.php) ] - -In the example above, `$accountSizeInBytes` will contain the space used, in bytes, by the account represented by `$account`. +https://doc.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index cb3c6245a..862a61ed7 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -1,81 +1,5 @@ # Orchestration -**Orchestration** is a service that can be used to create and manage cloud -resources. Examples of such resources are databases, load balancers, -servers and software installed on them. +Our docs have moved! Please visit the below link: -## Concepts - -To use the Orchestration service effectively, you should understand several -key concepts: - -* **Template**: An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment. The template specifies what resources should be used, what -attributes of these resources are parameterized and what information is -output to the user when a template is instantiated. - -* **Resource**: A resource is a template artifact that represents some component of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load balancer, some configuration management system, and so forth). - -* **Stack**: A stack is a running instance of a template. When a stack is created, -the resources specified in the template are created. - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Orchestration service object from the client. -All Orchestration operations are done via an _orchestration service object_. To -instantiate this object, call the `orchestrationService` method on the `$client` -object as shown in the following example: - -```php -$region = ''; -$orchestrationService = $client->orchestrationService(null, $region); -``` - -Any stacks and resources created with this `$orchestrationService` instance will -be stored in the cloud region specified by `$region`. - -### 3. Create a stack from a template. -```php -$stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/quickstart.php) ] - -## Next steps - -Once you have created a stack, there is more you can do with it. See [complete user guide for orchestration](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index a6d8af0f9..902d7741b 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -1,506 +1,5 @@ # Complete User Guide for the Orchestration Service -Orchestration is a service that you can use to create and manage cloud resources -such as databases, load balancers, and servers, and the software installed on -servers. +Our docs have moved! Please visit the below link: -## Table of Contents - -* [Concepts](#concepts) -* [Prerequisites](#prerequisites) - * [Client](#client) - * [Orchestration service](#orchestration-service) -* [Templates](#templates) - * [Validate template](#validate-template) - * [Validate a template from a file](#validate-a-template-from-a-file) - * [Validate Template from URL](#validate-template-from-url) -* [Stacks](#stacks) - * [Preview stack](#preview-stack) - * [Preview a stack from a template file](#preview-a-stack-from-a-template-file) - * [Preview a stack from a template URL](#preview-a-stack-from-a-template-url) - * [Create stack](#create-stack) - * [Create a stack from a template file](#create-a-stack-from-a-template-file) - * [Create a stack from a template URL](#create-a-stack-from-a-template-url) - * [List stacks](#list-stacks) - * [Get stack](#get-stack) - * [Get stack template](#get-stack-template) - * [Update stack](#update-stack) - * [Update a stack from a template file](#update-a-stack-from-a-template-file) - * [Update Stack from Template URL](#update-stack-from-template-url) - * [Delete stack](#delete-stack) - * [Abandon Stack](#abandon-stack) - * [Adopt stack](#adopt-stack) -* [Stack resources](#stack-resources) - * [List stack resources](#list-stack-resources) - * [Get stack resource](#get-stack-resource) - * [Get stack resource metadata](#get-stack-resource-metadata) -* [Stack resource events](#stack-resource-events) - * [List stack events](#list-stack-events) - * [List stack resource events](#list-stack-resource-events) - * [Get stack resource event](#get-stack-resource-event) -* [Resource types](#resource-types) - * [List resource types](#list-resource-types) - * [Get resource type](#get-resource-type) - * [Get resource type template](#get-resource-type-template) -* [Build info](#build-info) - * [Get build info](#get-build-info) - -## Concepts - -To use the Orchestration service effectively, you should understand the following -key concepts: - -* **Template**: A JSON or YAML document that describes how a set of resources should -be assembled to produce a working deployment. The template specifies the resources -to use, the attributes of these resources that are parameterized and the information -that is sent to the user when a template is instantiated. - -* **Resource**: Some component of your architecture (a cloud server, a group of scaled -cloud servers, a load balancer, some configuration management system, and so on) that -is defined in a template. - -* **Stack**: A running instance of a template. When a stack is created, the resources -specified in the template are created. - -## Prerequisites - -### Client -To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Orchestration service -All Orchestration operations are done via an _orchestration service object_. To -instantiate this object, call the `orchestrationService` method on the `$client` -object. This method takes two arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudOrchestration` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$orchestrationService = $client->orchestrationService(null, $region); -``` - -Any stacks and resources created with this `$orchestrationService` instance will -be stored in the cloud region specified by `$region`. - -## Templates -An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment (known as a [stack](#stacks)). The template specifies the resources -to use, the attributes of these resources that are parameterized and the -information that is sent to the user when a template is instantiated. - -### Validate template -Before you use a template to create a stack, you might want to validate it. - -#### Validate a template from a file -If your template is stored on your local computer as a JSON or YAML file, you -can validate it as shown in the following example: - -```php -use OpenCloud\Common\Exceptions\InvalidTemplateError; - -try { - $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') - )); -} catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid -} -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] - -#### Validate Template from URL -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can validate it as shown in the following example: - -```php -use OpenCloud\Common\Exceptions\InvalidTemplateError; - -try { - $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' - )); -} catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid -} -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] - -## Stacks -A stack is a running instance of a template. When a stack is created, the -[resources](#stack-resources) specified in the template are created. - -### Preview stack -Before you create a stack from a template, you might want to see what that stack -will look like. This is called _previewing the stack_. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters | Yes | - | `simple-lamp-setup` | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of the template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters. For example, see the parameters in [this template section](https://github.com/rackspace-orchestration-templates/lamp/blob/master/lamp.yaml#L22) | Associative array | No | `null` | `array('flavor_id' => 'general1-1')` | - -#### Preview a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to preview a stack as shown in the following example: - -```php -$stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-file.php) ] - -#### Preview a stack from a template URL - -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to preview a stack as shown in the following -example: - -```php -$stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-url.php) ] - -### Create stack -You can create a stack from a template. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | - | `simple-lamp-setup` | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null` | `array('server_hostname' => 'web01')` | -| `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | - -#### Create a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to create a stack as shown in the following example: - -```php -$stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-file.php) ] - -#### Create a stack from a template URL -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to create a stack as shown in the following -example: - -```php -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-url.php) ] - -### List stacks -You can list all the stacks that you have created as shown in the following example: - -```php -$stacks = $orchestrationService->listStacks(); -foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stacks.php) ] - -### Get stack -You can retrieve a specific stack using its name, as shown in the following example: - -```php -$stack = $orchestrationService->getStack('simple-lamp-setup'); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack.php) ] - -### Get stack template -You can retrieve the template used to create a stack. Note that a JSON string is -returned, regardless of whether a JSON or YAML template was used to create the stack. - -```php -$stackTemplate = $stack->getTemplate(); -/** @var $stackTemplate string **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-template.php) ] - -### Update stack -You can update a running stack. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null`| `array('flavor_id' => 'general1-1')` | -| `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | - -#### Update a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to update a stack as shown in the following example: - -```php -$stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-file.php) ] - -#### Update Stack from Template URL - -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to update a stack as shown in the following -example: - -```php -$stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-url.php) ] - -### Delete stack - -If you no longer need a stack and all its resources, you can delete the stack -_and_ the resources as shown in the following example: - -```php -$stack->delete(); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/delete-stack.php) ] - -### Abandon Stack - -If you want to delete a stack but preserve all its resources, you can abandon -the stack as shown in the following example: - -```php -$abandonStackData = $stack->abandon(); -/** @var $abandonStackData string **/ - -file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/abandon-stack.php) ] - -Note that this operation returns data about the abandoned stack as a string. You -can use this data to recreate the stack by using the [adopt stack](#adopt-stack) -operation. - -### Adopt stack - -If you have data from an abandoned stack, you can re-create the stack as shown -in the following example: - -```php -$stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/adopt-stack.php) ] - -## Stack resources - -A stack is made up of zero or more resources such as databases, load balancers, -and servers, and the software installed on servers. - -### List stack resources - -You can list all the resources for a stack as shown in the following example: - -```php -$resources = $stack->listResources(); -foreach ($resources as $resource) { - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] - -### Get stack resource - -You can retrieve a specific resource in a stack bt using that resource's name, -as shown in the following example: - -```php -$resource = $stack->getResource('load-balancer'); -/** @var $resource OpenCloud\Orchestration\Resource\Resource **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] - -### Get stack resource metadata - -You can retrieve the metadata for a specific resource in a stack as shown in the -following example: - -```php -$resourceMetadata = $resource->getMetadata(); -/** @var $resourceMetadata \stdClass **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] - -## Stack resource events -Operations on resources within a stack (such as the creation of a resource) produce -events. - -### List stack events -You can list all of the events for all of the resources in a stack as shown in -the following example: - -```php -$stackEvents = $stack->listEvents(); -foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] - -### List stack resource events -You can list all of the events for a specific resource in a stack as shown in the -following example: - -```php -$resourceEvents = $resource->listEvents(); -foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] - -### Get stack resource event -You can retrieve a specific event for a specific resource in a stack, by using -the resource event's ID, as shown in the following example: - -```php -$resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -/** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] - -## Resource types -When you define a template, you must use resource types supported by your cloud. - -### List resource types -You can list all supported resource types as shown in the following example: - -```php -$resourceTypes = $orchestrationService->listResourceTypes(); -foreach ($resourceTypes as $resourceType) { - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-resource-types.php) ] - -### Get resource type -You can retrieve a specific resource type's schema as shown in the following example: - -```php -$resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); -/** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type.php) ] - -### Get resource type template -You can retrieve a specific resource type's representation as it would appear -in a template, as shown in the following example: - -```php -$resourceTypeTemplate = $resourceType->getTemplate(); -/** @var $resourceTypeTemplate string **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type-template.php) ] - -## Build info - -### Get build info -You can retrieve information about the current Orchestration service build as -shown in the following example: - -```php -$buildInfo = $orchestrationService->getBuildInfo(); -/** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-build-info.php) ] +https://doc.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index dbaa53ba9..0e338956c 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -1,123 +1,5 @@ ## 1. Introduction -A __Claim__ is the process of a worker checking out a message to perform a task. Claiming a message prevents other -workers from attempting to process the same messages. +Our docs have moved! Please visit the below link: -## 2. Setup - -A Claim is initialized on its parent object, a Queue: - -```php -// To initialize an empty object: -$claim = $queue->getClaim(); - -// or retrieve a specific claim: -$claim = $queue->getClaim('51db7067821e727dc24df754'); -``` - -## 3. Claim messages - -### 3.1 Description - -This operation claims a set of messages (up to the value of the limit parameter) from oldest to newest and skips any -messages that are already claimed. If no unclaimed messages are available, the API returns a `204 No Content` message. - -When a client (worker) finishes processing a message, it should delete the message before the claim expires to ensure -that the message is processed only once. As part of the delete operation, workers should specify the claim ID (which is -best done by simply using the provided href). If workers perform these actions, then if a claim simply expires, the -server can return an error and notify the worker of the race condition. This action gives the worker a chance to roll -back its own processing of the given message because another worker can claim the message and process it. - -The age given for a claim is relative to the server's clock. The claim's age is useful for determining how quickly -messages are getting processed and whether a given message's claim is about to expire. - -When a claim expires, it is released. If the original worker failed to process the message, another client worker can -then claim the message. - -### 3.2 Attributes - -The `ttl` attribute specifies how long the server waits before releasing the claim. The ttl value must be between 60 and -43200 seconds (12 hours). You must include a value for this attribute in your request. - -The `grace` attribute specifies the message grace period in seconds. The value of grace value must be between 60 and -43200 seconds (12 hours). You must include a value for this attribute in your request. To deal with workers that have -stopped responding (for up to 1209600 seconds or 14 days, including claim lifetime), the server extends the lifetime of -claimed messages to be at least as long as the lifetime of the claim itself, plus the specified grace period. If a -claimed message would normally live longer than the grace period, its expiration is not adjusted. - -The `limit` attribute specifies the number of messages to return, up to 20 messages. If limit is not specified, limit - defaults to 10. The limit parameter is optional. - -### 3.3 Code - -```php -use OpenCloud\Common\Constants\Datetime; - -$queue->claimMessages(array( - 'limit' => 15, - 'grace' => 5 * Datetime::MINUTE, - 'ttl' => 5 * Datetime::MINUTE -)); -``` - -## 4. Query claim - -### 4.1 Description - -This operation queries the specified claim for the specified queue. Claims with malformed IDs or claims that are not -found by ID are ignored. - -### 4.2 Attributes - -Claim ID. - -### 4.3 Code - -```php -$claim = $queue->getClaim('51db7067821e727dc24df754'); -``` - -## 5. Update claim - -### 5.1 Description - -This operation updates the specified claim for the specified queue. Claims with malformed IDs or claims that are not -found by ID are ignored. - -Clients should periodically renew claims during long-running batches of work to avoid losing a claim while processing a -message. The client can renew a claim by executing this method on a specific __Claim__ and including a new TTL. The API - will then reset the age of the claim and apply the new TTL. - -### 5.2 Attributes - -See section 4.2. - -### 5.3 Code - -```php -use OpenCloud\Common\Constants\Datetime; - -$claim->update(array( - 'ttl' => 10 * Datetime::MINUTE -)); -``` - -## 6. Release claim - -### 6.1 Description - -This operation immediately releases a claim, making any remaining undeleted messages that are associated with the -claim available to other workers. Claims with malformed IDs or claims that are not found by ID are ignored. - -This operation is useful when a worker is performing a graceful shutdown, fails to process one or more messages, or is -taking longer than expected to process messages, and wants to make the remainder of the messages available to other workers. - -### 6.2 Attributes - -See section 4.2. - -### 6.3 Code - -```php -$message->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/queues/claims.html diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md index d27f2fbbf..ce0f54b07 100644 --- a/docs/userguide/Queues/Message.md +++ b/docs/userguide/Queues/Message.md @@ -1,207 +1,5 @@ ## 1. Introduction -A __Message__ is a task, a notification, or any meaningful data that a producer or publisher sends to the queue. A -message exists until it is deleted by a recipient or automatically by the system based on a TTL (time-to-live) value. +Our docs have moved! Please visit the below link: -## 2. Setup - -A message is initialized from its parent object, a Queue: - -```php -// Setup an empty object -$message = $queue->getMessage(); - -// or retrieve an existing one -$message = $queue->getMessage(''); -``` - -## 3. Post message - -### 3.1 Description - -This operation posts the specified message or messages. You can submit up to 10 messages in a single request. - -When posting new messages, you specify only the `body` and `ttl` for the message. The API will insert metadata, such as -ID and age. - -### 3.2 Parameters - -How you pass through the array structure depends on whether you are executing multiple (3.3.2) or single (3.3.3) posts, -but the keys are the same. - -The `body` attribute specifies an arbitrary document that constitutes the body of the message being sent. The size of -this body is limited to 256 KB, excluding whitespace. - -The `ttl` attribute specifies how long the server waits before marking the message as expired and removing it from the -queue. The value of ttl must be between 60 and 1209600 seconds (14 days). Note that the server might not actually -delete the message until its age has reached up to (ttl + 60) seconds, to allow for flexibility in storage implementations. - -### 3.3 Code samples - -#### 3.3.1 Posting a single message - -``` -use OpenCloud\Common\Constants\Datetime; - -$queue->createMessage(array( - 'body' => (object) array( - 'event' => 'BackupStarted', - 'deadline' => '26.12.2013 - ), - 'ttl' => 2 * Datetime::DAY -)); -``` - -#### 3.3.2 Post a batch of messages - -Please note that the list of messages will be truncated at 10. For more, please execute another method call. - -```php -use OpenCloud\Common\Constants\Datetime; - -$messages = array( - array( - 'body' => (object) array( - 'play' => 'football' - ), - 'ttl' => 2 * Datetime::DAY - ), - array( - 'body' => (object) array( - 'play' => 'tennis' - ), - 'ttl' => 50 * Datetime::HOUR - ) -); - -$queue->createMessages($messages); -``` - -## 4. Get messages - -### 4.1 Description - -This operation gets the message or messages in the specified queue. - -Message IDs and markers are opaque strings. Clients should make no assumptions about their format or length. Furthermore, -clients should assume that there is no relationship between markers and message IDs (that is, one cannot be derived -from the other). This allows for a wide variety of storage driver implementations. - -Results are ordered by age, oldest message first. - -### 4.2 Parameters - -A hash of options. - -|Name|Style|Type|Description| -|----|----|----|----| -|marker|Query|String|Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional.| -|limit|Query|Integer|When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional.| -|echo|Query|Boolean|Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. -|include_claimed|Query|​Boolean|Determines whether the API returns claimed messages and unclaimed messages. The include_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include_claimed uses the default value of false (only unclaimed messages are returned). Optional. - -### 4.3 Code sample - -```php -$messages = $queue->listMessages(array( - 'marker' => '51db6f78c508f17ddc924357', - 'limit' => 20, - 'echo' => true -)); - -while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; -} -``` - -## 5. Get a set of messages by ID - -### 5.1 Description - -This operation provides a more efficient way to query multiple messages compared to using a series of individual GET. -Note that the list of IDs cannot exceed 20. If a malformed ID or a nonexistent message ID is provided, it is ignored, -and the remaining messages are returned. - -### 5.2 Parameters - -A hash of options. - -|Name|Style|Type|Description| -|----|-----|----|-----------| -|ids|Query|String|Specifies the IDs of the messages to get. Format multiple message ID values by separating them with -commas (comma-separated). Optional.| -|claim_id|Query|​String|Specifies the claim ID with which the message is associated. Optional.| -|----|-----|----|-----------| - -### 5.3 Code sample - -```php -$ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); - -$messages = $queue->listMessages(array('ids' => $ids)); - -while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; -} -``` - -## 6. Delete a set of messages by ID - -### 6.1 Description - -This operation immediately deletes the specified messages. If any of the message IDs are malformed or non-existent, -they are ignored. The remaining valid messages IDs are deleted. - -### 6.2 Parameters - -An array of IDs. - -### 6.3 Code sample - -```php -$ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); - -$response = $queue->deleteMessages($ids); -``` - -## 7. Get a specific message - -### 7.1 Description - -This operation gets the specified message from the specified queue. - -### 7.2 Parameters - -Message ID. - -### 7.3 Object properties - -`href` is an opaque relative URI that the client can use to uniquely identify a message resource and interact with it. - -`ttl` is the TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. - -`age` is the number of seconds relative to the server's clock. - -`body` is the arbitrary document that was submitted with the original request to post the message. - -### 7.4 Code sample - -```php -$message = $queue->getMessage('51db6f78c508f17ddc924357'); -``` - -## 8. Delete message - -### 8.1 Description - -This operation immediately deletes the specified message. - -### 8.2 Parameters - -None. - -### 8.3 Code sample - -```php -$message->delete(); -``` +https://doc.php-opencloud.com/en/latest/services/queues/messages.html diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md index 16290f445..01bb95e74 100644 --- a/docs/userguide/Queues/Queue.md +++ b/docs/userguide/Queues/Queue.md @@ -1,156 +1,5 @@ ## 1. Introduction -A Queue is an entity that holds messages. Ideally, a queue is created per work type. For example, if you want to -compress files, you would create a queue dedicated to this job. Any application that reads from this queue would only -compress files. +Our docs have moved! Please visit the below link: -## 2. Setup - -```php -$service = $client->queuesService('cloudQueues', 'ORD'); -``` - -## 3. Client IDs - -With most of Marconi's operation, you must specify a __Client ID__ which will be used as a unique identifier for the -process accessing this Queue. This is basically a UUID that must be unique to each client accessing the API - it can be -an arbitrary string. - -```php -$service->setClientId(); - -echo $service->getClientId(); -``` - -If you call `setClientId` without any parameters, a UUID is automatically generated for you. - -## 4. List queues - -### 4.1 Description - -This operation lists queues for the project. The queues are sorted alphabetically by name. - -### 4.2 Parameters - -|Name|Style|Type|Description| -|----|-----|----|-----------| -|marker|Query|​String|Specifies the name of the last queue received in a previous request, or none to get the first page - of results. Optional.| -|limit|Query|Integer|Specifies the number of queues to return. The default value for the number of queues returned is -10. If you do not specify this parameter, the default number of queues is returned. Optional.| -|detailed|Query|​Boolean|Determines whether queue metadata is included in the response. The default value for this - parameter is false, which excludes the metadata. Optional.| - |----|-----|----|-----------| - -### 4.3 Code sample - -```php -$queues = $service->listQueues(); - -while ($queue = $queues->next()) { - echo $queue->getName() . PHP_EOL; -} -``` - -## 5. Create queue - -### 5.1 Description - -This operation creates a new queue. - -### 5.2 Parameters - -A string representation of the name for your new Queue. The name must not exceed 64 bytes in length, and it is limited -to US-ASCII letters, digits, underscores, and hyphens. - -### 5.3 Code sample - -```php -$queue = $service->createQueue('new_queue'); -``` - -## 6. Retrieve queue - -### 6.1 Description - -Returns a `Queue` object for use. - -### 6.2 Parameters - -Queue name. - -### 6.3 Code sample - -```php -$queue = $service->getQueue('new_queue'); -``` - -## 7. Check queue existence - -### 7.1 Description - -This operation verifies whether the specified queue exists by returning `TRUE` or `FALSE`. - -### 7.2 Parameters - -### 7.3 Code sample - -```php -if ($service->hasQueue('new_queue')) { - // do something -} -``` - -## 8. Update queue metadata (permanently to the API) - -### 4.1 Description - -This operation replaces any existing metadata document in its entirety. Ensure that you do not accidentally overwrite -existing metadata that you want to retain. If you want to _append_ metadata, ensure you merge a new array to the -existing values. - -### 4.2 Parameters - -Hash of key pairs. - -### 4.3 Code sample - -```php -$queue->saveMetadata(array( - 'foo' => 'bar' -)); -``` - -## 9. Retrieve the queue metadata (fresh from the API) - -### 4.1 Description - -This operation returns metadata, such as message TTL, for the queue. - -### 4.2 Parameters - -None. - -### 4.3 Code sample - -```php -$metadata = $queue->retrieveMetadata(); - -print_r($metadata->toArray()); -``` - -## 10. Get queue stats - -### 4.1 Description - -This operation returns queue statistics, including how many messages are in the queue, categorized by status. - -### 4.2 Parameters - -None. - -### 4.3 Code sample - -```php -$queue->getStats(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/queues/queues.html diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 934951e24..0be239d94 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -1,74 +1,5 @@ # Table of contents -## Auto Scale -- [Groups](/docs/userguide/Autoscale/Groups.md) -- [Policies](/docs/userguide/Autoscale/Policies.md) -- [Webhooks](/docs/userguide/Autoscale/Webhooks.md) +Our docs have moved! Please visit the below link: -## Cloud Monitoring -- [Agents](/docs/userguide/CloudMonitoring/Agents.md) -- [Alarms](/docs/userguide/CloudMonitoring/Alarms.md) -- [Changelogs](/docs/userguide/CloudMonitoring/Changelogs.md) -- [Checks](/docs/userguide/CloudMonitoring/Checks.md) -- [Entities](/docs/userguide/CloudMonitoring/Entities.md) -- [Metrics](/docs/userguide/CloudMonitoring/Metrics.md) -- [Notifications](/docs/userguide/CloudMonitoring/Notifications.md) -- [Views](/docs/userguide/CloudMonitoring/Views.md) -- [Zones](/docs/userguide/CloudMonitoring/Zones.md) - -## Compute -- [Servers](/docs/userguide/Compute/Server.md) -- [Images](/docs/userguide/Compute/Images.md) -- [Flavors](/docs/userguide/flavors.md) -- [Keypairs](/docs/userguide/Compute/Keypairs.md) - -## DNS -- [Records](/docs/userguide/DNS/Records.md) -- [Domains](/docs/userguide/DNS/Domains.md) -- [Reverse DNS](/docs/userguide/DNS/Reverse-DNS.md) - -## Databases -- [Getting Started](/docs/userguide/Database/README.md) - -## Identity -- [Users](/docs/userguide/Identity/Users.md) -- [Roles](/docs/userguide/Identity/Roles.md) -- [Tokens](/docs/userguide/Identity/Tokens.md) -- [Tenants](/docs/userguide/Identity/Tenants.md) - -## Images -- [Images](/docs/userguide/Image/Images.md) -- [Sharing](/docs/userguide/Image/Sharing.md) -- [Tagging](/docs/userguide/Image/Tags.md) - -## Load Balancers -- [Getting Started](/docs/userguide/LoadBalancer/README.md) -- [Complete userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) - -## Networking -- [Getting Started](/docs/userguide/Networking/README.md) -- [Complete userguide](/docs/userguide/Networking/USERGUIDE.md) - -## Object Store -- [Objects](/docs/userguide/ObjectStore/Storage/Object.md) -- [Containers](/docs/userguide/ObjectStore/Storage/Container.md) -- [Migrating containers across regions](/docs/userguide/ObjectStore/Storage/Migrating.md) -- [Access control](/docs/userguide/ObjectStore/Access.md) -- [Accounts](/docs/userguide/ObjectStore/Account.md) -- [CDN-enabled containers](/docs/userguide/ObjectStore/CDN/Container.md) -- [Purging objects from the CDN](/docs/userguide/ObjectStore/CDN/Object.md) - -## Orchestration -- [Getting Started](/docs/userguide/Orchestration/README.md) -- [Complete userguide](/docs/userguide/Orchestration/USERGUIDE.md) - -## Queues -- [Queues](/docs/userguide/Queues/Queue.md) -- [Messages](/docs/userguide/Queues/Message.md) -- [Claims](/docs/userguide/Queues/Claim.md) - -## Miscelleneous -- [Client setup](/docs/userguide/Clients.md) -- [Debugging](/docs/userguide/Debugging.md) -- [Iterators](/docs/userguide/Iterators.md) -- [Caching credentials](/docs/userguide/caching-credentials.md) +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/Services.md b/docs/userguide/Services.md index 6f67222a2..9c9946e34 100644 --- a/docs/userguide/Services.md +++ b/docs/userguide/Services.md @@ -1,47 +1,5 @@ # Services -Both OpenStack and Rackspace are comprised of different services: Compute (Nova), -Object Storage (Swift), Queues (Marconi), etc. Each of these services is -encapsulated in its own class which implements the -`OpenCloud\Common\Service\ServiceInterface` interface. +Our docs have moved! Please visit the below link: -Each service class is nested in its own namespace: - -Service|OpenStack codename|FQCN ----|---|--- -Compute|Nova|`OpenCloud\Compute\Service` -Object Storage|Swift|`OpenCloud\ObjectStore\Service` -Identity|Keystone|`OpenCloud\Identity\Service` -Queues|Marconi|`OpenCloud\Queues\Service` - -## Instantiating a service - -The easiest way to instantiate a service, is using the convenient factory -methods of the client class. For more information about clients, see the -[client documentation](Clients.md). - -To create a Compute service, for example: - -```php -$service = $client->computeService('cloudServersOpenStack', 'DFW'); -``` - -### Method arguments - -As you will notice, each factory method accepts three arguments: - -Position|Name|Required?|Type|Description ----|---|---|---|--- -1|Service name|Yes|string|The name of the service as it appears in the [Service Catalog](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Clients.md#service-catalog) -2|Region|Yes|string|The region you want your service to operate in -3|URL type|No|enum|Each service has two different URL types for API transactions: a public URL (i.e. over the Internet) and a private URL (through Rackspace's private network). Private URL's are beneficial because they have lower latency and incur no bandwidth charges; the only condition is that you are communicating to the API from the **same geographic region** (i.e. from a Cloud Serve in the same region).

          The accepted options you may pass in are either `OpenCloud\Common\Constants\Service::INTERNAL_URL` or `OpenCloud\Common\Constants\Service::PUBLIC_URL`. - - -## Differences between OpenStack and Rackspace - -Not all of Rackspace's services are supported by OpenStack; examples include: -Cloud Databases, Auto Scale, Load Balancers, DNS, etc. - -For this reason, if you want to use or interact with these Rackspace-only -services, you must do so by using the methods of the `OpenCloud\Rackspace` -client object and **not** the `OpenCloud\OpenStack` client. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md index 60c89b606..5002ef8de 100644 --- a/docs/userguide/accessip.md +++ b/docs/userguide/accessip.md @@ -1,51 +1,6 @@ About the Access IP Addresses ============================= -OpenStack deployments generally provide new [servers](servers.md) with one -or two network interfaces, each with its own address(es). Usually, one of -these will be a public interface, with its addresses available on the Internet. +Our docs have moved! Please visit the below link: -However, in some cases, the servers are created on an internal network -(this is especially common in a hybrid solution where physical and virtual -servers are intermixed). The servers may be behind a NAT device, firewall, -or other network device that prohibits direct access to the server itself. - -The `OpenCloud\Compute\Resource\Server` object provides two attributes that are -used to instruct applications what IP address to use. These are called the -*access IP* addresses, and they are, in essence, documentation strings used to -direct applications to the correct network address. They can be changed at will -by the server's owner, and OpenStack Nova does not perform any validation on -them: - -* `accessIPv4` holds the IPv4 access address, and -* `accessIPv6` holds the IPv6 access address. - -### Updating the access IP address(es) - -For example, you may have a private cloud with internal addresses in the -10.1.x range. However, you can access a server via a firewall device at -address 50.57.94.244. In this case, you can change the `accessIPv4` attribute -to point to the firewall: - -```php -$server->update(array('accessIPv4' => '50.57.94.244')); -``` - -When a client application retrieves the server's information, it will know -that it needs to use the `accessIPv4` address to connect to the server, and -*not* the IP address assigned to one of the network interfaces. - -### Retrieving the server's IP address - -The `Server::ip()` method is used to retrieve the server's IP address. -It has one optional parameter: the format (either IPv4 or IPv6) of the address -to return (by default, it returns the IPv4 address): - -```php -// IPv4 -echo $server->ip(); -echo $server->ip(4); - -// IPv6 -echo $server->ip(6); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/caching-credentials.md b/docs/userguide/caching-credentials.md index d38d69937..365e6997e 100644 --- a/docs/userguide/caching-credentials.md +++ b/docs/userguide/caching-credentials.md @@ -1,39 +1,5 @@ # Caching credentials -You can speed up your API operations by caching your credentials in a (semi-)permanent location, such as your -DB or local filesystem. This enable subsequent requests to access a shared resource, instead of repetitively having to -re-authenticate on every thread of execution. +Our docs have moved! Please visit the below link: -Tokens are valid for 24 hours, so you can effectively re-use the same cached value for that period. If you try to use -a cached version that has expired, an authentication request will be made. - -## Filesystem example - -In this example, credentials will be saved to a file in the local filesystem. Be sure to exclude it from your VCS. - -```php -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' -)); - -$cacheFile = __DIR__ . '/.opencloud_token'; - -// If the cache file exists, try importing it into the client -if (file_exists($cacheFile)) { - $data = unserialize(file_get_contents($cacheFile)); - $client->importCredentials($data); -} - -$token = $client->getTokenObject(); - -// If no token exists, or the current token is expired, re-authenticate and save the new token to disk -if (!$token || ($token && $token->hasExpired())) { - $client->authenticate(); - file_put_contents($cacheFile, serialize($client->exportCredentials())); -} -``` - -In tests, the above code shaved about 1-2s off the execution time. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/caching-creds.html diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md index 8a8a05176..48d108014 100644 --- a/docs/userguide/dbaas.md +++ b/docs/userguide/dbaas.md @@ -1,319 +1,6 @@ Working with Cloud Databases ============================ -Cloud Databases is a "database-as-a-service" product offered by Rackspace. Since it is -not an official OpenStack project, it is only available via Rackspace connections, -and *not* through an OpenStack connection. +Our docs have moved! Please visit the below link: -## Setup - -```php -$service = $client->databaseService('cloudDatabases', 'ORD'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Instances - -A database instance is an isolated MySQL instance in a single tenant environment -on a shared physical host machine. Also referred to as instance. - -### Create a new Instance - -```php -// Create an empty OpenCloud\Database\Resource\Instance object -$instance = $service->instance(); - -// Send to the API -$instance->create(array( - // Pass in a name for your database instance - 'name' => '', - // Pass in a particular flavor object - 'flavor' => $service->flavor(''), - // Specify a 4GB volume - 'volume' => array('size' => 4) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/create-instance.php) ] - -### Retrieving an instance - -```php -$instance = $service->instance(''); -``` -[ [Get the executable PHP script for this example](/samples/Database/get-instance.php) ] - -### Updating an instance -An instance can be updated to use a specific [configuration](#configurations) as shown below. - -```php -$instance->update(array( - 'configuration' => '' -)); -``` - -**Note:** If any parameters in the associated configuration require a restart, then -you will need to [restart the instance](#restarting-an-instance) after the update. - -### Deleting an instance - -```php -$instance->delete(); -``` - -### Restarting an instance - -```php -$instance->restart(); -``` - -### Resizing an instance - -There are two methods for resizing an instance. The first, `resize()`, changes the amount -of RAM allocated to the instance: - -```php -$flavor = $service->flavor(''); -$instance->resize($flavor); -``` - -You can also independently change the volume size to increase the disk space: - -```php -// Increase to 8GB disk -$instance->resizeVolume(8); -``` - -## Databases - -Instances can have multiple databases; the `OpenCloud\Database\Resource\Database` -class corresponds to a single MySQL database. - -### Creating a new database - -To create a new database, you must supply it with a name; you can optionally -specify its character set and collating sequence: - -```php -// Create a new OpenCloud\Database\Resource\Database object -$database = $instance->database(); - -// Send to API -$database->create(array( - 'name' => 'production', - 'character_set' => 'utf8', - 'collate' => 'utf8_general_ci' -)); -``` - -You can find values for `character_set` and `collate` at -[the MySQL website](http://dev.mysql.com/doc/refman/5.0/en/charset-mysql.html). - -### Deleting a database - -```php -$database->delete(); -``` - -Note that this is destructive; all your data will be wiped away and will not be -retrievable. - -### Listing databases - -```php -$databases = $service->databaseList(); - -foreach ($databases as $database) { - /** @param $database OpenCloud\Database\Resource\Database */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Creating users - -Database users exist at the `Instance` level, but can be associated with a specific -`Database`. They are represented by the `OpenCloud\Database\Resource\User` class. - -Users cannot be altered after they are created, so they must be assigned to -databases when they are created: - -```php -// New instance of OpenCloud\Database\Resource\User -$user = $instance->user(); - -// Send to API -$user->create(array( - 'name' => 'Alice', - 'password' => 'fooBar' - 'databases' => array('production') -)); -``` - -If you need to add a new database to a user after it's been created, you must -delete the user and then re-add it. - -### Deleting users - -```php -$user->delete(); -``` - -## The root user - -By default, Cloud Databases does not enable the root user. In most cases, the root -user is not needed, and having one can leave you open to security violations. However, -if you want to enable access to the root user, the `enableRootUser()` method is -available: - -```php -$rootUser = $instance->enableRootUser(); -``` - -This returns a regular `User` object with the `name` attribute set to `root` and the -`password` attribute set to an auto-generated password. - -To check if the root user is enabled, use the `isRootEnabled()` method. - -## Configurations - -Configurations are groups of settings that can be [applied to instances](#updating-an-instance). - -### Creating a configuration -You can create a new configuration as shown below: - -```php -$configuration = $service->configuration(); -$configuration->create(array( - 'name' => 'example-configuration-name', - 'description' => 'An example configuration', - 'values' => array( - 'collation_server' => 'latin1_swedish_ci', - 'connect_timeout' => 120 - ), - 'datastore' => array( - 'type' => '10000000-0000-0000-0000-000000000001', - 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' - ) -)); -/** @var $configuration OpenCloud\Database\Resource\Configuration **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/create-configuration.php) ] - -### Listing configurations -You can list out all the configurations you have created as shown below: - -```php -$configurations = $service->configurationList(); -foreach ($configurations as $configuration) { - /** @var $configuration OpenCloud\Database\Resource\Configuration **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-configurations.php) ] - -### Retrieving a configuration -You can retrieve a specific configuration, using its ID, as shown below: - -```php -$configuration = $service->configuration(getenv('OS_DB_CONFIGURATION_ID')); -/** @var OpenCloud\Database\Resource\Configuration **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/get-configuration.php) ] - -### Updating a configuration -You have two choices when updating a configuration: -* You could [patch a configuration](#patching-a-configuration) to change only some configuration parameters, _or_ -* You could [entirely replace a configuration](#replacing-a-configuration) to replace all configuration parameters with new ones. - -#### Patching a configuration -You can patch a configuration as shown below: - -```php -$configuration->patch(array( - 'values' => array( - 'connect_timeout' => 30 - ) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/patch-configuration.php) ] - -#### Replacing a configuration -You can replace a configuration as shown below: - -```php -$configuration->update(array( - 'values' => array( - 'collation_server' => 'utf8_general_ci', - 'connect_timeout' => 60 - ) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/replace-configuration.php) ] - -### Deleting a configuration -You can delete a configuration as shown below: - -```php -$configuration->delete(); -``` -[ [Get the executable PHP script for this example](/samples/Database/delete-configuration.php) ] - -**Note:** You cannot delete a configuration if it is in use by a running instance. - -### Listing instances using a configuration -You can list all instances using a specific configuration, using its ID, as shown below: - -```php -$instances = $configuration->instanceList(); -foreach ($instances as $instance) { - /** @var $instance OpenCloud\Database\Resource\Instance **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-configuration-instances.php) ] - -## Datastores - -Datastores are technologies avaialable to persist data. - -### Listing datastores -You can list out all the datastores available as shown below: - -```php -$datastores = $service->datastoreList(); -foreach ($datastores as $datastore) { - /** @var $datastore OpenCloud\Database\Resource\Datastore **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-datastores.php) ] - -### Retrieving a datastore -You can retrieve a specific datastore's information, using its ID, as shown below: - -```php -$datastore = $service->datastore(''); -/** @var OpenCloud\Database\Resource\Datastore **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/get-datastore.php) ] - -### Listing datastore versions -You can list out all the versions available for a specific datastore, as shown below: - -```php -$versions = $datastore->versionList(); -foreach ($versions as $version) { - /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-datastore-versions.php) ] - -### Retrieving a datastore version -You a retrieve a specific datastore version, using its ID, as shown below: - -```php -$datastoreVersion = $datastore->version(''); -/** @var OpenCloud\Database\Resource\DatastoreVersion **/ -[ [Get the executable PHP script for this example](/samples/Database/get-datastore-version.php) ] +https://doc.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md index 41e3fa8d4..dd7806be5 100644 --- a/docs/userguide/flavors.md +++ b/docs/userguide/flavors.md @@ -1,60 +1,6 @@ Working with Flavors ==================== -A *flavor* is a named definition of certain server parameters such as the -amount of RAM and disk space available. (There are other parameters set via -the flavor, such as the amount of disk space and the number of virtual CPUs, -but a discussion of those is too in-depth for a simple Getting Started Guide -like this one.) +Our docs have moved! Please visit the below link: -## Get a flavor - -A `Flavor` object is generated from the `flavor()` method on an -`OpenCloud\Compute\Service` object: - -```php -$flavor = $service->flavor(''); -``` - -## List flavors - -```php -$flavors = $service->flavorList(); - -foreach ($flavors as $flavor) { - /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Details - -By default, the `flavorList()` method returns full details on all flavors. -However, because of the overhead involved in retrieving all the details, -this function can be slower than expected. You can supply an optional -boolean parameter to the `flavorList()` method to determine whether or not -the flavor details are included: - -```php -// Name and ID only -$compute->flavorList(false); - -// All details -$compute->flavorList(true); -``` - -### Filters - -The optional second parameter to the `flavorList()` method is an -associative array of filter parameters. See the -[List Flavors operation](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Flavors-d1e4188.html) - for a list of the available parameters. - -For example, you may be only interested in flavors that have at least 4GB of -memory: - -```php -$fourGbFlavors = $compute->FlavorList(true, array('minRam' => 4096)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/flavors.html diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md index f24752fc4..d42b73964 100644 --- a/docs/userguide/networks.md +++ b/docs/userguide/networks.md @@ -1,103 +1,6 @@ Working with Cloud Networks =========================== -Rackspace Cloud Networks is a virtual "isolated network" product -based upon (though not strictly identical to) the [OpenStack -Quantum](http://quantum.openstack.org) project. With Cloud Networks, -you can create multiple isolated networks and associate them with -new Cloud Servers. (You cannot add an isolated network to an -existing Cloud Server at this time, although that feature may be -available in the future.) +Our docs have moved! Please visit the below link: -Since the network is a feature of the Cloud Servers product, you -use the `Compute::network()` method to create a new network, and -the `Compute::networkList()` method to retrieve information about -existing networks. - -### Pseudo-networks - -Rackspace has two *pseudo-networks* called `public` and `private`. -The `public` network represents the Internet, while the `private` -network represents the Rackspace internal ServiceNet (an infrastructure -network used to facilitate communication within a Rackspace data -center). These are called "pseudo-networks" because they are not -actually represented in the Quantum component, but have a special -representation that you must use if you want to associate your -server with them. - -The `public` network is represented by the special UUID -`00000000-0000-0000-0000-000000000000` and the `private` network -by `11111111-1111-1111-1111-111111111111`. php-opencloud -provides the special constants `RAX_PUBLIC` and `RAX_PRIVATE` to -make using these networks easier (see [Create a server with an -isolated network](#server) below). - - -### Create a network - -A Cloud Network is identified by a *label* and must be defined with -a network *CIDR* address range. For example, we can create a network -used by our backend servers on the 192.168.0.0 network like this: - -```php -// Create instance of OpenCloud\Compute\Resource\Network -$network = $compute->network(); - -// Send to API -$network->create(array( - 'label' => 'Backend Network', - 'cidr' => '192.168.0.0/16' -)); -``` - -### Delete a network - -```php -$network->delete(); -``` - -Note that you cannot delete a network if there are still servers attached to it. - -### Listing networks - -``` -$networks = $service->networkList(); - -foreach ($networks as $network) { - /** @param $network OpenCloud\Compute\Resource\Network */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -## Create a server with an isolated network - -When you create a new server, you can specify the networks to which -it is attached via the `networks` attribute in the `Server::create()` -method: - -use OpenCloud\Compute\Constants\Network; - -// Create instance of OpenCloud\Compute\Resource\Server -$server = $compute->server(); - -// Send to API -$server->create(array( - 'name' => 'My Server', - 'flavor' => $compute->flavor(''), - 'image' => $compute->image(''), - 'networks' => array( - $network, - $compute->network(Network::RAX_PUBLIC) - ) -)); -``` - -In this example, the server `$server` is attached to the network that we -created in the previous example. It is also attached to the Rackspace `public` -network (the Internet). However, it is *not* attached to the Rackspace `private` -network (ServiceNet). - -Note that the `networks` attribute is an array of `OpenCloud\Compute\Resource\Network` -objects. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md index 8f58adea0..127a5b6c5 100644 --- a/docs/userguide/servers.md +++ b/docs/userguide/servers.md @@ -1,188 +1,6 @@ Working with Servers ==================== -A server is a virtual machine instance that is managed by OpenStack Nova. It is -represented by the `OpenCloud\Compute\Resource\Server` class. - -## Setup - -In order to use a `Server` object, you must create the Compute service first: - -```php -$service = $client->computeService('cloudServersOpenStack', 'ORD'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Get an existing server - -To retrieve an existing server, all you need is its unique ID: - -```php -/** @param $server OpenCloud\Compute\Resource\Server */ -$server = $server->server(''); -``` - -## Create a new server - -A server requires both a [Flavor object](flavors.md) and an -[Image object](images.md) to be created. In addition, a server requires a name: - -```php -// New instance of OpenCloud\Compute\Resource\Server -$server = $service->server(); - -/** @param $server OpenCloud\Image\Resource\ImageInterface */ -$image = $service->image(''); - -/** @param $server OpenCloud\Compute\Resource\Flavor */ -$flavor = $service->flavor(''); - -// Send to API -$server->create(array( - 'name' => 'New server', - 'flavor' => $flavor, - 'image' => $image -)); -``` - -Server builds typically take under 5 minutes to complete (depending upon the size -of the server). However, the initial response will return the server's ID as -well as the assigned root password: - -```php -echo $server->adminPass; -``` - -(Note: it is not recommended that you print out the root password because of -security risks. This is only provided as an example.) - -When you create a new server on the Rackspace public cloud, you can also -associate it with one or more isolated networks. For more information, see -[Working with Cloud Networks](networks.md). - -### Rebuilding an existing server - -"Rebuilding" a server is nearly identical to creating one; you must supply -an Image object. You can also change the server's name as part of the rebuild. -The primary difference between a create and a rebuild is that, in the rebuild, -the server's IP address(es) are retained (when the server is created, new IP -addresses are assigned). - -To rebuild a server: - -```php -$server->rebuild(array( - 'adminPass' => 'rootPassword', - 'name' => 'A Bigger Server', - 'image' => $compute->image('') -)); -``` - -### Updating a server - -The `update()` method is very similar to `create()` except that the only -attributes of a server that you are permitted to update are its name and -the [access IP addresses](accessip.md). - -```php -$server->update(array('accessIPv4' => '50.57.94.244')); -``` - -### Deleting a server - -```php -$server->delete(); -``` - -## Server actions - -You can perform various actions on a server, such as rebooting it, resizing -it, or changing the root password. - -### Setting the root password - -Use the `setPassword()` method to change the root user's password: - -```php -$server->setPassword('new password'); -``` - -Note that it may take a few second for the new password to take effect. Also, -password restrictions (such as the minimum number of characters, numbers of -punctuation characters, and so forth) are enforced by the operating system and are -not always detectable by the Compute service. This means that, even though -the `setPassword()` method succeeds, the password may not be changed, and -there may not be any feedback to that effect. - -### To reboot the server - -You can perform either a *hard* reboot (this is like pulling the power cord -and then restarting) or a *soft* reboot (initiated by the operating system -and generally less disruptive than a hard reboot). A hard reboot is -performed by default: - -```php -$server->reboot(); // hard reboot -$server->reboot(ServerState::REBOOT_STATE_HARD); // also a hard reboot -$server->reboot(ServerState::REBOOT_STATE_SOFT); // a soft reboot -``` - -If the server is "hung," or unresponsive, a hard reboot may sometimes be -the only way to access the server. - -### To resize the server - -A server can be resized by providing a new [Flavor object](flavors.md): - -```php -$server->resize($compute->flavor(5)); -``` - -Once the resize completes (check the `$server->status`), you can either -confirm it: - -```php -$server->resizeConfirm(); -``` - -or revert it back to the original size: - -```php -$server->resizeRevert(); -``` - -### To rescue/unrescue a server - -In rescue mode, a server is rebuilt to a pristine state and the existing -filesystem is mounted so that you can edit files and diagnose issues. -See [this document](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/rescue_mode.html) -for more details. - -Put server into rescue mode: - -```php -$password = $server->rescue(); -``` - -The `$password` is the assigned root password of the rescue server. - -Take server out of rescue mode: - -```php -$server->unrescue(); -``` - -This restores the server to its original state (plus any changes you may have -made while it was in rescue mode). - -## What next? - -* See also [Working with Networks](networks.md). -* To learn about dynamic - volume creation and assignment, see - [Working with Volumes](volumes.md). +Our docs have moved! Please visit the below link: +https://doc.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md index bd2f13a9d..8c891d4f2 100644 --- a/docs/userguide/volumes.md +++ b/docs/userguide/volumes.md @@ -1,173 +1,6 @@ Working with Volumes ---------------------------------------------- -Cloud Block Storage (CBS) is a dynamic volume creation and management service -built upon the OpenStack Cinder project. See http://cinder.openstack.org for -complete details about the available features and functionality. -## Setup +Our docs have moved! Please visit the below link: -```php -$service = $client->volumeService('cloudBlockStorage', 'DFW'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Volume Types - -Providers may support multiple types of volumes; at Rackspace, a volume can -either be `SSD` (solid state disk: expensive, high-performance) or -`SATA` (serial attached storage: regular disks, less expensive). - -### List volume types - -```php -$volumeTypes = $service->volumeTypeList(); - -foreach ($volumeTypes as $volumeType) { - /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Describe a volume type - -If you know the ID of a volume type, use the `volumeType` method to retrieve -information on it: - -```php -$volumeType = $service->volumeType(1); -``` - -A volume type has three attributes: - -* `$id` the volume type identifier -* `$name` its name -* `$extra_specs` additional information for the provider - -## Volumes - -A volume is a detachable block storage device. You can think of it as a USB -hard drive. It can only be attached to one instance at a time. - -A volume is represented by the `OpenCloud\Volume\Resource\Volume` class. - -### To create a volume - -To create a volume, you must specify its size (in gigabytes). All other -parameters are optional: - -```php -// Create instance of OpenCloud\Volume\Resource\Volume -$volume = $service->volume(); - -$volume->create(array( - 'size' => 200, - 'volume_type' => $service->volumeType(''), - 'display_name' => 'My Volume', - 'display_description' => 'Used for large object storage' -)); -``` - -### List volumes - -```php -$volumes = $service->volumeList(); - -foreach ($volumes as $volume) { - /** @param $volumeType OpenCloud\Volume\Resource\Volume */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Get details on a single volume - -If you specify an ID on the `volume()` method, it retrieves information on -the specified volume: - -```php -$volume = $dallas->volume(''); -echo $volume->size; -``` - -### To delete a volume - -```php -$volume->delete(); -``` - -## Snapshots - -A snapshot is a point-in-time copy of the data contained in a volume. It is -represented by the `OpenCloud\Volume\Resource\Snapshot` class. - -### Create a snapshot - -A `Snapshot` object is created from the Cloud Block Storage service. However, -it is associated with a volume, and you must specify a volume to create one: - -```php -// New instance of OpenCloud\Volume\Resource\Snapshot -$snapshot = $service->snapshot(); - -// Send to API -$snapshot->create(array( - 'display_name' => 'Name that snapshot', - 'volume_id' => $volume->id -)); -``` - -### List snapshots - -```php -$snapshots = $service->snapshotList(); - -foreach ($snapshots as $snapshot) { - /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### To get details on a single snapshot - -```php -$snapshot = $dallas->snapshot(''); -``` - -#### To delete a snapshot - -```php -$snapshot->delete(); -``` - -### Volumes and Servers - -A volume by itself is useful when it is attached to a server so that the -server can use the volume. - -### To attach a volume to a server - -```php -$server->attachVolume($volume, '') -``` - -The `` is the location on the server on which to -mount the volume (usually `/dev/xvhdd` or similar). You can also supply -`'auto'` as the mount point, in which case the mount point will be -automatically selected for you. `auto` is the default value for -`{mount-point}`, so you do not actually need to supply anything for that -parameter. - -### To detach a volume from a server - -```php -$server->detachVolume($volume); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/volume/index.html From b003bfd3a9bf58772229380432994a34b1d1d8d3 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 11 Mar 2015 19:35:26 +0100 Subject: [PATCH 618/835] Add more information about configuring clients --- doc/auth.rst | 54 ++++++++++++++++++++++++++++++++++++++++++ doc/http-clients.rst | 56 ++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 3 +++ doc/logging.rst | 33 ++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 doc/auth.rst create mode 100644 doc/http-clients.rst create mode 100644 doc/logging.rst diff --git a/doc/auth.rst b/doc/auth.rst new file mode 100644 index 000000000..ce5ba7233 --- /dev/null +++ b/doc/auth.rst @@ -0,0 +1,54 @@ +Authentication +============== + +The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary. + +You can force authentication, by calling: + +.. code-block:: php + + $client->authenticate(); + +If the credentials are incorrect, a ``401`` error will be returned. If +credentials are correct, a ``200`` status is returned with your Service +Catalog. + + +Service Catalog +--------------- + +The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the ``Catalog`` +object, which allows you greater control and interactivity. + +.. code-block:: php + + /** @var OpenCloud\Common\Service\Catalog */ + $catalog = $client->getCatalog(); + + // Return a list of OpenCloud\Common\Service\CatalogItem objects + foreach ($catalog->getItems() as $catalogItem) { + + $name = $catalogItem->getName(); + $type = $catalogItem->getType(); + + if ($name == 'cloudServersOpenStack' && $type == 'compute') { + break; + } + + // Array of OpenCloud\Common\Service\Endpoint objects + $endpoints = $catalogItem->getEndpoints(); + foreach ($endpoints as $endpoint) { + if ($endpoint->getRegion() == 'DFW') { + echo $endpoint->getPublicUrl(); + } + } + } + +As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs. diff --git a/doc/http-clients.rst b/doc/http-clients.rst new file mode 100644 index 000000000..49828509b --- /dev/null +++ b/doc/http-clients.rst @@ -0,0 +1,56 @@ +HTTP Clients +============ + +Default HTTP headers +-------------------- + +To set default HTTP headers: + +.. code-block:: php + + $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); + + +User agents +----------- + +php-opencloud will send a default ``User-Agent`` header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format: + + User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz + +where ``xxx`` is the current version of the SDK, ``yyy`` is the current +version of cURL, and ``zzz`` is the current PHP version. To override +this default, you must run: + +.. code-block:: php + + $client->setUserAgent('MyCustomUserAgent'); + +which will result in: + + User-Agent: MyCustomUserAgent + +If you want to set a *prefix* for the user agent, but retain the default +``User-Agent`` as a suffix, you must run: + +.. code-block:: php + + $client->setUserAgent('MyPrefix', true); + +which will result in: + + User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + +where ``$client`` is an instance of ``OpenCloud\OpenStack`` or +``OpenCloud\Rackspace``. + + +Other functionality +------------------- + +For a full list of functionality provided by Guzzle, please consult the +`official documentation`_. + +.. _official documentation: http://docs.guzzlephp.org/en/latest/http-client/client.html diff --git a/doc/index.rst b/doc/index.rst index fc7c280c3..4994f0efb 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -50,6 +50,9 @@ Usage tips iterators regions url-types + logging + http-clients + auth Help and support ---------------- diff --git a/doc/logging.rst b/doc/logging.rst new file mode 100644 index 000000000..bb2a3de09 --- /dev/null +++ b/doc/logging.rst @@ -0,0 +1,33 @@ +Logging +======= + +Logger injection +---------------- + +As the ``Rackspace`` client extends the ``OpenStack`` client, they both support +passing ``$options`` as an array via the constructor's third parameter. The +options are passed as a config to the `Guzzle` client, but also allow to inject +your own logger. + +Your logger should implement the ``Psr\Log\LoggerInterface`` `as defined in +PSR-3 `_. +One example of a compatible logger is `Monolog `_. +When the client does create a service, it will inject the logger if one is +available. + +To inject a ``LoggerInterface`` compatible logger into a new client: + +.. code-block:: php + + use Monolog\Logger; + use OpenCloud\OpenStack; + + // create a log channel + $logger = new Logger('name'); + + $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' + ), array( + 'logger' => $logger, + )); From 1dca415bb9281df25d637cd85b98e63279c89b8b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 16 Mar 2015 14:22:04 +0100 Subject: [PATCH 619/835] Use correct CNAME record --- docs/getting-started-openstack.md | 2 +- docs/getting-started.md | 2 +- docs/userguide/Autoscale/Config.md | 2 +- docs/userguide/Autoscale/Groups.md | 2 +- docs/userguide/Autoscale/Policies.md | 2 +- docs/userguide/Autoscale/Webhooks.md | 2 +- docs/userguide/Clients.md | 2 +- docs/userguide/CloudMonitoring/Agents.md | 2 +- docs/userguide/CloudMonitoring/Alarms.md | 2 +- docs/userguide/CloudMonitoring/Changelogs.md | 2 +- docs/userguide/CloudMonitoring/Checks.md | 2 +- docs/userguide/CloudMonitoring/Entities.md | 2 +- docs/userguide/CloudMonitoring/Metrics.md | 2 +- docs/userguide/CloudMonitoring/Notifications.md | 2 +- docs/userguide/CloudMonitoring/Service.md | 2 +- docs/userguide/CloudMonitoring/Views.md | 2 +- docs/userguide/CloudMonitoring/Zones.md | 2 +- docs/userguide/Compute/Images.md | 2 +- docs/userguide/Compute/Keypair.md | 2 +- docs/userguide/Compute/Server.md | 2 +- docs/userguide/Compute/Service.md | 2 +- docs/userguide/DNS/Domains.md | 2 +- docs/userguide/DNS/Limits.md | 2 +- docs/userguide/DNS/Records.md | 2 +- docs/userguide/DNS/Reverse-DNS.md | 2 +- docs/userguide/DNS/Service.md | 2 +- docs/userguide/Database/README.md | 2 +- docs/userguide/Debugging.md | 2 +- docs/userguide/Identity/Roles.md | 2 +- docs/userguide/Identity/Service.md | 2 +- docs/userguide/Identity/Tenants.md | 2 +- docs/userguide/Identity/Tokens.md | 2 +- docs/userguide/Identity/Users.md | 2 +- docs/userguide/Image/Images.md | 2 +- docs/userguide/Image/Schemas.md | 2 +- docs/userguide/Image/Sharing.md | 2 +- docs/userguide/Image/Tags.md | 2 +- docs/userguide/Iterators.md | 2 +- docs/userguide/LoadBalancer/README.md | 2 +- docs/userguide/LoadBalancer/USERGUIDE.md | 2 +- docs/userguide/Networking/README.md | 2 +- docs/userguide/Networking/USERGUIDE.md | 2 +- docs/userguide/ObjectStore/Access.md | 2 +- docs/userguide/ObjectStore/Account.md | 2 +- docs/userguide/ObjectStore/CDN/Container.md | 2 +- docs/userguide/ObjectStore/CDN/Object.md | 2 +- docs/userguide/ObjectStore/README.md | 2 +- docs/userguide/ObjectStore/Storage/Container.md | 2 +- docs/userguide/ObjectStore/Storage/Migrating.md | 2 +- docs/userguide/ObjectStore/Storage/Object.md | 2 +- docs/userguide/ObjectStore/USERGUIDE.md | 2 +- docs/userguide/Orchestration/README.md | 2 +- docs/userguide/Orchestration/USERGUIDE.md | 2 +- docs/userguide/Queues/Claim.md | 2 +- docs/userguide/Queues/Message.md | 2 +- docs/userguide/Queues/Queue.md | 2 +- docs/userguide/README.md | 2 +- docs/userguide/Services.md | 2 +- docs/userguide/accessip.md | 2 +- docs/userguide/caching-credentials.md | 2 +- docs/userguide/dbaas.md | 2 +- docs/userguide/flavors.md | 2 +- docs/userguide/networks.md | 2 +- docs/userguide/servers.md | 2 +- docs/userguide/volumes.md | 2 +- 65 files changed, 65 insertions(+), 65 deletions(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index 24eda2779..1133b948d 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/getting-started-with-openstack.html +http://docs.php-opencloud.com/en/latest/getting-started-with-openstack.html diff --git a/docs/getting-started.md b/docs/getting-started.md index af65271fe..c7d731a4c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/getting-started-with-rackspace.html +http://docs.php-opencloud.com/en/latest/getting-started-with-rackspace.html diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md index 02f2e891d..328fdcc93 100644 --- a/docs/userguide/Autoscale/Config.md +++ b/docs/userguide/Autoscale/Config.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/group-config.html +http://docs.php-opencloud.com/en/latest/services/autoscale/group-config.html diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md index 7ec7aa269..e0d8bfcdb 100644 --- a/docs/userguide/Autoscale/Groups.md +++ b/docs/userguide/Autoscale/Groups.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/groups.html +http://docs.php-opencloud.com/en/latest/services/autoscale/groups.html diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md index 7a312af50..1f890d7ad 100644 --- a/docs/userguide/Autoscale/Policies.md +++ b/docs/userguide/Autoscale/Policies.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/policies.html +http://docs.php-opencloud.com/en/latest/services/autoscale/policies.html diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md index c5e6453ed..783075508 100644 --- a/docs/userguide/Autoscale/Webhooks.md +++ b/docs/userguide/Autoscale/Webhooks.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/webhooks.html +http://docs.php-opencloud.com/en/latest/services/autoscale/webhooks.html diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 184b601f4..762a7ed70 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md index b316d004c..f1b8cab5d 100644 --- a/docs/userguide/CloudMonitoring/Agents.md +++ b/docs/userguide/CloudMonitoring/Agents.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/agents.html +http://docs.php-opencloud.com/en/latest/services/monitoring/agents.html diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md index b08caf4cd..b64c1e948 100644 --- a/docs/userguide/CloudMonitoring/Alarms.md +++ b/docs/userguide/CloudMonitoring/Alarms.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/alarms.html +http://docs.php-opencloud.com/en/latest/services/monitoring/alarms.html diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md index ff943e592..a1d137c9f 100644 --- a/docs/userguide/CloudMonitoring/Changelogs.md +++ b/docs/userguide/CloudMonitoring/Changelogs.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/changelogs.html +http://docs.php-opencloud.com/en/latest/services/monitoring/changelogs.html diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md index 5710aa465..37aefb644 100644 --- a/docs/userguide/CloudMonitoring/Checks.md +++ b/docs/userguide/CloudMonitoring/Checks.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/checks.html +http://docs.php-opencloud.com/en/latest/services/monitoring/checks.html diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md index beed807a2..0f09a74a2 100644 --- a/docs/userguide/CloudMonitoring/Entities.md +++ b/docs/userguide/CloudMonitoring/Entities.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/entities.html +http://docs.php-opencloud.com/en/latest/services/monitoring/entities.html diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md index 0b275c97e..86450bee9 100644 --- a/docs/userguide/CloudMonitoring/Metrics.md +++ b/docs/userguide/CloudMonitoring/Metrics.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/metrics.html +http://docs.php-opencloud.com/en/latest/services/monitoring/metrics.html diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md index 129881fae..30a1cf97c 100644 --- a/docs/userguide/CloudMonitoring/Notifications.md +++ b/docs/userguide/CloudMonitoring/Notifications.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/notifications.html +http://docs.php-opencloud.com/en/latest/services/monitoring/notifications.html diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md index 6445de039..be9f9435f 100644 --- a/docs/userguide/CloudMonitoring/Service.md +++ b/docs/userguide/CloudMonitoring/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/index.html +http://docs.php-opencloud.com/en/latest/services/monitoring/index.html diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md index ac0017db0..7672c2ea8 100644 --- a/docs/userguide/CloudMonitoring/Views.md +++ b/docs/userguide/CloudMonitoring/Views.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/views.html +http://docs.php-opencloud.com/en/latest/services/monitoring/views.html diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md index fcad4b900..8257efa29 100644 --- a/docs/userguide/CloudMonitoring/Zones.md +++ b/docs/userguide/CloudMonitoring/Zones.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/zones.html +http://docs.php-opencloud.com/en/latest/services/monitoring/zones.html diff --git a/docs/userguide/Compute/Images.md b/docs/userguide/Compute/Images.md index 6cc06d37f..b7a99025d 100644 --- a/docs/userguide/Compute/Images.md +++ b/docs/userguide/Compute/Images.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/images.html +http://docs.php-opencloud.com/en/latest/services/compute/images.html diff --git a/docs/userguide/Compute/Keypair.md b/docs/userguide/Compute/Keypair.md index 37e5080fe..a3e0b4870 100644 --- a/docs/userguide/Compute/Keypair.md +++ b/docs/userguide/Compute/Keypair.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/keypairs.html +http://docs.php-opencloud.com/en/latest/services/compute/keypairs.html diff --git a/docs/userguide/Compute/Server.md b/docs/userguide/Compute/Server.md index e43e1cc42..dedc1da77 100644 --- a/docs/userguide/Compute/Server.md +++ b/docs/userguide/Compute/Server.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/servers.html +http://docs.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/Compute/Service.md b/docs/userguide/Compute/Service.md index ba49ff4e4..9bb8e996a 100644 --- a/docs/userguide/Compute/Service.md +++ b/docs/userguide/Compute/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/index.html +http://docs.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index d8d3a12d3..53aa45c3b 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/domains.html +http://docs.php-opencloud.com/en/latest/services/dns/domains.html diff --git a/docs/userguide/DNS/Limits.md b/docs/userguide/DNS/Limits.md index 70fac6504..68e928594 100644 --- a/docs/userguide/DNS/Limits.md +++ b/docs/userguide/DNS/Limits.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/limits.html +http://docs.php-opencloud.com/en/latest/services/dns/limits.html diff --git a/docs/userguide/DNS/Records.md b/docs/userguide/DNS/Records.md index 52998b955..fb428c60e 100644 --- a/docs/userguide/DNS/Records.md +++ b/docs/userguide/DNS/Records.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/records.html +http://docs.php-opencloud.com/en/latest/services/dns/records.html diff --git a/docs/userguide/DNS/Reverse-DNS.md b/docs/userguide/DNS/Reverse-DNS.md index e930431c1..84a3a2d15 100644 --- a/docs/userguide/DNS/Reverse-DNS.md +++ b/docs/userguide/DNS/Reverse-DNS.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/reverse-dns.html +http://docs.php-opencloud.com/en/latest/services/dns/reverse-dns.html diff --git a/docs/userguide/DNS/Service.md b/docs/userguide/DNS/Service.md index 3404ed7c7..993648cd9 100644 --- a/docs/userguide/DNS/Service.md +++ b/docs/userguide/DNS/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/index.html +http://docs.php-opencloud.com/en/latest/services/dns/index.html diff --git a/docs/userguide/Database/README.md b/docs/userguide/Database/README.md index 199d049e4..2fc712efd 100644 --- a/docs/userguide/Database/README.md +++ b/docs/userguide/Database/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/database/index.html +http://docs.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/Debugging.md b/docs/userguide/Debugging.md index 0c6afa4d6..8922df10d 100644 --- a/docs/userguide/Debugging.md +++ b/docs/userguide/Debugging.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/debugging.html +http://docs.php-opencloud.com/en/latest/debugging.html diff --git a/docs/userguide/Identity/Roles.md b/docs/userguide/Identity/Roles.md index 333d53c81..5d608bf51 100644 --- a/docs/userguide/Identity/Roles.md +++ b/docs/userguide/Identity/Roles.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/roles.html +http://docs.php-opencloud.com/en/latest/services/identity/roles.html diff --git a/docs/userguide/Identity/Service.md b/docs/userguide/Identity/Service.md index 579cf2a9f..4195d4bcb 100644 --- a/docs/userguide/Identity/Service.md +++ b/docs/userguide/Identity/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/index.html +http://docs.php-opencloud.com/en/latest/services/identity/index.html diff --git a/docs/userguide/Identity/Tenants.md b/docs/userguide/Identity/Tenants.md index ae9bcdcb0..2129f9604 100644 --- a/docs/userguide/Identity/Tenants.md +++ b/docs/userguide/Identity/Tenants.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/tenants.html +http://docs.php-opencloud.com/en/latest/services/identity/tenants.html diff --git a/docs/userguide/Identity/Tokens.md b/docs/userguide/Identity/Tokens.md index a60c57ab5..f59835ef4 100644 --- a/docs/userguide/Identity/Tokens.md +++ b/docs/userguide/Identity/Tokens.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/tokens.html +http://docs.php-opencloud.com/en/latest/services/identity/tokens.html diff --git a/docs/userguide/Identity/Users.md b/docs/userguide/Identity/Users.md index e387cbee5..a69fbb7dc 100644 --- a/docs/userguide/Identity/Users.md +++ b/docs/userguide/Identity/Users.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/users.html +http://docs.php-opencloud.com/en/latest/services/identity/users.html diff --git a/docs/userguide/Image/Images.md b/docs/userguide/Image/Images.md index 1f593f17a..95824b2a5 100644 --- a/docs/userguide/Image/Images.md +++ b/docs/userguide/Image/Images.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/images.html +http://docs.php-opencloud.com/en/latest/services/image/images.html diff --git a/docs/userguide/Image/Schemas.md b/docs/userguide/Image/Schemas.md index b37222b84..5efce8c59 100644 --- a/docs/userguide/Image/Schemas.md +++ b/docs/userguide/Image/Schemas.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/schemas.html +http://docs.php-opencloud.com/en/latest/services/image/schemas.html diff --git a/docs/userguide/Image/Sharing.md b/docs/userguide/Image/Sharing.md index 91515b9fa..3fbe0c1d1 100644 --- a/docs/userguide/Image/Sharing.md +++ b/docs/userguide/Image/Sharing.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/sharing.html +http://docs.php-opencloud.com/en/latest/services/image/sharing.html diff --git a/docs/userguide/Image/Tags.md b/docs/userguide/Image/Tags.md index fc8bcd8aa..2686391f4 100644 --- a/docs/userguide/Image/Tags.md +++ b/docs/userguide/Image/Tags.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/tags.html +http://docs.php-opencloud.com/en/latest/services/image/tags.html diff --git a/docs/userguide/Iterators.md b/docs/userguide/Iterators.md index 114982668..4c8c794ce 100644 --- a/docs/userguide/Iterators.md +++ b/docs/userguide/Iterators.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/iterators.html +http://docs.php-opencloud.com/en/latest/iterators.html diff --git a/docs/userguide/LoadBalancer/README.md b/docs/userguide/LoadBalancer/README.md index 34008ab6c..3531e3dc8 100644 --- a/docs/userguide/LoadBalancer/README.md +++ b/docs/userguide/LoadBalancer/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html +http://docs.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 0a5c3b15d..d29925da0 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html +http://docs.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md index 1778646ad..9597c33a1 100644 --- a/docs/userguide/Networking/README.md +++ b/docs/userguide/Networking/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 376daa093..d3185c6f2 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md index 8e224392a..53466362e 100644 --- a/docs/userguide/ObjectStore/Access.md +++ b/docs/userguide/ObjectStore/Access.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/access.html +http://docs.php-opencloud.com/en/latest/services/object-store/access.html diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md index fc60751a8..286bb9ca1 100644 --- a/docs/userguide/ObjectStore/Account.md +++ b/docs/userguide/ObjectStore/Account.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/account.html +http://docs.php-opencloud.com/en/latest/services/object-store/account.html diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 9f3b4a5ca..c854b31e5 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html +http://docs.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md index 9f3b4a5ca..c854b31e5 100644 --- a/docs/userguide/ObjectStore/CDN/Object.md +++ b/docs/userguide/ObjectStore/CDN/Object.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html +http://docs.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index ea43fcdf7..c66bbabdb 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/index.html +http://docs.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index e9fd5af14..ab705fa15 100644 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/containers.html +http://docs.php-opencloud.com/en/latest/services/object-store/containers.html diff --git a/docs/userguide/ObjectStore/Storage/Migrating.md b/docs/userguide/ObjectStore/Storage/Migrating.md index 8d12c382a..550268f60 100644 --- a/docs/userguide/ObjectStore/Storage/Migrating.md +++ b/docs/userguide/ObjectStore/Storage/Migrating.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/migrating-containers.html +http://docs.php-opencloud.com/en/latest/services/object-store/migrating-containers.html diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 0c58ca9fb..2ecca81e1 100644 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/objects.html +http://docs.php-opencloud.com/en/latest/services/object-store/objects.html diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 6881ee3e6..3773b0078 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/index.html +http://docs.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 862a61ed7..3647da0c9 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/orchestration/index.html +http://docs.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 902d7741b..4a5b9a135 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/orchestration/index.html +http://docs.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index 0e338956c..58b57d22f 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/claims.html +http://docs.php-opencloud.com/en/latest/services/queues/claims.html diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md index ce0f54b07..767bb0b1d 100644 --- a/docs/userguide/Queues/Message.md +++ b/docs/userguide/Queues/Message.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/messages.html +http://docs.php-opencloud.com/en/latest/services/queues/messages.html diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md index 01bb95e74..2299a72e2 100644 --- a/docs/userguide/Queues/Queue.md +++ b/docs/userguide/Queues/Queue.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/queues.html +http://docs.php-opencloud.com/en/latest/services/queues/queues.html diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 0be239d94..b49b4e67c 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/Services.md b/docs/userguide/Services.md index 9c9946e34..6973130eb 100644 --- a/docs/userguide/Services.md +++ b/docs/userguide/Services.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md index 5002ef8de..9d343de11 100644 --- a/docs/userguide/accessip.md +++ b/docs/userguide/accessip.md @@ -3,4 +3,4 @@ About the Access IP Addresses Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/servers.html +http://docs.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/caching-credentials.md b/docs/userguide/caching-credentials.md index 365e6997e..949dcad53 100644 --- a/docs/userguide/caching-credentials.md +++ b/docs/userguide/caching-credentials.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/caching-creds.html +http://docs.php-opencloud.com/en/latest/caching-creds.html diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md index 48d108014..fe0327861 100644 --- a/docs/userguide/dbaas.md +++ b/docs/userguide/dbaas.md @@ -3,4 +3,4 @@ Working with Cloud Databases Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/database/index.html +http://docs.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md index dd7806be5..4bf978b52 100644 --- a/docs/userguide/flavors.md +++ b/docs/userguide/flavors.md @@ -3,4 +3,4 @@ Working with Flavors Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/flavors.html +http://docs.php-opencloud.com/en/latest/services/compute/flavors.html diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md index d42b73964..f6a153508 100644 --- a/docs/userguide/networks.md +++ b/docs/userguide/networks.md @@ -3,4 +3,4 @@ Working with Cloud Networks Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md index 127a5b6c5..1ff28fd30 100644 --- a/docs/userguide/servers.md +++ b/docs/userguide/servers.md @@ -3,4 +3,4 @@ Working with Servers Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/index.html +http://docs.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md index 8c891d4f2..bc161e5b3 100644 --- a/docs/userguide/volumes.md +++ b/docs/userguide/volumes.md @@ -3,4 +3,4 @@ Working with Volumes Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/volume/index.html +http://docs.php-opencloud.com/en/latest/services/volume/index.html From 1ca8d6c12d7d2c55d45b0efe5d62e59833e9d376 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:21:50 -0700 Subject: [PATCH 620/835] Using TLSv1 cipher suite for Cloud Databases client. --- lib/OpenCloud/Database/Service.php | 22 +++++++++++++++++++ .../OpenCloud/Tests/Database/ServiceTest.php | 7 ++++++ 2 files changed, 29 insertions(+) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index dd63a60f1..50845ba24 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -17,6 +17,8 @@ namespace OpenCloud\Database; +use Guzzle\Http\ClientInterface; +use Psr\Log\LogLevel; use OpenCloud\Common\Service\NovaService; use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; @@ -104,4 +106,24 @@ public function datastoreList($params = array()) return $this->resourceList('Datastore', $url); } + + /** + * {@inheritDoc} + */ + public function setClient(ClientInterface $client) + { + // The Rackspace Cloud Databases service only supports the + // RC4 SSL cipher which is not supported by modern OpenSSL clients. + // Until the service can support additional, more modern and secure + // ciphers, this SDK has to ask curl to allow using the weaker + // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 + + $curlOptions = $client->getConfig()->get('curl.options'); + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; + $client->getConfig()->set('curl.options', $curlOptions); + + $client->getLogger()->critical('The SDK is using the TLSv1 cipher suite when connecting to the Rackspace Cloud Databases service. This suite contains a weak cipher (RC4) so please use at your own risk. See https://github.com/rackspace/php-opencloud/issues/560 for details.'); + + $this->client = $client; + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 8212fb61a..4c690ae81 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -71,4 +71,11 @@ public function testDatastoreList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } + + public function testClientUsesTLSv1CipherSuite() + { + $client = $this->service->getClient(); + $curlOptions = $client->getConfig('curl.options'); + $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + } } From 784aa4f495a8a3eeb70b95cd7127a952ef0d0e70 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:40:04 -0700 Subject: [PATCH 621/835] Check that critical message is being logged + suppress log output. --- .../Tests/Database/DatabaseTestCase.php | 25 ++++++++++++++++++- .../OpenCloud/Tests/Database/ServiceTest.php | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index 0e8ad75cb..c0a0b38e0 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -18,6 +18,22 @@ namespace OpenCloud\Tests\Database; use OpenCloud\Tests\OpenCloudTestCase; +use OpenCloud\Common\Log\Logger; + +class UnitTestLogger extends Logger +{ + protected $criticalLogMessage; + + public function critical($message, array $context = array()) + { + ++$this->criticalLogMessage; + } + + public function getCriticalLogMessage() + { + return $this->criticalLogMessage; + } +} class DatabaseTestCase extends OpenCloudTestCase { @@ -28,7 +44,9 @@ class DatabaseTestCase extends OpenCloudTestCase public function setupObjects() { - $this->service = $this->getClient()->databaseService(); + $client = $this->getClient(); + $client->setLogger(new UnitTestLogger()); + $this->service = $client->databaseService(); $this->addMockSubscriber($this->getTestFilePath('Instance')); $this->instance = $this->service->instance('foo'); @@ -37,4 +55,9 @@ public function setupObjects() $this->datastore = $this->service->datastore('10000000-0000-0000-0000-000000000001'); $this->datastoreVersion = $this->datastore->version('b00000b0-00b0-0b00-00b0-000b000000bb'); } + + protected function assertCriticalMessageWasLogged() + { + $this->assertNotEmpty($this->getClient()->getLogger()->getCriticalLogMessage()); + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 4c690ae81..3dc5a11f1 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -77,5 +77,6 @@ public function testClientUsesTLSv1CipherSuite() $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertCriticalMessageWasLogged(); } } From b42644a994062d63be5bf83c31f4bce64c88c56f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:49:39 -0700 Subject: [PATCH 622/835] Removing unnecessary import. --- lib/OpenCloud/Database/Service.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 50845ba24..2e9d56093 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -18,7 +18,6 @@ namespace OpenCloud\Database; use Guzzle\Http\ClientInterface; -use Psr\Log\LogLevel; use OpenCloud\Common\Service\NovaService; use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; From 6ed1eaa30b94c11c9756177a4a987f1866096e9c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:49:46 -0700 Subject: [PATCH 623/835] Breaking up log message over several lines. --- lib/OpenCloud/Database/Service.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 2e9d56093..00498ed28 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -121,7 +121,11 @@ public function setClient(ClientInterface $client) $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; $client->getConfig()->set('curl.options', $curlOptions); - $client->getLogger()->critical('The SDK is using the TLSv1 cipher suite when connecting to the Rackspace Cloud Databases service. This suite contains a weak cipher (RC4) so please use at your own risk. See https://github.com/rackspace/php-opencloud/issues/560 for details.'); + $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' + . 'to the Rackspace Cloud Databases service. This suite contains ' + . 'a weak cipher (RC4) so please use at your own risk. See ' + . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; + $client->getLogger()->critical($logMessage); $this->client = $client; } From fdeff66de63a11d5bc54e9de978a96817dacf430 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:54:37 -0700 Subject: [PATCH 624/835] Fixing option value - should be cipher name not integer (constant). --- lib/OpenCloud/Database/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 00498ed28..b73ee2f7c 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -118,7 +118,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'TLSv1'; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' From f17959cec5a44e1d3161e49b4e874d418b5a0662 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 09:50:56 -0700 Subject: [PATCH 625/835] Use custom cipher suite stronger than TLSv1 (but still including RC4 for Cloud Databases). --- lib/OpenCloud/Database/Service.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index b73ee2f7c..065191095 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -118,7 +118,10 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'TLSv1'; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' From d1e227af87e1363ef6d27c6d757608ad0128bed9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:08:35 -0700 Subject: [PATCH 626/835] Refactoring SSL cipher list into constant; fixing test. --- lib/OpenCloud/Database/Service.php | 10 ++++++---- tests/OpenCloud/Tests/Database/ServiceTest.php | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 065191095..0828d47f1 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -31,6 +31,11 @@ class Service extends NovaService const DEFAULT_TYPE = 'rax:database'; const DEFAULT_NAME = 'cloudDatabases'; + const SSL_CIPHER_LIST = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + /** * Returns an Instance * @@ -118,10 +123,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = self::SSL_CIPHER_LIST; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 3dc5a11f1..28636a363 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -27,6 +27,8 @@ namespace OpenCloud\Tests\Database; +use OpenCloud\Database\Service; + class ServiceTest extends DatabaseTestCase { public function test__construct() @@ -76,7 +78,7 @@ public function testClientUsesTLSv1CipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertEquals(Service::SSL_CIPHER_LIST, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); $this->assertCriticalMessageWasLogged(); } } From 5573919d972416fa671ceb43b7d60d1de262b6a8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:16:20 -0700 Subject: [PATCH 627/835] PHP < 5.6 does not like multi-line consts :| --- lib/OpenCloud/Database/Service.php | 15 +++++++++------ tests/OpenCloud/Tests/Database/ServiceTest.php | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 0828d47f1..1098e6add 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -31,11 +31,6 @@ class Service extends NovaService const DEFAULT_TYPE = 'rax:database'; const DEFAULT_NAME = 'cloudDatabases'; - const SSL_CIPHER_LIST = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; - /** * Returns an Instance * @@ -123,7 +118,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = self::SSL_CIPHER_LIST; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' @@ -134,4 +129,12 @@ public function setClient(ClientInterface $client) $this->client = $client; } + + public static function getSslCipherList() + { + return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 28636a363..a3f5f4401 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -78,7 +78,7 @@ public function testClientUsesTLSv1CipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(Service::SSL_CIPHER_LIST, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertEquals(Service::getSslCipherList(), $curlOptions['CURLOPT_SSL_CIPHER_LIST']); $this->assertCriticalMessageWasLogged(); } } From 8809cddade3e0e9d2dda654badb3bf20c27dead9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:18:45 -0700 Subject: [PATCH 628/835] Fixing log message to match reality. --- lib/OpenCloud/Database/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 1098e6add..58c5ba260 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -121,7 +121,7 @@ public function setClient(ClientInterface $client) $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); $client->getConfig()->set('curl.options', $curlOptions); - $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' + $logMessage = 'The SDK is using a custom cipher suite when connecting ' . 'to the Rackspace Cloud Databases service. This suite contains ' . 'a weak cipher (RC4) so please use at your own risk. See ' . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; From 8062469787a2090da08e26a7207dc89ec38bff98 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:20:01 -0700 Subject: [PATCH 629/835] Fixing unit test method name to match reality. --- tests/OpenCloud/Tests/Database/ServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index a3f5f4401..96a6be1c6 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -74,7 +74,7 @@ public function testDatastoreList() $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } - public function testClientUsesTLSv1CipherSuite() + public function testClientUsesCustomCipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); From 316de0978ee441efcbd08e67924ea2fa7c1c0ed8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:29:50 -0700 Subject: [PATCH 630/835] Refactoring logger into common class to enable reuse. --- .../Tests/Database/DatabaseTestCase.php | 19 ++-------- tests/OpenCloud/Tests/MockLogger.php | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 tests/OpenCloud/Tests/MockLogger.php diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index c0a0b38e0..403f6955f 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -18,22 +18,7 @@ namespace OpenCloud\Tests\Database; use OpenCloud\Tests\OpenCloudTestCase; -use OpenCloud\Common\Log\Logger; - -class UnitTestLogger extends Logger -{ - protected $criticalLogMessage; - - public function critical($message, array $context = array()) - { - ++$this->criticalLogMessage; - } - - public function getCriticalLogMessage() - { - return $this->criticalLogMessage; - } -} +use OpenCloud\Tests\MockLogger; class DatabaseTestCase extends OpenCloudTestCase { @@ -45,7 +30,7 @@ class DatabaseTestCase extends OpenCloudTestCase public function setupObjects() { $client = $this->getClient(); - $client->setLogger(new UnitTestLogger()); + $client->setLogger(new MockLogger()); $this->service = $client->databaseService(); $this->addMockSubscriber($this->getTestFilePath('Instance')); diff --git a/tests/OpenCloud/Tests/MockLogger.php b/tests/OpenCloud/Tests/MockLogger.php new file mode 100644 index 000000000..9aca3dd68 --- /dev/null +++ b/tests/OpenCloud/Tests/MockLogger.php @@ -0,0 +1,35 @@ +criticalLogMessage; + } + + public function getCriticalLogMessage() + { + return $this->criticalLogMessage; + } +} From f2cae65cadb4dce3daa2c94e8a5e09c3a471bcb9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:30:04 -0700 Subject: [PATCH 631/835] Use mock logger for database service test to supress log output. --- tests/OpenCloud/Tests/RackspaceTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/OpenCloud/Tests/RackspaceTest.php b/tests/OpenCloud/Tests/RackspaceTest.php index 790d54645..7cf37f183 100644 --- a/tests/OpenCloud/Tests/RackspaceTest.php +++ b/tests/OpenCloud/Tests/RackspaceTest.php @@ -17,6 +17,8 @@ namespace OpenCloud\Tests; +use OpenCloud\Tests\MockLogger; + class RackspaceTest extends OpenCloudTestCase { const CREDENTIALS = <<getClient()->getLogger(); + $this->getClient()->setLogger(new MockLogger()); + $this->assertInstanceOf( 'OpenCloud\Database\Service', $this->getClient()->databaseService('cloudDatabases', 'DFW') ); + + // Re-inject old logger + $this->getClient()->setLogger($oldLogger); + $this->assertInstanceOf( 'OpenCloud\LoadBalancer\Service', $this->getClient()->loadBalancerService('cloudLoadBalancers', 'DFW') From 5888580297649e49738acd8230a8418216040510 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 12:06:59 -0700 Subject: [PATCH 632/835] Adding reference to cipher list comment in docblock. --- lib/OpenCloud/Database/Service.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 58c5ba260..de20872b6 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -130,6 +130,9 @@ public function setClient(ClientInterface $client) $this->client = $client; } + /** + * @see https://github.com/rackspace/php-opencloud/issues/560#issuecomment-81790778 + */ public static function getSslCipherList() { return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' From ff90ba1ae728557dceec1f9ec43e630005ca4bdc Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Wed, 18 Mar 2015 14:50:43 +0000 Subject: [PATCH 633/835] Added symlink functionality via X-Object-Manifest header management --- lib/OpenCloud/Common/Constants/Header.php | 1 + .../ObjectStore/Resource/DataObject.php | 75 +++++++++++++++++-- .../ObjectStore/Resource/DataObjectTest.php | 17 +++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Common/Constants/Header.php b/lib/OpenCloud/Common/Constants/Header.php index c0246f71f..5de04bc2a 100644 --- a/lib/OpenCloud/Common/Constants/Header.php +++ b/lib/OpenCloud/Common/Constants/Header.php @@ -70,4 +70,5 @@ class Header const USER_AGENT = 'User-Agent'; const VARY = 'Vary'; const VIA = 'Via'; + const X_OBJECT_MANIFEST = 'X-Object-Manifest'; } diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index 5877431b8..ca74e4d66 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -76,6 +76,11 @@ class DataObject extends AbstractResource * @var string Etag. */ protected $etag; + + /** + * @var string Manifest. Can be null so we use false to mean unset. + */ + protected $manifest = false; /** * Also need to set Container parent and handle pseudo-directories. @@ -139,7 +144,9 @@ public function populateFromResponse(Response $response) ->setContentType((string) $headers[HeaderConst::CONTENT_TYPE]) ->setLastModified((string) $headers[HeaderConst::LAST_MODIFIED]) ->setContentLength((string) $headers[HeaderConst::CONTENT_LENGTH]) - ->setEtag((string) $headers[HeaderConst::ETAG]); + ->setEtag((string) $headers[HeaderConst::ETAG]) + // do not cast to a string to allow for null (i.e. no header) + ->setManifest($headers[HeaderConst::X_OBJECT_MANIFEST]); } public function refresh() @@ -293,6 +300,26 @@ public function getEtag() { return $this->etag ? : $this->content->getContentMd5(); } + + /** + * @param $manifest + * @return $this + */ + public function setManifest($manifest) + { + $this->manifest = $manifest; + + return $this; + } + + /** + * @return null|string + */ + public function getManifest() + { + // only make a request if manifest has not been set (is false) + return $this->manifest !== false ? $this->manifest : $this->getManifestHeader(); + } public function setLastModified($lastModified) { @@ -327,10 +354,11 @@ public function update($params = array()) // merge specific properties with metadata $metadata += array( - HeaderConst::CONTENT_TYPE => $this->contentType, - HeaderConst::LAST_MODIFIED => $this->lastModified, - HeaderConst::CONTENT_LENGTH => $this->contentLength, - HeaderConst::ETAG => $this->etag + HeaderConst::CONTENT_TYPE => $this->contentType, + HeaderConst::LAST_MODIFIED => $this->lastModified, + HeaderConst::CONTENT_LENGTH => $this->contentLength, + HeaderConst::ETAG => $this->etag, + HeaderConst::X_OBJECT_MANIFEST => $this->manifest ); return $this->container->uploadObject($this->name, $this->content, $metadata); @@ -354,6 +382,26 @@ public function delete($params = array()) { return $this->getService()->getClient()->delete($this->getUrl())->send(); } + + /** + * @param string $source Path (`container/object') of other object to symlink this object to + * @return \Guzzle\Http\Message\Response + */ + public function symlink($source) + { + $response = $this->getService() + ->getClient() + ->createRequest('PUT', $this->getUrl(), array( + HeaderConst::X_OBJECT_MANIFEST => (string) $source + )) + ->send(); + + if ($response->getStatusCode() == 201) { + $this->setManifest($source); + } + + return $response; + } /** * Get a temporary URL for this object. @@ -449,4 +497,21 @@ protected static function headerIsValidMetadata($header) return preg_match($pattern, $header); } + + /** + * @return null|string + */ + protected function getManifestHeader() + { + $response = $this->getService() + ->getClient() + ->head($this->getUrl()) + ->send(); + + $manifest = $response->getHeader(HeaderConst::X_OBJECT_MANIFEST); + + $this->setManifest($manifest); + + return $manifest; + } } diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 8193c267d..fc67ad35d 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -101,4 +101,21 @@ public function test_Public_Urls() $this->assertNotNull($object->getPublicUrl(UrlType::STREAMING)); $this->assertNotNull($object->getPublicUrl(UrlType::IOS_STREAMING)); } + + public function test_Symlink() + { + $object = $this->container->dataObject('foobar'); + $this->assertInstanceOf( + 'Guzzle\Http\Message\Response', + $object->symlink('/new_container/new_object') + ); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\NoNameError + */ + public function test_Symlink_Fails() + { + $this->container->dataObject()->symlink(null); + } } From 6f69e7ab4daaa69a7e3b731846ea450e62b5aaec Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Thu, 19 Mar 2015 18:28:25 +0000 Subject: [PATCH 634/835] Added createSymlinkFrom. Renamed symlink to createSymlinkTo. Made setManifest protected. Updated PHPDoc and tests. --- .../ObjectStore/Resource/DataObject.php | 69 ++++++++++++++----- .../ObjectStore/Resource/DataObjectTest.php | 34 ++++++--- 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index ca74e4d66..ace797bd3 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -302,10 +302,10 @@ public function getEtag() } /** - * @param $manifest + * @param string $manifest Path (`container/object') to set as the value to X-Object-Manifest * @return $this */ - public function setManifest($manifest) + protected function setManifest($manifest) { $this->manifest = $manifest; @@ -313,7 +313,7 @@ public function setManifest($manifest) } /** - * @return null|string + * @return null|string Path (`container/object') from X-Object-Manifest header or null if the header does not exist */ public function getManifest() { @@ -384,23 +384,60 @@ public function delete($params = array()) } /** - * @param string $source Path (`container/object') of other object to symlink this object to - * @return \Guzzle\Http\Message\Response + * @param string $destination Path (`container/object') of other object to symlink this object to + * @return null|\Guzzle\Http\Message\Response The response or null if $this is not empty */ - public function symlink($source) + public function createSymlinkTo($destination) { - $response = $this->getService() - ->getClient() - ->createRequest('PUT', $this->getUrl(), array( - HeaderConst::X_OBJECT_MANIFEST => (string) $source - )) - ->send(); - - if ($response->getStatusCode() == 201) { - $this->setManifest($source); + if (!$this->name) { + throw new Exceptions\NoNameError(Lang::translate('Object has no name')); + } + + if ($this->getContentLength() == 0) { + $response = $this->getService() + ->getClient() + ->createRequest('PUT', $this->getUrl(), array( + HeaderConst::X_OBJECT_MANIFEST => (string) $destination + )) + ->send(); + + if ($response->getStatusCode() == 201) { + $this->setManifest($source); + } + + return $response; + } + + return null; + } + + /** + * @param string $source Path (`container/object') of other object to symlink this object from + * @return null|DataObject The symlinked object or null if object already exists and is not empty + */ + public function createSymlinkFrom($source) + { + if (!strlen($source)) { + throw new Exceptions\NoNameError(Lang::translate('Object has no name')); + } + + // Use ltrim to remove leading slash from source + list($containerName, $resourceName) = explode("/", ltrim($source, '/'), 2); + + $container = $this->getService()->getContainer($containerName); + + if ($unsafe = $container->objectExists($resourceName)) { + $object = $container->getPartialObject($source); + $unsafe = $object->getContentLength() > 0; + } + + if (!$unsafe) { + return $container->uploadObject($resourceName, 'data', array( + HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl() + )); } - return $response; + return null; } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index fc67ad35d..bac0ebebf 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -101,21 +101,37 @@ public function test_Public_Urls() $this->assertNotNull($object->getPublicUrl(UrlType::STREAMING)); $this->assertNotNull($object->getPublicUrl(UrlType::IOS_STREAMING)); } - - public function test_Symlink() + + public function test_Symlink_To() { $object = $this->container->dataObject('foobar'); - $this->assertInstanceOf( - 'Guzzle\Http\Message\Response', - $object->symlink('/new_container/new_object') - ); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $object->createSymlinkTo('new_container/new_object')); + // @todo getManifest should return the manifest not null + //$this->assertEquals('new_container/new_object', $object->getManifest()); } - + + /** + * @expectedException OpenCloud\Common\Exceptions\NoNameError + */ + public function test_Symlink_To_Fails() + { + $object = $this->container->dataObject()->createSymlinkTo(null); + } + + public function test_Symlink_From() + { + $object = $this->container->dataObject('foobar'); + $symlink = $object->createSymlinkFrom('new_container/new_object'); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $symlink); + // @todo getManifest should return the manifest not null + //$this->assertEquals('new_container/new_object', $symlink->getManifest()); + } + /** * @expectedException OpenCloud\Common\Exceptions\NoNameError */ - public function test_Symlink_Fails() + public function test_Symlink_From_Fails() { - $this->container->dataObject()->symlink(null); + $object = $this->container->dataObject()->createSymlinkFrom(null); } } From 0b4a0ed459589ab852fbbe81e451a36f678ac7e9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 20 Mar 2015 15:00:34 +0100 Subject: [PATCH 635/835] [ci skip] Add more links to our official docs --- README.md | 2 +- docs/README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/README.md diff --git a/README.md b/README.md index 6031cf4ac..8ddd9f1b5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ PHP SDK for OpenStack/Rackspace APIs [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) -For SDKs in different languages, see http://developer.rackspace.com. +Our official documentation is now available on http://docs.php-opencloud.com. For SDKs in different languages, see http://developer.rackspace.com. The PHP SDK should work with most OpenStack-based cloud deployments, though it specifically targets the Rackspace public cloud. In diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..154fd158d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Documentation + +Our official docs are hosted on http://docs.php-opencloud.com. From cde380b91846d4e2a6cc45ae1f5b3a0a661a0796 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Fri, 20 Mar 2015 15:51:08 +0000 Subject: [PATCH 636/835] Added getManifest assertions to symlink tests --- .../ObjectStore/Resource/DataObjectTest.php | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index bac0ebebf..c7859649e 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -17,7 +17,10 @@ namespace OpenCloud\Tests\ObjectStore\Resource; +use Guzzle\Http\Message\Response; +use OpenCloud\Common\Constants\Header; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\Tests\MockSubscriber; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; class DataObjectTest extends ObjectStoreTestCase @@ -104,10 +107,11 @@ public function test_Public_Urls() public function test_Symlink_To() { + $targetName = 'new_container/new_object'; + $this->addMockSubscriber(new Response(200, array(Header::X_OBJECT_MANIFEST => $targetName))); $object = $this->container->dataObject('foobar'); - $this->assertInstanceOf('Guzzle\Http\Message\Response', $object->createSymlinkTo('new_container/new_object')); - // @todo getManifest should return the manifest not null - //$this->assertEquals('new_container/new_object', $object->getManifest()); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $object->createSymlinkTo($targetName)); + $this->assertEquals($targetName, $object->getManifest()); } /** @@ -120,11 +124,27 @@ public function test_Symlink_To_Fails() public function test_Symlink_From() { - $object = $this->container->dataObject('foobar'); - $symlink = $object->createSymlinkFrom('new_container/new_object'); - $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $symlink); - // @todo getManifest should return the manifest not null - //$this->assertEquals('new_container/new_object', $symlink->getManifest()); + $symlinkName = 'new_container/new_object'; + + // We have to fill the mock response queue to properly get the correct X-Object-Manifest header + // Container\dataObject( ) + // - Container\refresh( ) + $this->addMockSubscriber(new Response(200)); + // DataObject\createSymlinkFrom( ) + // - Container\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - CDNContainer\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - Container\objectExists( ) + $this->addMockSubscriber(new Response(200)); + // - Container\getPartialObject( ) + $this->addMockSubscriber(new Response(200)); + // - Container\uploadObject( ) + $this->addMockSubscriber(new Response(200, array(Header::X_OBJECT_MANIFEST => $symlinkName))); + + $object = $this->container->dataObject('foobar')->createSymlinkFrom($symlinkName); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $object); + $this->assertEquals($symlinkName, $object->getManifest()); } /** From ffbd1b707af59202dd9cf0afd1ced5e7f0d80c4c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 20 Mar 2015 17:42:30 +0100 Subject: [PATCH 637/835] [ci skip] Fix incorrect method in docs --- doc/services/object-store/objects.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 0eb0ea836..7d9b2120e 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -204,8 +204,8 @@ Get file name .. code-block:: php - /** @param $container OpenCloud\ObjectStore\Resource\Container */ - $container = $object->getContainer(); + /** @param $name string */ + $name = $object->getName(); Get file size From b75c19d91ca74e5bded9a803f4efc2debe2a398f Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Fri, 20 Mar 2015 17:25:06 +0000 Subject: [PATCH 638/835] Changed symlink failures from null to exceptions. Added tests. --- .../Exception/ObjectNotEmptyException.php | 35 ++++++++++++++++ .../ObjectStore/Resource/DataObject.php | 42 +++++++++---------- .../ObjectStore/Resource/DataObjectTest.php | 37 ++++++++++++++-- 3 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php diff --git a/lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php b/lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php new file mode 100644 index 000000000..6bf75f2f9 --- /dev/null +++ b/lib/OpenCloud/ObjectStore/Exception/ObjectNotEmptyException.php @@ -0,0 +1,35 @@ +name = $name; + + return $e; + } +} diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index ace797bd3..12ab65d8b 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -24,6 +24,7 @@ use OpenCloud\Common\Exceptions; use OpenCloud\Common\Lang; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\ObjectStore\Exception\ObjectNotEmptyException; /** * Objects are the basic storage entities in Cloud Files. They represent the @@ -393,22 +394,22 @@ public function createSymlinkTo($destination) throw new Exceptions\NoNameError(Lang::translate('Object has no name')); } - if ($this->getContentLength() == 0) { - $response = $this->getService() - ->getClient() - ->createRequest('PUT', $this->getUrl(), array( - HeaderConst::X_OBJECT_MANIFEST => (string) $destination - )) - ->send(); + if ($this->getContentLength()) { + throw new ObjectNotEmptyException($this->getContainer()->getName() . '/' . $this->getName()); + } - if ($response->getStatusCode() == 201) { - $this->setManifest($source); - } + $response = $this->getService() + ->getClient() + ->createRequest('PUT', $this->getUrl(), array( + HeaderConst::X_OBJECT_MANIFEST => (string) $destination + )) + ->send(); - return $response; + if ($response->getStatusCode() == 201) { + $this->setManifest($source); } - return null; + return $response; } /** @@ -423,21 +424,18 @@ public function createSymlinkFrom($source) // Use ltrim to remove leading slash from source list($containerName, $resourceName) = explode("/", ltrim($source, '/'), 2); - $container = $this->getService()->getContainer($containerName); - if ($unsafe = $container->objectExists($resourceName)) { + if ($container->objectExists($resourceName)) { $object = $container->getPartialObject($source); - $unsafe = $object->getContentLength() > 0; - } - - if (!$unsafe) { - return $container->uploadObject($resourceName, 'data', array( - HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl() - )); + if ($object->getContentLength() > 0) { + throw new ObjectNotEmptyException($source); + } } - return null; + return $container->uploadObject($resourceName, 'data', array( + HeaderConst::X_OBJECT_MANIFEST => (string) $this->getUrl() + )); } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index c7859649e..97af383a9 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -20,6 +20,7 @@ use Guzzle\Http\Message\Response; use OpenCloud\Common\Constants\Header; use OpenCloud\ObjectStore\Constants\UrlType; +use OpenCloud\ObjectStore\Exception\ObjectNotEmptyException; use OpenCloud\Tests\MockSubscriber; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; @@ -117,11 +118,20 @@ public function test_Symlink_To() /** * @expectedException OpenCloud\Common\Exceptions\NoNameError */ - public function test_Symlink_To_Fails() + public function test_Symlink_To_Fails_With_NoName() { $object = $this->container->dataObject()->createSymlinkTo(null); } + /** + * @expectedException OpenCloud\ObjectStore\Exception\ObjectNotEmptyException + */ + public function test_Symlink_To_Fails_With_NotEmpty() + { + $this->addMockSubscriber(new Response(200, array(Header::CONTENT_LENGTH => 100))); + $object = $this->container->dataObject('foobar')->createSymlinkTo('new_container/new_object'); + } + public function test_Symlink_From() { $symlinkName = 'new_container/new_object'; @@ -144,14 +154,35 @@ public function test_Symlink_From() $object = $this->container->dataObject('foobar')->createSymlinkFrom($symlinkName); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $object); - $this->assertEquals($symlinkName, $object->getManifest()); } /** * @expectedException OpenCloud\Common\Exceptions\NoNameError */ - public function test_Symlink_From_Fails() + public function test_Symlink_From_Fails_With_NoName() { $object = $this->container->dataObject()->createSymlinkFrom(null); } + + /** + * @expectedException OpenCloud\ObjectStore\Exception\ObjectNotEmptyException + */ + public function test_Symlink_From_Fails_With_NotEmpty() + { + // We have to fill the mock response queue to properly get the correct Content-Length header + // Container\dataObject( ) + // - Container\refresh( ) + $this->addMockSubscriber(new Response(200)); + // DataObject\createSymlinkFrom( ) + // - Container\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - CDNContainer\createRefreshRequest( ) + $this->addMockSubscriber(new Response(200)); + // - Container\objectExists( ) + $this->addMockSubscriber(new Response(200)); + // - Container\getPartialObject( ) + $this->addMockSubscriber(new Response(200, array(Header::CONTENT_LENGTH => 100))); + + $object = $this->container->dataObject('foobar')->createSymlinkFrom('new_container/new_object'); + } } From dc33b8c971dfc1d65ae85f9c586d2de5d0056770 Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Fri, 20 Mar 2015 17:38:32 +0000 Subject: [PATCH 639/835] Added documentation --- doc/services/object-store/objects.rst | 35 ++++++++++++++++++++ samples/ObjectStore/symlink-object.php | 45 ++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 samples/ObjectStore/symlink-object.php diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 7d9b2120e..d266ef9a2 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -326,6 +326,41 @@ the name of the object inside the container that does not exist yet. `Get the executable PHP script for this example `_ +Symlinking to this object from another location +----------------------------------------------- + +To create a symlink to this file in another location you need to specify +a string-based source + +.. code-block:: php + + $object->createSymlinkFrom('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container that either does not exist yet or +is an empty file. + +`Get the executable PHP script for this example `_ + + +Setting this object to symlink to another location +-------------------------------------------------- + +To set this file to symlink to another location you need to specify +a string-based destination + +.. code-block:: php + + $object->createSymlinkTo('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container. + +The object must be an empty file. + +`Get the executable PHP script for this example `_ + + Get object metadata ------------------- diff --git a/samples/ObjectStore/symlink-object.php b/samples/ObjectStore/symlink-object.php new file mode 100644 index 000000000..f5c4bc129 --- /dev/null +++ b/samples/ObjectStore/symlink-object.php @@ -0,0 +1,45 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Get container. +$container = $objectStoreService->getContainer('{sourceContainerName}'); + +// 4. Get object. +$object = $container->getObject('{objectName}'); + +// 5. Create another container for object's copy. +$objectStoreService->createContainer('{destinationContainerName}'); + +// 6. Symlink to object from another container object. {objectName} must either not exist or be an empty file. +$object->createSymlinkFrom('{destinationContainerName}/{objectName}'); + +// 7. Symlink from object to another container object. $object must be an empty file. +$object->createSymlinkTo('{destinationContainerName}/{objectName}'); From 2cbc2230e5f348298adc185465e217498be6f9df Mon Sep 17 00:00:00 2001 From: justin Finkelstein Date: Sat, 21 Mar 2015 13:27:20 +0000 Subject: [PATCH 640/835] Added example of how to retrieve more than 10,000 objects --- doc/services/object-store/objects.rst | 36 +++++++++++ .../ObjectStore/list-objects-over-10000.php | 62 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 samples/ObjectStore/list-objects-over-10000.php diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index d266ef9a2..347b243ea 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -177,6 +177,42 @@ docs `_ +List over 10,000 objects +------------------------ + +To retrieve more than 10,000 objects (the default limit), you'll need to use +the built-in paging which uses a 'marker' parameter to fetch the next page of data. + +.. code-block:: php + + $containerObjects = array(); + $marker = ''; + + while ($marker !== null) { + $params = array( + 'marker' => $marker, + ); + + $objects = $container->objectList($params); + $total = $objects->count(); + $count = 0; + + if ($total == 0) { + break; + } + + while ($object = $objects->next()) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + $containerObjects[] = $object->getName(); + $count++; + + $marker = ($count == $total) ? $object->getName() : null; + } + } + +`Get the executable PHP script for this example `_ + + Get object ---------- diff --git a/samples/ObjectStore/list-objects-over-10000.php b/samples/ObjectStore/list-objects-over-10000.php new file mode 100644 index 000000000..364ebfdc7 --- /dev/null +++ b/samples/ObjectStore/list-objects-over-10000.php @@ -0,0 +1,62 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Get container. +$container = $objectStoreService->getContainer('{containerName}'); + +// 4. Get the list of objects +$objects = $container->objectList(); + +// 5. Create a list of all objects in the container +$containerObjects = array(); +$marker = ''; + +while ($marker !== null) { + $params = array( + 'marker' => $marker, + ); + + $objects = $container->objectList($params); + $total = $objects->count(); + $count = 0; + + if ($total == 0) { + break; + } + + while ($object = $objects->next()) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + $containerObjects[] = $object->getName(); + $count++; + + $marker = ($count == $total) ? $object->getName() : null; + } +} From f197f786fd81620f88cf7ee55ada7dfc58adf9c7 Mon Sep 17 00:00:00 2001 From: justin Finkelstein Date: Sat, 21 Mar 2015 15:50:46 +0000 Subject: [PATCH 641/835] Updated to use foreach rather than while --- doc/services/object-store/objects.rst | 2 +- samples/ObjectStore/list-objects-over-10000.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 347b243ea..e23d6d4cb 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -201,7 +201,7 @@ the built-in paging which uses a 'marker' parameter to fetch the next page of da break; } - while ($object = $objects->next()) { + foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ $containerObjects[] = $object->getName(); $count++; diff --git a/samples/ObjectStore/list-objects-over-10000.php b/samples/ObjectStore/list-objects-over-10000.php index 364ebfdc7..ec349e88d 100644 --- a/samples/ObjectStore/list-objects-over-10000.php +++ b/samples/ObjectStore/list-objects-over-10000.php @@ -52,7 +52,7 @@ break; } - while ($object = $objects->next()) { + foreach ($objects as $object) { /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ $containerObjects[] = $object->getName(); $count++; From 63d0f7bfb7347bd76edc8bcb1cbf622a3d5b7154 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 22 Mar 2015 12:08:35 -0700 Subject: [PATCH 642/835] Fixing return type. --- lib/OpenCloud/DNS/Resource/Domain.php | 2 +- lib/OpenCloud/DNS/Service.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/DNS/Resource/Domain.php b/lib/OpenCloud/DNS/Resource/Domain.php index cdd8ef28e..fc9bd89b8 100644 --- a/lib/OpenCloud/DNS/Resource/Domain.php +++ b/lib/OpenCloud/DNS/Resource/Domain.php @@ -76,7 +76,7 @@ public function record($info = null) * returns a Collection of Record objects * * @param array $filter query-string parameters - * @return \OpenCloud\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function recordList($filter = array()) { diff --git a/lib/OpenCloud/DNS/Service.php b/lib/OpenCloud/DNS/Service.php index cafb4ad94..cdee6dc48 100644 --- a/lib/OpenCloud/DNS/Service.php +++ b/lib/OpenCloud/DNS/Service.php @@ -59,7 +59,7 @@ public function domain($info = null) * Returns a collection of domains * * @param array $filter key/value pairs to use as query strings - * @return \OpenCloud\Common\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function domainList($filter = array()) { @@ -85,7 +85,7 @@ public function ptrRecord($info = null) * * @param \OpenCloud\Compute\Resource\Server $server the server for which to * retrieve the PTR records - * @return \OpenCloud\Common\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function ptrRecordList(HasPtrRecordsInterface $parent) { From b28ca90c390603fd77fa22a0b336085c0e09af4b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 22 Mar 2015 12:08:46 -0700 Subject: [PATCH 643/835] Adding return type. --- lib/OpenCloud/DNS/Resource/Domain.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/OpenCloud/DNS/Resource/Domain.php b/lib/OpenCloud/DNS/Resource/Domain.php index fc9bd89b8..a103677f5 100644 --- a/lib/OpenCloud/DNS/Resource/Domain.php +++ b/lib/OpenCloud/DNS/Resource/Domain.php @@ -107,6 +107,7 @@ public function subdomain($info = array()) * * @param array $filter key/value pairs for query string parameters * return \OpenCloud\Collection + * @return OpenCloud\DNS\Collection\DnsIterator */ public function subdomainList($filter = array()) { From 9ad41612279ce45b2c98316056a77d209746f014 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Sun, 22 Mar 2015 12:08:54 -0700 Subject: [PATCH 644/835] Adding docblock. --- lib/OpenCloud/DNS/Service.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/OpenCloud/DNS/Service.php b/lib/OpenCloud/DNS/Service.php index cdee6dc48..9d3bbc9dd 100644 --- a/lib/OpenCloud/DNS/Service.php +++ b/lib/OpenCloud/DNS/Service.php @@ -181,6 +181,13 @@ public function limitTypes() return $body->limitTypes; } + /** + * List asynchronous responses' statuses. + * @see http://docs.rackspace.com/cdns/api/v1.0/cdns-devguide/content/viewing_status_all_asynch_jobs.html + * + * @param array $query Any query parameters. Optional. + * @return OpenCloud\DNS\Collection\DnsIterator + */ public function listAsyncJobs(array $query = array()) { $url = clone $this->getUrl(); From 9a8a8abe78f3589af0ce0a2f4ed0c0d487423f2d Mon Sep 17 00:00:00 2001 From: Mark Challoner Date: Mon, 23 Mar 2015 15:09:51 +0000 Subject: [PATCH 645/835] Updated phpdoc for createSymlink functions --- lib/OpenCloud/ObjectStore/Resource/DataObject.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index 12ab65d8b..de302b4cd 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -385,8 +385,12 @@ public function delete($params = array()) } /** + * Create a symlink to another named object from this object. Requires this object to be empty. + * * @param string $destination Path (`container/object') of other object to symlink this object to - * @return null|\Guzzle\Http\Message\Response The response or null if $this is not empty + * @return \Guzzle\Http\Message\Response The response + * @throws \OpenCloud\Common\Exceptions\NoNameError if a destination name is not provided + * @throws \OpenCloud\ObjectStore\Exception\ObjectNotEmptyException if $this is not an empty object */ public function createSymlinkTo($destination) { @@ -413,8 +417,12 @@ public function createSymlinkTo($destination) } /** + * Create a symlink to this object from another named object. Requires the other object to either not exist or be empty. + * * @param string $source Path (`container/object') of other object to symlink this object from - * @return null|DataObject The symlinked object or null if object already exists and is not empty + * @return DataObject The symlinked object + * @throws \OpenCloud\Common\Exceptions\NoNameError if a source name is not provided + * @throws \OpenCloud\ObjectStore\Exception\ObjectNotEmptyException if object already exists and is not empty */ public function createSymlinkFrom($source) { From 63a1c31242272447895eda34ba06a08242eafd4e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Mar 2015 10:41:16 -0700 Subject: [PATCH 646/835] Appeasing the PSR-2 linter. --- lib/OpenCloud/ObjectStore/Resource/AbstractResource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php index 8e16b30b3..37722e93d 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php @@ -77,7 +77,7 @@ public function getClient() /** * Factory method that allows for easy instantiation from a Response object. - * + * * For internal use only. * * @param Response $response HTTP response from an API operation. From 73bcedcfc1df3853b8af1388ea754a1c270fe5b1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Mar 2015 16:13:51 -0700 Subject: [PATCH 647/835] Lazily initialize CDN container object composed insider regular container object. --- .../ObjectStore/Resource/Container.php | 41 +++++++++++-------- .../Tests/ObjectStore/ObjectStoreTestCase.php | 11 +++-- .../ObjectStore/Resource/ContainerTest.php | 6 ++- .../ObjectStore/Resource/DataObjectTest.php | 2 + 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 0f7b0df08..57b8ce34a 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -323,23 +323,6 @@ public function refresh($id = null, $url = null) { $headers = $this->createRefreshRequest()->send()->getHeaders(); $this->setMetadata($headers, true); - - try { - if (null !== ($cdnService = $this->getService()->getCDNService())) { - $cdn = new CDNContainer($cdnService); - $cdn->setName($this->name); - - $response = $cdn->createRefreshRequest()->send(); - - if ($response->isSuccessful()) { - $this->cdn = $cdn; - $this->cdn->setMetadata($response->getHeaders(), true); - } - } else { - $this->cdn = null; - } - } catch (ClientErrorResponseException $e) { - } } /** @@ -597,6 +580,30 @@ public function uploadDirectory($path) public function isCdnEnabled() { + // If CDN object is not already populated, try to populate it. + if (null === $this->cdn) { + $this->refreshCdnObject(); + } return ($this->cdn instanceof CDNContainer) && $this->cdn->isCdnEnabled(); } + + protected function refreshCdnObject() + { + try { + if (null !== ($cdnService = $this->getService()->getCDNService())) { + $cdn = new CDNContainer($cdnService); + $cdn->setName($this->name); + + $response = $cdn->createRefreshRequest()->send(); + + if ($response->isSuccessful()) { + $this->cdn = $cdn; + $this->cdn->setMetadata($response->getHeaders(), true); + } + } else { + $this->cdn = null; + } + } catch (ClientErrorResponseException $e) { + } + } } diff --git a/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php b/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php index 985f7830c..3c3c995c4 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php +++ b/tests/OpenCloud/Tests/ObjectStore/ObjectStoreTestCase.php @@ -42,7 +42,12 @@ public function setupObjects() $this->addMockSubscriber($response1); - $response2 = new Response(204, array( + $this->container = $this->service->getContainer('foo'); + } + + protected function setupCdnContainerMockResponse() + { + $response = new Response(204, array( 'X-Cdn-Ssl-Uri' => 'https://83c49b9a2f7ad18250b3-346eb45fd42c58ca13011d659bfc1ac1.ssl.cf0.rackcdn.com', 'X-Ttl' => '259200', 'X-Cdn-Uri' => 'http://081e40d3ee1cec5f77bf-346eb45fd42c58ca13011d659bfc1ac1.r49.cf0.rackcdn.com', @@ -52,8 +57,6 @@ public function setupObjects() 'X-Trans-Id' => 'tx82a6752e00424edb9c46fa2573132e2c' )); - $this->addMockSubscriber($response2); - - $this->container = $this->service->getContainer('foo'); + $this->addMockSubscriber($response); } } diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index b5d2bc602..c5b191249 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -51,6 +51,8 @@ public function test_Get_Container() $this->assertEquals('3846773', $container->getBytesUsed()); $this->assertFalse($container->hasLogRetention()); + $this->setupCdnContainerMockResponse(); + $cdn = $container->getCdn(); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\CDNContainer', $cdn); $this->assertEquals('tx82a6752e00424edb9c46fa2573132e2c', $cdn->getTransId()); @@ -115,13 +117,13 @@ public function test_Delete_NonEmpty_Container() $this->addMockSubscriber($this->makeResponse('[]', 409)); $container->delete(); } + public function test_Object_List() { $container = $this->container; $this->addMockSubscriber($this->makeResponse('[{"name":"test_obj_1","hash":"4281c348eaf83e70ddce0e07221c3d28","bytes":14,"content_type":"application\/octet-stream","last_modified":"2009-02-03T05:26:32.612278"},{"name":"test_obj_2","hash":"b039efe731ad111bc1b0ef221c3849d0","bytes":64,"content_type":"application\/octet-stream","last_modified":"2009-02-03T05:26:32.612278"}]', 200)); - $list = $container->objectList(); $this->assertInstanceOf(self::COLLECTION_CLASS, $list); $this->assertEquals('test_obj_1', $list->first()->getName()); @@ -141,6 +143,8 @@ public function test_Misc_Operations() $container->disableLogging() ); + $this->setupCdnContainerMockResponse(); + $this->assertInstanceOf( 'Guzzle\Http\Message\Response', $container->getCdn()->setStaticIndexPage('index.html') diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 97af383a9..cd35674d3 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -90,6 +90,7 @@ public function test_Temp_Url_Fails_With_Incorrect_Method() public function test_Purge() { $object = $this->container->dataObject('foobar'); + $this->setupCdnContainerMockResponse(); $this->assertInstanceOf( 'Guzzle\Http\Message\Response', $object->purge('test@example.com') @@ -100,6 +101,7 @@ public function test_Public_Urls() { $object = $this->container->dataObject('foobar'); + $this->setupCdnContainerMockResponse(); $this->assertNotNull($object->getPublicUrl()); $this->assertNotNull($object->getPublicUrl(UrlType::SSL)); $this->assertNotNull($object->getPublicUrl(UrlType::STREAMING)); From 11b107d86d137a67931d47ae3c0b1d9aab26d762 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 23 Mar 2015 16:28:30 -0700 Subject: [PATCH 648/835] Removing mocking for refreshing CDN container composed object. --- tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index cd35674d3..4bebaf346 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -178,8 +178,6 @@ public function test_Symlink_From_Fails_With_NotEmpty() // DataObject\createSymlinkFrom( ) // - Container\createRefreshRequest( ) $this->addMockSubscriber(new Response(200)); - // - CDNContainer\createRefreshRequest( ) - $this->addMockSubscriber(new Response(200)); // - Container\objectExists( ) $this->addMockSubscriber(new Response(200)); // - Container\getPartialObject( ) From edd6dd71d6d61c000267c3506d0e2dbcc80db648 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 03:19:04 +0100 Subject: [PATCH 649/835] Fixing add, modify, and remove records links. --- doc/services/dns/domains.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/dns/domains.rst b/doc/services/dns/domains.rst index 0a7b83b96..b6c60290c 100644 --- a/doc/services/dns/domains.rst +++ b/doc/services/dns/domains.rst @@ -268,8 +268,8 @@ Modify domain Only the TTL, email address and comment attributes of a domain can be modified. Records cannot be added, modified, or removed through this API operation - you -will need to use the `add records `__, `modify records -`__ or `remove records `__ +will need to use the `add records `__, `modify records +`__ or `remove records `__ operations respectively. .. code-block:: php From 91054a6f52f75cc04a5eb5bbf5946e39bbee1b4e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 08:51:08 +0100 Subject: [PATCH 650/835] Adding convenience method to retrieve domain by name. --- .../Exceptions/DomainNotFoundException.php | 22 +++++++++++++++++++ lib/OpenCloud/DNS/Service.php | 19 ++++++++++++++++ tests/OpenCloud/Tests/DNS/ServiceTest.php | 9 ++++++++ 3 files changed, 50 insertions(+) create mode 100644 lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php diff --git a/lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php b/lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php new file mode 100644 index 000000000..07ddfb608 --- /dev/null +++ b/lib/OpenCloud/Common/Exceptions/DomainNotFoundException.php @@ -0,0 +1,22 @@ +resource('Domain', $info); } + /** + * Returns a domain, given a domain name + * + * @param string $domainName Domain name + * @return Domain the domain object + */ + public function domainByName($domainName) + { + $domainList = $this->domainList(array("name" => $domainName)); + + if (count($domainList) != 1) { + throw new DomainNotFoundException(); + } + + return $this->resource('Domain', $domainList[0]); + } + + /** * Returns a collection of domains * diff --git a/tests/OpenCloud/Tests/DNS/ServiceTest.php b/tests/OpenCloud/Tests/DNS/ServiceTest.php index 73da7403e..5c299ff1c 100644 --- a/tests/OpenCloud/Tests/DNS/ServiceTest.php +++ b/tests/OpenCloud/Tests/DNS/ServiceTest.php @@ -40,6 +40,15 @@ public function testDomain() $this->assertInstanceOf('OpenCloud\DNS\Resource\Domain', $this->service->domain()); } + public function testDomainByName() + { + $this->addMockSubscriber($this->makeResponse('{"domains":[{"name":"region2.example.net","id":2725352,"updated":"2011-06-23T20:21:06.000+0000","created":"2011-06-23T19:24:27.000+0000"}],"totalEntries":114}', 200)); + $domain = $this->service->domainByName("region2.example.net"); + + $this->assertInstanceOf('OpenCloud\DNS\Resource\Domain', $domain); + $this->assertEquals("region2.example.net", $domain->getName()); + } + /** * @mockFile Domain_List */ From b993d876137c99304f30fa0f79a4e3f673121392 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 08:58:48 +0100 Subject: [PATCH 651/835] Adding test for domain not found, given domain name. --- tests/OpenCloud/Tests/DNS/ServiceTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/OpenCloud/Tests/DNS/ServiceTest.php b/tests/OpenCloud/Tests/DNS/ServiceTest.php index 5c299ff1c..9503e0409 100644 --- a/tests/OpenCloud/Tests/DNS/ServiceTest.php +++ b/tests/OpenCloud/Tests/DNS/ServiceTest.php @@ -49,6 +49,15 @@ public function testDomainByName() $this->assertEquals("region2.example.net", $domain->getName()); } + /** + * @expectedException OpenCloud\Common\Exceptions\DomainNotFoundException + */ + public function testDomainByNameWhenDomainNotFound() + { + $this->addMockSubscriber($this->makeResponse('{"domains":[],"totalEntries":114}', 200)); + $domain = $this->service->domainByName("region2.example.net"); + } + /** * @mockFile Domain_List */ From 7188974a2a75a15c424a14dad5f9d0ff2c4c46f2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 09:40:29 +0100 Subject: [PATCH 652/835] Adding support for availability zones in Compute servers. --- lib/OpenCloud/Compute/Resource/Server.php | 13 +++++ .../create_server_with_availability_zone.php | 48 +++++++++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 9 ++++ .../Tests/Compute/_response/Extensions.resp | 8 ++++ 4 files changed, 78 insertions(+) create mode 100644 samples/Compute/create_server_with_availability_zone.php diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 11fb474ca..490c285e6 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -172,6 +172,12 @@ class Server extends NovaResource implements HasPtrRecordsInterface */ public $powerStatus; + /** + * @link http://developer.openstack.org/api-ref-compute-v2-ext.html#ext-os-ext-az + * @var string Availability zone of the VM + */ + public $availabilityZone; + protected static $json_name = 'server'; protected static $url_resource = 'servers'; @@ -206,6 +212,7 @@ class Server extends NovaResource implements HasPtrRecordsInterface 'OS-EXT-STS:vm_state' => 'extendedStatus', 'OS-EXT-STS:task_state' => 'taskStatus', 'OS-EXT-STS:power_state' => 'powerStatus', + 'OS-EXT-AZ:availability_zone' => 'availabilityZone' ); /** @@ -729,6 +736,12 @@ protected function createJson() $server->user_data = $this->user_data; } + // Availability zone + if (!empty($this->availabilityZone)) { + $this->checkExtension('OS-EXT-AZ'); + $server->availability_zone = $this->availabilityZone; + } + return (object) array('server' => $server); } diff --git a/samples/Compute/create_server_with_availability_zone.php b/samples/Compute/create_server_with_availability_zone.php new file mode 100644 index 000000000..cf4b1f4bc --- /dev/null +++ b/samples/Compute/create_server_with_availability_zone.php @@ -0,0 +1,48 @@ + '{username}', + 'password' => '{password}', +)); + +// 2. Create Compute service +$service = $client->computeService('nova', '{region}'); + +// 3. Get empty server +$server = $service->server(); + +// 4. Create the server. If you do not know what imageId or flavorId to use, +// please run the list_flavors.php and list_images.php scripts. +try { + $response = $server->create(array( + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'availabilityZone' => '{availabilityZone}' + )); +} catch (BadResponseException $e) { + echo $e->getResponse(); +} diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index 580e77e65..13ed80a35 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -403,4 +403,13 @@ public function test_Stop() $this->addMockSubscriber(new \Guzzle\Http\Message\Response(202)); $this->assertEquals(202, $this->server->stop()->getStatusCode()); } + + public function test_Create_Availability_Zone() + { + $new = new PublicServer($this->service); + $new->setAvailabilityZone('AZ1'); + $obj = $new->CreateJson(); + + $this->assertEquals('AZ1', $obj->server->availability_zone); + } } diff --git a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp index 68c63da10..8474a182c 100644 --- a/tests/OpenCloud/Tests/Compute/_response/Extensions.resp +++ b/tests/OpenCloud/Tests/Compute/_response/Extensions.resp @@ -15,6 +15,14 @@ Server: Jetty(8.0.y.z-SNAPSHOT) "alias": "OS-DCF", "description": "Disk Management Extension." }, + { + "updated": "2013-01-30T00:00:00Z", + "name": "ExtendedAvailabilityZone", + "links": [], + "namespace": "http://docs.openstack.org/compute/ext/extended_availability_zone/api/v2", + "alias": "OS-EXT-AZ", + "description": "Extended Availability Zone support." + }, { "updated": "2013-02-19T00:00:00Z", "name": "ImageSize", From 5459af99d00fa678ca08c407939cb4596e50245b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Mar 2015 09:49:09 +0100 Subject: [PATCH 653/835] Fixing indentation. --- lib/OpenCloud/Compute/Resource/Server.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 490c285e6..0d47f5dec 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -738,8 +738,8 @@ protected function createJson() // Availability zone if (!empty($this->availabilityZone)) { - $this->checkExtension('OS-EXT-AZ'); - $server->availability_zone = $this->availabilityZone; + $this->checkExtension('OS-EXT-AZ'); + $server->availability_zone = $this->availabilityZone; } return (object) array('server' => $server); From 0120e361bc8408356d2f5a92859fe1b1d05f139b Mon Sep 17 00:00:00 2001 From: AdamMerrifield Date: Wed, 1 Apr 2015 08:09:19 -0400 Subject: [PATCH 654/835] Changed 'privateURL' to 'internalURL' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Object store documentation is displaying the incorrect url type. It says 'privateURL’ but it should be ‘internalURL’. --- doc/services/object-store/migrating-containers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/migrating-containers.rst b/doc/services/object-store/migrating-containers.rst index 22adb839b..da983af3e 100644 --- a/doc/services/object-store/migrating-containers.rst +++ b/doc/services/object-store/migrating-containers.rst @@ -68,12 +68,12 @@ You can access all this functionality by executing: It's advisable to do this process in a Cloud Server in one of the two -regions you're migrating to/from. This allows you to use ``privateURL`` +regions you're migrating to/from. This allows you to use ``internalURL`` as the third argument in the ``objectStoreService`` methods like this: .. code-block:: php - $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + $client->objectStoreService('cloudFiles', 'IAD', 'internalURL'); This will ensure that traffic between your server and your new IAD From ad3a09ca7689fdfb2267c76fd45f6a0ddb9be791 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 10:41:02 +0200 Subject: [PATCH 655/835] Fix dead links; fixes #579 --- doc/services/common/service-args.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/services/common/service-args.rst b/doc/services/common/service-args.rst index c1cc754b8..128986d91 100644 --- a/doc/services/common/service-args.rst +++ b/doc/services/common/service-args.rst @@ -3,9 +3,9 @@ default will be provided if you pass in ``null``. * ``{region}`` is the region the service will operate in. For Rackspace - users, you can select one of the following from the `supported regions page - `_. + users, you can select one of the following from the :doc:`supported regions page + `. -* ``{urlType}`` is the `type of URL `_ to use, depending on which +* ``{urlType}`` is the :doc:`type of URL ` to use, depending on which endpoints your catalog provides. If omitted, it will default to the public network. From 84dd00458fe2ef060bf551cab9805e5b0f7808b6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 14:08:54 +0200 Subject: [PATCH 656/835] Bump versions for development packages --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fe720466b..1f6725de1 100644 --- a/composer.json +++ b/composer.json @@ -32,10 +32,11 @@ "mikemccabe/json-patch-php": "~0.1" }, "require-dev" : { + "php": ">=5.4", "phpunit/phpunit": "4.3.*", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", - "apigen/apigen": "~2.8" + "apigen/apigen": "~4.0" } } From beefcd00014cb1cfc5c7455e739f9a8a5be83aef Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:06:34 +0200 Subject: [PATCH 657/835] Shift minimum PHP version to 5.4 --- README.md | 4 +++- composer.json | 5 ++--- doc/using-php-5.4.rst | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 doc/using-php-5.4.rst diff --git a/README.md b/README.md index 8ddd9f1b5..5f4d75da5 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,11 @@ so that you can still use the SDK with a pure OpenStack instance Requirements ------------ -* PHP >=5.3.3 +* PHP >=5.4 * cURL extension for PHP +**Note**: Since PHP 5.3 has reached [end of life](http://php.net/eol.php) and is no longer officially supported, we are moving to 5.4 as a minimum requirement. If upgrading is not an option and you still need a stable version of the SDK for 5.3, please follow [this guide](http://docs.php-opencloud.com/en/latest/using-php-5.3). + Installation ------------ You must install this library through Composer: diff --git a/composer.json b/composer.json index 1f6725de1..cebfc7280 100644 --- a/composer.json +++ b/composer.json @@ -26,13 +26,12 @@ } }, "require": { - "php" : ">=5.3.3", + "php" : ">=5.4", "guzzle/http" : "~3.8", "psr/log": "~1.0", "mikemccabe/json-patch-php": "~0.1" - }, + , "require-dev" : { - "php": ">=5.4", "phpunit/phpunit": "4.3.*", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", diff --git a/doc/using-php-5.4.rst b/doc/using-php-5.4.rst new file mode 100644 index 000000000..8790710f2 --- /dev/null +++ b/doc/using-php-5.4.rst @@ -0,0 +1,20 @@ +Using the SDK with PHP v5.3 +=========================== + +Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. + +The first way is by requiring it through the command line: + +.. code-block:: bash + + composer require rackspace/php-opencloud:1.12 + +The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: + +.. code-block:: json + + "require": { + "rackspace/php-opencloud": "~1.12" + } + +Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. From ea72c18d9900cc805912b4c574488dd61c86ce7b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:07:03 +0200 Subject: [PATCH 658/835] Remove 5.3 from build matrix --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bdd5f76af..78dd0003f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ php: - "5.6" - "5.5" - "5.4" - - "5.3" - hhvm sudo: false @@ -33,4 +32,4 @@ notifications: - shaunak.kashyap@rackspace.com env: - - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" \ No newline at end of file + - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" From 5abfbe8f76f05817e865ea70b3daa407604d515f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:07:58 +0200 Subject: [PATCH 659/835] Fix path --- doc/using-php-5.4.rst | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 doc/using-php-5.4.rst diff --git a/doc/using-php-5.4.rst b/doc/using-php-5.4.rst deleted file mode 100644 index 8790710f2..000000000 --- a/doc/using-php-5.4.rst +++ /dev/null @@ -1,20 +0,0 @@ -Using the SDK with PHP v5.3 -=========================== - -Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. - -The first way is by requiring it through the command line: - -.. code-block:: bash - - composer require rackspace/php-opencloud:1.12 - -The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: - -.. code-block:: json - - "require": { - "rackspace/php-opencloud": "~1.12" - } - -Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. From ec940c2cf17621f257ee3a0d0e61ff0904986e4f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 15:08:30 +0200 Subject: [PATCH 660/835] Fix path --- doc/using-php-5.3.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 doc/using-php-5.3.rst diff --git a/doc/using-php-5.3.rst b/doc/using-php-5.3.rst new file mode 100644 index 000000000..8790710f2 --- /dev/null +++ b/doc/using-php-5.3.rst @@ -0,0 +1,20 @@ +Using the SDK with PHP v5.3 +=========================== + +Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. + +The first way is by requiring it through the command line: + +.. code-block:: bash + + composer require rackspace/php-opencloud:1.12 + +The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: + +.. code-block:: json + + "require": { + "rackspace/php-opencloud": "~1.12" + } + +Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. From 1e007ac9fad7d9e4abe7b395c4a763b37426d6bf Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 2 Apr 2015 16:55:23 +0200 Subject: [PATCH 661/835] Add note to main install guide --- doc/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/index.rst b/doc/index.rst index 4994f0efb..3c47091cc 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -30,6 +30,10 @@ Read the :doc:`getting-started-with-openstack` or :doc:`getting-started-with-rackspace` to help you get started with basic Compute operations. +.. note:: + + If you are running PHP 5.3, please see our :doc:`using-php-5.3` guide. + Services -------- From 72460ae4f49feabecd3d8aca5799c7d5cab2bfe5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 29 Apr 2015 11:04:38 +0200 Subject: [PATCH 662/835] Bump version --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index 3e8504cfb..f71d254fa 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.13.0-alpha1'; + const VERSION = '1.14.0'; /** * @return string Indicate current SDK version. From d16851c31723e6ecea7283086bfd965946c53d5f Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 15:12:39 +0100 Subject: [PATCH 663/835] replace Logger::deprecated with PRS-3 Logger::warning --- lib/OpenCloud/Common/Collection.php | 3 ++- lib/OpenCloud/Common/Collection/ResourceIterator.php | 3 ++- lib/OpenCloud/Common/Log/Logger.php | 4 ++++ lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 6 ++++-- lib/OpenCloud/LoadBalancer/Service.php | 3 ++- lib/OpenCloud/ObjectStore/Service.php | 3 ++- lib/OpenCloud/OpenStack.php | 9 ++++++--- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index f2710639e..b6fe7471b 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -50,7 +50,8 @@ class Collection extends Base */ public function __construct($service, $class, array $array = array()) { - $service->getLogger()->deprecated(__METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder'); + $service->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); $this->setService($service); diff --git a/lib/OpenCloud/Common/Collection/ResourceIterator.php b/lib/OpenCloud/Common/Collection/ResourceIterator.php index 627ed78eb..a95455386 100644 --- a/lib/OpenCloud/Common/Collection/ResourceIterator.php +++ b/lib/OpenCloud/Common/Collection/ResourceIterator.php @@ -223,7 +223,8 @@ public function getElement($offset) */ public function first() { - Logger::newInstance()->deprecated(__METHOD__, 'getElement'); + Logger::newInstance()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'getElement')); return $this->getElement(0); } diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index 7eb48cdc0..56cb7cbf2 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -239,6 +239,10 @@ private function dispatch($message, $context) } } + /** + * @deprecated use warning for deprecated messages + * @see http://www.php-fig.org/psr/psr-3/ + */ public function deprecated($method, $new) { $string = sprintf('The %s method is deprecated, please use %s instead', $method, $new); diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index bd0742bcb..080dbb732 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -621,7 +621,8 @@ public function enableConnectionLogging($bool) */ public function connectionLogging() { - $this->getLogger()->deprecated(__METHOD__, 'hasConnectionLogging or enableConnectionLogging'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasConnectionLogging or enableConnectionLogging')); } /** @@ -663,7 +664,8 @@ public function enableContentCaching($bool) */ public function contentCaching() { - $this->getLogger()->deprecated(__METHOD__, 'hasContentCaching or setContentCaching'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasContentCaching or setContentCaching')); } /** diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 8ba659f87..c11022114 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -62,7 +62,8 @@ public function loadBalancerList($detail = true, array $filter = array()) */ public function billableLoadBalancer($id = null) { - $this->getLogger()->deprecated(__METHOD__, 'loadBalancer'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'loadBalancer')); return $this->resource('LoadBalancer', $id); } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index a2d01a479..ce3446880 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -174,7 +174,8 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ */ public function bulkDelete(array $paths) { - $this->getLogger()->deprecated(__METHOD__, '::batchDelete()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, '::batchDelete()')); return $this->executeBatchDeleteRequest($paths); } diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 6bc6eeba1..3511bc4f4 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -165,7 +165,8 @@ public function getTokenObject() */ public function setExpiration($expiration) { - $this->getLogger()->deprecated(__METHOD__, '::getTokenObject()->setExpires()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->setExpires()')); if ($this->getTokenObject()) { $this->getTokenObject()->setExpires($expiration); } @@ -178,7 +179,8 @@ public function setExpiration($expiration) */ public function getExpiration() { - $this->getLogger()->deprecated(__METHOD__, '::getTokenObject()->getExpires()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->getExpires()')); if ($this->getTokenObject()) { return $this->getTokenObject()->getExpires(); } @@ -290,7 +292,8 @@ public function getLogger() */ public function hasExpired() { - $this->getLogger()->deprecated(__METHOD__, 'getTokenObject()->hasExpired()'); + $this->getLogger()->warning(sprintf( + 'The %s method is deprecated, please use %s instead', __METHOD__, 'getTokenObject()->hasExpired()')); return $this->getTokenObject() && $this->getTokenObject()->hasExpired(); } From fd9fcefb026a6d989b9acce6c98252b803d53b0e Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 6 Jan 2015 17:41:36 +0100 Subject: [PATCH 664/835] move deprecation message to static function --- lib/OpenCloud/Common/Collection.php | 5 +++-- lib/OpenCloud/Common/Collection/ResourceIterator.php | 3 +-- lib/OpenCloud/Common/Log/Logger.php | 8 +++----- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 4 ++-- lib/OpenCloud/LoadBalancer/Service.php | 4 ++-- lib/OpenCloud/ObjectStore/Service.php | 4 ++-- lib/OpenCloud/OpenStack.php | 10 ++++------ tests/OpenCloud/Tests/Common/Log/LoggerTest.php | 8 ++++++++ 8 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/OpenCloud/Common/Collection.php b/lib/OpenCloud/Common/Collection.php index b6fe7471b..f15e83c47 100644 --- a/lib/OpenCloud/Common/Collection.php +++ b/lib/OpenCloud/Common/Collection.php @@ -17,6 +17,8 @@ namespace OpenCloud\Common; +use OpenCloud\Common\Log\Logger; + /** * @deprecated * @codeCoverageIgnore @@ -50,8 +52,7 @@ class Collection extends Base */ public function __construct($service, $class, array $array = array()) { - $service->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); + $service->getLogger()->warning(Logger::deprecated(__METHOD__, 'OpenCloud\Common\Collection\CollectionBuilder')); $this->setService($service); diff --git a/lib/OpenCloud/Common/Collection/ResourceIterator.php b/lib/OpenCloud/Common/Collection/ResourceIterator.php index a95455386..c0054b8d1 100644 --- a/lib/OpenCloud/Common/Collection/ResourceIterator.php +++ b/lib/OpenCloud/Common/Collection/ResourceIterator.php @@ -223,8 +223,7 @@ public function getElement($offset) */ public function first() { - Logger::newInstance()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'getElement')); + Logger::newInstance()->warning(Logger::deprecated(__METHOD__, 'getElement')); return $this->getElement(0); } diff --git a/lib/OpenCloud/Common/Log/Logger.php b/lib/OpenCloud/Common/Log/Logger.php index 56cb7cbf2..b054fc004 100644 --- a/lib/OpenCloud/Common/Log/Logger.php +++ b/lib/OpenCloud/Common/Log/Logger.php @@ -240,13 +240,11 @@ private function dispatch($message, $context) } /** - * @deprecated use warning for deprecated messages + * Helper method, use PSR-3 warning function for deprecation warnings * @see http://www.php-fig.org/psr/psr-3/ */ - public function deprecated($method, $new) + public static function deprecated($method, $new) { - $string = sprintf('The %s method is deprecated, please use %s instead', $method, $new); - - return $this->warning($string); + return sprintf('The %s method is deprecated, please use %s instead', $method, $new); } } diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index 080dbb732..cc3e85339 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -18,6 +18,7 @@ namespace OpenCloud\LoadBalancer\Resource; use OpenCloud\Common\Exceptions; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Resource\PersistentResource; use OpenCloud\DNS\Resource\HasPtrRecordsInterface; use OpenCloud\LoadBalancer\Enum\NodeCondition; @@ -621,8 +622,7 @@ public function enableConnectionLogging($bool) */ public function connectionLogging() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'hasConnectionLogging or enableConnectionLogging')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'hasConnectionLogging or enableConnectionLogging')); } /** diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index c11022114..93905a0d2 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -17,6 +17,7 @@ namespace OpenCloud\LoadBalancer; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\NovaService; /** @@ -62,8 +63,7 @@ public function loadBalancerList($detail = true, array $filter = array()) */ public function billableLoadBalancer($id = null) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'loadBalancer')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'loadBalancer')); return $this->resource('LoadBalancer', $id); } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index ce3446880..66735dea8 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -24,6 +24,7 @@ use OpenCloud\Common\Exceptions\InvalidArgumentError; use OpenCloud\Common\Http\Client; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\ServiceBuilder; use OpenCloud\ObjectStore\Constants\UrlType; use OpenCloud\ObjectStore\Resource\Container; @@ -174,8 +175,7 @@ public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ */ public function bulkDelete(array $paths) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, '::batchDelete()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::batchDelete()')); return $this->executeBatchDeleteRequest($paths); } diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 3511bc4f4..8e177135f 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -23,6 +23,7 @@ use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Http\Message\RequestSubscriber; use OpenCloud\Common\Lang; +use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\Catalog; use OpenCloud\Common\Service\ServiceBuilder; use OpenCloud\Identity\Resource\Tenant; @@ -165,8 +166,7 @@ public function getTokenObject() */ public function setExpiration($expiration) { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->setExpires()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->setExpires()')); if ($this->getTokenObject()) { $this->getTokenObject()->setExpires($expiration); } @@ -179,8 +179,7 @@ public function setExpiration($expiration) */ public function getExpiration() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead',__METHOD__, '::getTokenObject()->getExpires()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->getExpires()')); if ($this->getTokenObject()) { return $this->getTokenObject()->getExpires(); } @@ -292,8 +291,7 @@ public function getLogger() */ public function hasExpired() { - $this->getLogger()->warning(sprintf( - 'The %s method is deprecated, please use %s instead', __METHOD__, 'getTokenObject()->hasExpired()')); + $this->getLogger()->warning(Logger::deprecated(__METHOD__, 'getTokenObject()->hasExpired()')); return $this->getTokenObject() && $this->getTokenObject()->hasExpired(); } diff --git a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php index da06da6d0..c18f38c62 100644 --- a/tests/OpenCloud/Tests/Common/Log/LoggerTest.php +++ b/tests/OpenCloud/Tests/Common/Log/LoggerTest.php @@ -85,4 +85,12 @@ public function testOutputFailsWithIncorrectFile() $this->logger->emergency('Can anyone see this?'); } + + public function testDeprecationMessage() + { + $this->assertEquals( + 'The OpenCloud\Tests\Common\Log\LoggerTest::testDeprecationMessage method is deprecated, please use testMethod instead', + $this->logger->deprecated(__METHOD__, 'testMethod') + ); + } } From 5b5fc0fc3092f3f4b16c2beb0bbf0e830e75714b Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 7 Jan 2015 10:47:14 +0100 Subject: [PATCH 665/835] + inject Logger in service when available in Client * allow OpenStack Logger to be set via options array --- lib/OpenCloud/Common/Base.php | 8 +++++ .../Common/Service/CatalogService.php | 6 ++++ lib/OpenCloud/Identity/Service.php | 7 ++++ lib/OpenCloud/OpenStack.php | 12 +++++++ tests/OpenCloud/Tests/OpenStackTest.php | 35 +++++++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/lib/OpenCloud/Common/Base.php b/lib/OpenCloud/Common/Base.php index fd638cc81..82bf0061b 100644 --- a/lib/OpenCloud/Common/Base.php +++ b/lib/OpenCloud/Common/Base.php @@ -266,6 +266,14 @@ public function getLogger() return $this->logger; } + /** + * @return bool + */ + public function hasLogger() + { + return (null !== $this->logger); + } + /** * @deprecated */ diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index addcd170b..69e5c659d 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -20,8 +20,10 @@ use Guzzle\Http\ClientInterface; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Url; +use OpenCloud\Common\Base; use OpenCloud\Common\Exceptions; use OpenCloud\Common\Http\Message\Formatter; +use OpenCloud\OpenStack; use Symfony\Component\EventDispatcher\EventSubscriberInterface; abstract class CatalogService extends AbstractService @@ -71,6 +73,10 @@ abstract class CatalogService extends AbstractService */ public function __construct(ClientInterface $client, $type = null, $name = null, $region = null, $urlType = null) { + if (($client instanceof Base || $client instanceof OpenStack) && $client->hasLogger()) { + $this->setLogger($client->getLogger()); + } + $this->setClient($client); $this->name = $name ? : static::DEFAULT_NAME; diff --git a/lib/OpenCloud/Identity/Service.php b/lib/OpenCloud/Identity/Service.php index 9c90d2811..4e34f1e24 100644 --- a/lib/OpenCloud/Identity/Service.php +++ b/lib/OpenCloud/Identity/Service.php @@ -18,11 +18,13 @@ namespace OpenCloud\Identity; use Guzzle\Http\ClientInterface; +use OpenCloud\Common\Base; use OpenCloud\Common\Collection\PaginatedIterator; use OpenCloud\Common\Collection\ResourceIterator; use OpenCloud\Common\Http\Message\Formatter; use OpenCloud\Common\Service\AbstractService; use OpenCloud\Identity\Constants\User as UserConst; +use OpenCloud\OpenStack; /** * Class responsible for working with Rackspace's Cloud Identity service. @@ -40,6 +42,11 @@ class Service extends AbstractService public static function factory(ClientInterface $client) { $identity = new self(); + + if (($client instanceof Base || $client instanceof OpenStack) && $client->hasLogger()) { + $identity->setLogger($client->getLogger()); + } + $identity->setClient($client); $identity->setEndpoint(clone $client->getAuthUrl()); diff --git a/lib/OpenCloud/OpenStack.php b/lib/OpenCloud/OpenStack.php index 8e177135f..e970b9912 100644 --- a/lib/OpenCloud/OpenStack.php +++ b/lib/OpenCloud/OpenStack.php @@ -79,6 +79,10 @@ class OpenStack extends Client public function __construct($url, array $secret, array $options = array()) { + if (isset($options['logger']) && $options['logger'] instanceof LoggerInterface) { + $this->setLogger($options['logger']); + } + $this->setSecret($secret); $this->setAuthUrl($url); @@ -286,6 +290,14 @@ public function getLogger() return $this->logger; } + /** + * @return bool + */ + public function hasLogger() + { + return (null !== $this->logger); + } + /** * @deprecated */ diff --git a/tests/OpenCloud/Tests/OpenStackTest.php b/tests/OpenCloud/Tests/OpenStackTest.php index f38f3777b..8ff20892a 100644 --- a/tests/OpenCloud/Tests/OpenStackTest.php +++ b/tests/OpenCloud/Tests/OpenStackTest.php @@ -184,4 +184,39 @@ public function test_Import_Credentials_Numeric_Tenant() $this->assertEquals('{expiration}', $this->client->getExpiration()); $this->assertEquals($randomNumericTenant, $this->client->getTenant()); } + + public function testLoggerServiceInjection() + { + // Create a new client, pass stub via constructor options argument + $stubLogger = $this->getMock('Psr\Log\NullLogger'); + $client = new OpenStack(Rackspace::US_IDENTITY_ENDPOINT, $this->credentials, array( + 'logger' => $stubLogger, + )); + $client->addSubscriber(new MockSubscriber()); + + // Test all OpenStack factory methods on proper Logger service injection + $service = $client->objectStoreService('cloudFiles', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $service->getCdnService(); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->computeService('cloudServersOpenStack', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->orchestrationService(null, 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->volumeService('cloudBlockStorage', 'DFW'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->identityService(); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->imageService('cloudImages', 'IAD'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + + $service = $client->networkingService(null, 'IAD'); + $this->assertContains("Mock_NullLogger", get_class($service->getLogger())); + } } From 208a9b71953e9c56ef8c5a214b53c86ea4b7cc2d Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:22:14 +0100 Subject: [PATCH 666/835] Update getting-started.md added information about injecting `Logger` in the client --- docs/getting-started.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 48f99fb86..f47daf5cf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -55,6 +55,11 @@ You can see in the first example that the constant `Rackspace::US_IDENTITY_ENDPO Rackspace's identity endpoint (`https://identity.api.rackspacecloud.com/v2.0/`). Another difference is that Rackspace uses API key for authentication, whereas OpenStack uses a generic password. +#### 1.2 Logger injection +As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. + +Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. + ### 2. Pick what service you want to use In this case, we want to use the Compute (Nova) service: @@ -175,4 +180,4 @@ $callback = function($server) { $server->waitFor(ServerState::ACTIVE, 600, $callback); ``` So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. \ No newline at end of file +callback function is executed - which in this case just logs some output. From 7f04cf2a8dac8b00b67f6dca704ec331ae054108 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:26:47 +0100 Subject: [PATCH 667/835] Update Clients.md also add `Logger` injection to Clients userguide --- docs/userguide/Clients.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 9a7efec8e..70f1a8131 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -11,7 +11,7 @@ Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\R 3. `OpenCloud\OpenStack` 4. `OpenCloud\Rackspace` -## Initializing a client +## 1. Initializing a client ### Rackspace @@ -44,7 +44,29 @@ $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( )); ``` -## Authentication +#### 1.2 Logger injection +As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. + +Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. + +To inject a `LoggerInterface` compatible logger into a new `Client`: + +```php +use Monolog\Logger; +use OpenCloud\OpenStack; + +// create a log channel +$log = new Logger('name'); + +$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' +), array( + 'logger' => $log, +)); +``` + +## 2. Authentication The Client does not automatically authenticate against the API on object creation - it waits for an API call. When this happens, it checks whether the current "token" has expired, and (re-)authenticates if necessary. @@ -56,7 +78,7 @@ $client->authenticate(); If the credentials are incorrect, a `401` error will be returned. If credentials are correct, a `200` status is returned with your Service Catalog. -## Service Catalog +## 3. Service Catalog The Service Catalog is returned on successful authentication, and is composed of all the different API services available to the current tenant. All of this functionality is encapsulated in the `Catalog` object, which allows you greater control and interactivity. @@ -86,7 +108,7 @@ foreach ($catalog->getItems() as $catalogItem) { As you can see, you have access to each Service's name, type and list of endpoints. Each endpoint provides access to the specific region, along with its public and private endpoint URLs. -## Default HTTP headers +## 4. Default HTTP headers To set default HTTP headers: @@ -94,6 +116,6 @@ To set default HTTP headers: $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); ``` -## Other functionality +## 5. Other functionality -For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). \ No newline at end of file +For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). From 5cb4ac7bf887c206b806778ba1df39485e619999 Mon Sep 17 00:00:00 2001 From: Richard van Laak Date: Wed, 7 Jan 2015 15:28:26 +0100 Subject: [PATCH 668/835] Link getting-started.md to the clients userguide --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index f47daf5cf..e661e0ce8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -58,7 +58,7 @@ uses API key for authentication, whereas OpenStack uses a generic password. #### 1.2 Logger injection As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. -Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. +Prerequisities and usage example can be found in [the Clients userguide](/docs/userguide/Clients.md#12-logger-injection) ### 2. Pick what service you want to use From b153504bbcd130918697be7919a70dd0441c08ef Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:12:13 +0100 Subject: [PATCH 669/835] Add mock HTTP responses --- .../_response/LoadBalancers1.resp | 80 +++++++++++++++++++ .../_response/LoadBalancers2.resp | 80 +++++++++++++++++++ .../_response/LoadBalancers3.resp | 34 ++++++++ 3 files changed, 194 insertions(+) create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp create mode 100644 tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp new file mode 100644 index 000000000..1827f2b44 --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers1.resp @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site1", + "id":1, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":3, + "virtualIps":[ + { + "id":403, + "address":"206.55.130.1", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site2", + "id":2, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site3", + "id":3, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp new file mode 100644 index 000000000..2ab7b2381 --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers2.resp @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site3", + "id":3, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site4", + "id":4, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + }, + { + "name":"lb-site5", + "id":5, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file diff --git a/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp new file mode 100644 index 000000000..554a4430d --- /dev/null +++ b/tests/OpenCloud/Tests/LoadBalancer/_response/LoadBalancers3.resp @@ -0,0 +1,34 @@ +HTTP/1.1 200 OK +Status: 200 OK +Date: Thu, 28 Jul 2011 21:54:21 GMT +X-API-VERSION: 1.0.17 +Content-Type: application/json +Content-Length: 1975 + +{ + "loadBalancers":[ + { + "name":"lb-site5", + "id":5, + "protocol":"HTTP", + "port":80, + "algorithm":"RANDOM", + "status":"ACTIVE", + "nodeCount":4, + "virtualIps":[ + { + "id":401, + "address":"206.55.130.2", + "type":"PUBLIC", + "ipVersion":"IPV4" + } + ], + "created":{ + "time":"2010-11-30T03:23:42Z" + }, + "updated":{ + "time":"2010-11-30T03:23:44Z" + } + } + ] +} \ No newline at end of file From 4380e4ec0e58ce213f5717a82bd5f4f7ce987faf Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:22 +0100 Subject: [PATCH 670/835] Decouple the process of retrieving last element from setting marker --- lib/OpenCloud/Common/Collection/PaginatedIterator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/OpenCloud/Common/Collection/PaginatedIterator.php b/lib/OpenCloud/Common/Collection/PaginatedIterator.php index a0628993d..ef827ac80 100644 --- a/lib/OpenCloud/Common/Collection/PaginatedIterator.php +++ b/lib/OpenCloud/Common/Collection/PaginatedIterator.php @@ -145,7 +145,11 @@ public function updateMarkerToCurrent() } $element = $this->elements[$this->position]; + $this->setMarkerFromElement($element); + } + protected function setMarkerFromElement($element) + { $key = $this->getOption('key.marker'); if (isset($element->$key)) { From d4fa19512fbb77afeea34a32fdd42929614e65cd Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:39 +0100 Subject: [PATCH 671/835] Add new iterator to handle differences --- .../Collection/LoadBalancerIterator.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php new file mode 100644 index 000000000..f554c6512 --- /dev/null +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -0,0 +1,43 @@ +getQuery(); + $query['limit'] = $query['limit'] + 1; + $url->setQuery($query); + + return $url; + } + + public function updateMarkerToCurrent() + { + $this->setMarkerFromElement($this->nextElement); + } + + public function parseResponseBody($body) + { + $response = parent::parseResponseBody($body); + + if (count($response) >= $this->getOption('limit.page')) { + // Save last element (we will need it for the next marker) + $this->nextElement = end($response); + + // Since we previously asked for n+1 elements, pop the unwanted element + array_pop($response); + reset($response); + } + + return $response; + } +} \ No newline at end of file From f9dda99147f6d81b9bed1e0f1c3213d00e220086 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 8 Jan 2015 16:13:51 +0100 Subject: [PATCH 672/835] Implement into service and refactor tests --- lib/OpenCloud/LoadBalancer/Service.php | 12 ++++- .../Tests/LoadBalancer/ServiceTest.php | 48 ++++++++++++++----- tests/OpenCloud/Tests/OpenCloudTestCase.php | 13 ++--- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 93905a0d2..6f5052f2c 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -19,6 +19,7 @@ use OpenCloud\Common\Log\Logger; use OpenCloud\Common\Service\NovaService; +use OpenCloud\LoadBalancer\Collection\LoadBalancerIterator; /** * Class that encapsulates the Rackspace Cloud Load Balancers service @@ -51,11 +52,20 @@ public function loadBalancer($id = null) */ public function loadBalancerList($detail = true, array $filter = array()) { + $options = $this->makeResourceIteratorOptions($this->resolveResourceClass('LoadBalancer')); + + if (isset($filter['limit'])) { + $options['limit.page'] = $filter['limit']; + unset($filter['limit']); + } + $url = $this->getUrl(); $url->addPath(Resource\LoadBalancer::resourceName()); $url->setQuery($filter); - return $this->resourceList('LoadBalancer', $url); + $options += array('baseUrl' => $url, 'key.marker' => 'id'); + + return LoadBalancerIterator::factory($this, $options); } /** diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 6bd047064..2d01e2f93 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -27,15 +27,13 @@ namespace OpenCloud\Tests\LoadBalancer; -class ServiceTest extends \OpenCloud\Tests\OpenCloudTestCase -{ - private $service; - - public function setupObjects() - { - $this->service = $this->getClient()->loadBalancerService('cloudLoadBalancers', 'DFW', 'publicURL'); - } +use Guzzle\Http\Message\Response; +use Guzzle\Plugin\Mock\MockPlugin; +use OpenCloud\Rackspace; +use OpenCloud\Tests\MockSubscriber; +class ServiceTest extends LoadBalancerTestCase +{ public function test__construct() { $this->assertInstanceOf( @@ -52,12 +50,36 @@ public function testLoadBalancer() ); } - public function testLoadBalancerList() + public function test_Listing_Load_Balancers() { - $this->assertInstanceOf( - self::COLLECTION_CLASS, - $this->service->loadBalancerList() - ); + // Load JSON HTTP data + $authData = file_get_contents($this->getTestFilePath('Auth', './')); + $data1 = file_get_contents($this->getTestFilePath('LoadBalancers1')); + $data2 = file_get_contents($this->getTestFilePath('LoadBalancers2')); + $data3 = file_get_contents($this->getTestFilePath('LoadBalancers3')); + + // Populate mock response queue + $mock = new MockPlugin(); + $mock->addResponse(Response::fromMessage($authData)) + ->addResponse(Response::fromMessage($data1)) + ->addResponse(Response::fromMessage($data2)) + ->addResponse(Response::fromMessage($data3)); + + // We need to define our own setup because *jazz hands* + $client = $this->newClient(); + $client->addSubscriber($mock); + $service = $client->loadBalancerService(null, 'IAD'); + + // Ensure that a series of paginated calls return a holistic collection + $lbs = $service->loadBalancerList(false, array('limit' => 2)); + $ids = array(); + foreach ($lbs as $lb) { + $ids[] = $lb->id; + } + + // Check our assumptions + $this->isCollection($lbs); + $this->assertEquals($ids, array(1,2,3,4,5)); } public function testBillableLoadBalancer() diff --git a/tests/OpenCloud/Tests/OpenCloudTestCase.php b/tests/OpenCloud/Tests/OpenCloudTestCase.php index 628881fbb..8d7582c1f 100644 --- a/tests/OpenCloud/Tests/OpenCloudTestCase.php +++ b/tests/OpenCloud/Tests/OpenCloudTestCase.php @@ -38,17 +38,10 @@ abstract class OpenCloudTestCase extends \PHPUnit_Framework_TestCase public function newClient() { - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + return new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( 'username' => 'foo', 'apiKey' => 'bar' )); - - $client->addSubscriber(new MockSubscriber()); - //$client->addSubscriber(LogPlugin::getDebugPlugin()); - - $client->authenticate(); - - return $client; } public function getClient() @@ -59,6 +52,10 @@ public function getClient() public function setUp() { $this->client = $this->newClient(); + + $this->client->addSubscriber(new MockSubscriber()); + $this->client->authenticate(); + $this->setupObjects(); $this->handleMockSubscribers(); } From 5473f31a343e0e104c3b177db09fd9b6e4a70131 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 9 Jan 2015 11:25:08 +0100 Subject: [PATCH 673/835] Appease PSR-2 gods --- lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php | 2 +- tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php index f554c6512..d6326048e 100644 --- a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -40,4 +40,4 @@ public function parseResponseBody($body) return $response; } -} \ No newline at end of file +} diff --git a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php index 2d01e2f93..9beb0c620 100644 --- a/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php +++ b/tests/OpenCloud/Tests/LoadBalancer/ServiceTest.php @@ -79,7 +79,7 @@ public function test_Listing_Load_Balancers() // Check our assumptions $this->isCollection($lbs); - $this->assertEquals($ids, array(1,2,3,4,5)); + $this->assertEquals($ids, array(1, 2, 3, 4, 5)); } public function testBillableLoadBalancer() From 7a5efc45c97f6e4c7036c912cb20ca5a71f97536 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 9 Jan 2015 11:32:29 +0100 Subject: [PATCH 674/835] Attempt to fix 5.3 segfault --- phpunit.xml.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3d4b02cd2..c2aeed2d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -26,8 +26,8 @@ - - + + From d2cce750dfe19b6400c975a399231327088ffcaf Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 13 Jan 2015 12:36:20 +0100 Subject: [PATCH 675/835] Tweaks based on code review --- .../LoadBalancer/Collection/LoadBalancerIterator.php | 8 ++------ lib/OpenCloud/LoadBalancer/Service.php | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php index d6326048e..7febf1af3 100644 --- a/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php +++ b/lib/OpenCloud/LoadBalancer/Collection/LoadBalancerIterator.php @@ -30,12 +30,8 @@ public function parseResponseBody($body) $response = parent::parseResponseBody($body); if (count($response) >= $this->getOption('limit.page')) { - // Save last element (we will need it for the next marker) - $this->nextElement = end($response); - - // Since we previously asked for n+1 elements, pop the unwanted element - array_pop($response); - reset($response); + // Pop last element and save (we will need it for the next marker) + $this->nextElement = array_pop($response); } return $response; diff --git a/lib/OpenCloud/LoadBalancer/Service.php b/lib/OpenCloud/LoadBalancer/Service.php index 6f5052f2c..7f8d01f74 100644 --- a/lib/OpenCloud/LoadBalancer/Service.php +++ b/lib/OpenCloud/LoadBalancer/Service.php @@ -63,7 +63,7 @@ public function loadBalancerList($detail = true, array $filter = array()) $url->addPath(Resource\LoadBalancer::resourceName()); $url->setQuery($filter); - $options += array('baseUrl' => $url, 'key.marker' => 'id'); + $options = array_merge($options, array('baseUrl' => $url, 'key.marker' => 'id')); return LoadBalancerIterator::factory($this, $options); } From 25bd5e7b3fa1f02392e886d8935f322484fc84b1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Tue, 13 Jan 2015 17:28:57 -0800 Subject: [PATCH 676/835] Add comment to clarify intent of code. --- lib/OpenCloud/Common/Service/CatalogItem.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/OpenCloud/Common/Service/CatalogItem.php b/lib/OpenCloud/Common/Service/CatalogItem.php index b55274762..5d0e44d1a 100644 --- a/lib/OpenCloud/Common/Service/CatalogItem.php +++ b/lib/OpenCloud/Common/Service/CatalogItem.php @@ -141,6 +141,8 @@ public function getEndpoints() public function getEndpointFromRegion($region) { foreach ($this->endpoints as $endpoint) { + // Return the endpoint if it is regionless OR if the endpoint's + // region matches the $region supplied by the caller. if (!isset($endpoint->region) || $endpoint->region == $region) { return $endpoint; } From a379aed93318dabf45e500948f18f90e59c4bac4 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 14 Jan 2015 03:41:21 -0800 Subject: [PATCH 677/835] Clarifying where to place autoloader require statement. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b05d1d58..42948e8c3 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,15 @@ php composer.phar require rackspace/php-opencloud ``` Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): +namespaces). To do this, place the following line of PHP code at the top of your application's PHP files: ```php require 'vendor/autoload.php'; ``` +**Note**: this assumes your application's PHP files are located in the same folder as `vendor/`. If your files are located +elsewhere, please supply the path to `vendor/autoload.php` in the `require` statement above. + And you're ready to go! You can also check out the [Getting Started guide](docs/getting-started.md) for a quick tutorial. From fd0b51ec7bbb57a7cac62b35d343b78d27561e12 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 10:42:32 +0100 Subject: [PATCH 678/835] Fix CDN container population when iterating --- lib/OpenCloud/ObjectStore/AbstractService.php | 16 --------- lib/OpenCloud/ObjectStore/CDNService.php | 35 +++++++++++++++++++ lib/OpenCloud/ObjectStore/Service.php | 13 +++++++ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/AbstractService.php b/lib/OpenCloud/ObjectStore/AbstractService.php index 8e24c0b96..8f92874f8 100644 --- a/lib/OpenCloud/ObjectStore/AbstractService.php +++ b/lib/OpenCloud/ObjectStore/AbstractService.php @@ -29,22 +29,6 @@ abstract class AbstractService extends CatalogService const MAX_OBJECT_NAME_LEN = 1024; const MAX_OBJECT_SIZE = 5102410241025; - /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. - * - * @param array $filter - * @return Collection - */ - public function listContainers(array $filter = array()) - { - $filter['format'] = 'json'; - - $class = ($this instanceof Service) ? 'Container' : 'CDNContainer'; - - return $this->resourceList($class, $this->getUrl(null, $filter), $this); - } - /** * @return Resource\Account */ diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index e6b20cd5c..7d38c9956 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -16,6 +16,8 @@ */ namespace OpenCloud\ObjectStore; +use OpenCloud\ObjectStore\Resource\CDNContainer; +use OpenCloud\ObjectStore\Resource\ContainerMetadata; /** * This is the CDN version of the ObjectStore service. @@ -24,4 +26,37 @@ class CDNService extends AbstractService { const DEFAULT_NAME = 'cloudFilesCDN'; const DEFAULT_TYPE = 'rax:object-cdn'; + + /** + * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular + * service, normal containers are returned. + * + * @param array $filter + * @return CDNContainer + */ + public function listContainers(array $filter = array()) + { + $filter['format'] = 'json'; + return $this->resourceList('CDNContainer', $this->getUrl(null, $filter), $this); + } + + public function cdnContainer($data) + { + $container = new CDNContainer($this, $data); + + $metadata = new ContainerMetadata(); + $metadata->setArray(array( + 'Streaming-Uri' => $data->cdn_streaming_uri, + 'Ios-Uri' => $data->cdn_ios_uri, + 'Ssl-Uri' => $data->cdn_ssl_uri, + 'Enabled' => $data->cdn_enabled, + 'Ttl' => $data->ttl, + 'Log-Retention' => $data->log_retention, + 'Uri' => $data->cdn_uri, + )); + + $container->setMetadata($metadata); + + return $container; + } } diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index 66735dea8..df9ffa6c8 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -66,6 +66,19 @@ public function getCdnService() return $this->cdnService; } + /** + * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular + * service, normal containers are returned. + * + * @param array $filter + * @return Container + */ + public function listContainers(array $filter = array()) + { + $filter['format'] = 'json'; + return $this->resourceList('Container', $this->getUrl(null, $filter), $this); + } + /** * @param $data * @return Container From 45302cfff1e297bbb50fcc36e92660fa3e6a9e61 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 10:50:13 +0100 Subject: [PATCH 679/835] Minor fixes --- lib/OpenCloud/ObjectStore/CDNService.php | 6 +++--- lib/OpenCloud/ObjectStore/Service.php | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index 7d38c9956..d45fc494d 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -16,6 +16,7 @@ */ namespace OpenCloud\ObjectStore; + use OpenCloud\ObjectStore\Resource\CDNContainer; use OpenCloud\ObjectStore\Resource\ContainerMetadata; @@ -28,11 +29,10 @@ class CDNService extends AbstractService const DEFAULT_TYPE = 'rax:object-cdn'; /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. + * List CDN-enabled containers. * * @param array $filter - * @return CDNContainer + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function listContainers(array $filter = array()) { diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index df9ffa6c8..f0e1c3dc9 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -67,11 +67,10 @@ public function getCdnService() } /** - * List all available containers. If called by a CDN service, it returns CDN-enabled; if called by a regular - * service, normal containers are returned. + * List all available containers. * * @param array $filter - * @return Container + * @return \OpenCloud\Common\Collection\PaginatedIterator */ public function listContainers(array $filter = array()) { From d69194883abd950d4daf4444ad774c41415f1013 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 15 Jan 2015 15:13:34 +0100 Subject: [PATCH 680/835] [ci skip] Adding better documentation --- docs/userguide/ObjectStore/CDN/Container.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 3d06216fc..642ee968d 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -21,7 +21,16 @@ $cdn = $container->getCdn(); ## List CDN-enabled container To list CDN-only containers, follow the same operation for Storage which lists all containers. The only difference is -which service object you execute the method on. +which service object you execute the method on: + +```php +$cdnService = $service->getCdnService(); +$cdnContainers = $cdnService->listContainers(); + +foreach ($cdnContainers as $cdnContainer) { + +} +``` ## CDN-enable and -disable a container @@ -66,4 +75,4 @@ To enable and disable logging for your CDN: ```php $cdn->enableCdnLogging(); $cdn->disableCdnLogging(); -``` \ No newline at end of file +``` From 5cb0fd8db94ab91388cbe489e8515ddd6a6a6d0f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:24:42 -0800 Subject: [PATCH 681/835] Generate and publish SDK API reference from Travis builds of master branch. --- .travis.yml | 3 +++ composer.json | 3 ++- docs/generate.sh | 50 +++++++++++++++++++++++++++++++----------------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d45afa70..fe792bec4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,6 @@ notifications: - jamie.hannaford@rackspace.com - glen.campbell@rackspace.com - shaunak.kashyap@rackspace.com + +env: + - secure: "bdrUeYb3nSGgBB+QtDZxUHVPw6B/wjb3KXLm8TgonWrQm4GPeWKK29qhmDnFZmQjwQPfuebe7wAk1ZxGoZKbEiELVpJJ+8XYVOt6W/6V53H31JL6FqiIE5+7qBwDe+9ziveM6GcTXHT1GI5mUeACIbeBDPZaNubIJH3U6MPim64=" \ No newline at end of file diff --git a/composer.json b/composer.json index 9c1752463..abaae2a78 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", - "fabpot/php-cs-fixer": "1.0.*@dev" + "fabpot/php-cs-fixer": "1.0.*@dev", + "apigen/apigen": "~4.0" } } diff --git a/docs/generate.sh b/docs/generate.sh index 01a43a58e..3970fe309 100644 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -1,25 +1,39 @@ -#!/bin/bash -# (c)2013 Rackspace Hosting. See COPYING for license. +#!/bin/sh -DOC_DIR=docs/api -LIB_DIR=lib -BIN_FILE=vendor/bin/apigen.php +# Script to be used by Travis CI builds to generate the php-opencloud SDK API +# reference and publish it to the gh-pages branch of the rackerlabs/php-opencloud +# repository. -if [ ! -f $BIN_FILE ]; then - rm composer.lock - php composer.phar require apigen/apigen:dev-master --dev -fi +SOURCE_DIR=lib +WORK_DIR=build/api +API_DOCS_DIR=docs/api +REPO_REMOTE_URL=https://$GH_TOKEN@github.com/rackspace/php-opencloud -if [ ! -d $DOC_DIR ]; then - mkdir $DOC_DIR +# We want our generated API reference to reflect what is +# on the master branch. So if we aren't currently on +# the master branch, or we aren't part of a PR targetted +# to the master branch, do nothing. +if [ "$TRAVIS_BRANCH" != "master" ]; then + exit 0 fi -if [ ! -d docs ]; then - echo "No docs/ directory found; run this script from the top directory" - exit; -fi +# Generate the API references +rm -rf $API_DOCS_DIR && \ +./vendor/bin/apigen generate \ + --source $SOURCE_DIR \ + --destination $WORK_DIR + +# Switch the branch to gh-pages +git checkout gh-pages + +# Commit the generated API references +rm -rf $API_DOCS_DIR +mv $WORK_DIR $API_DOCS_DIR +git add -f $API_DOCS_DIR +git commit -m "Re-generated API documentation" -rm -rf DOCS_DIR +# Push to the remote gh-pages branch so +# changes show up on php-opencloud.com +git push $REPO_REMOTE_URL gh-pages -# regenerate all the docs! -php $BIN_FILE -s $LIB_DIR -d $DOC_DIR --title="PHP OpenCloud API" --groups="namespaces" --download --progressbar \ No newline at end of file +git checkout master From 9f47ec524237a127c109ae9b101e10c5cc6b9d40 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:46:19 -0800 Subject: [PATCH 682/835] Using an older version of apigen that works with PHP 5.3. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index abaae2a78..5fdc047a5 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,6 @@ "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", - "apigen/apigen": "~4.0" + "apigen/apigen": "~2.8" } } From ebcec94cfdfed31a6815de66e080621332a55afe Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 15:59:52 -0800 Subject: [PATCH 683/835] Making generation+publish script executable. --- docs/generate.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 docs/generate.sh diff --git a/docs/generate.sh b/docs/generate.sh old mode 100644 new mode 100755 From 2464ef7e165e0fd5b9ae255be1c3659c9b2b3374 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:11:02 -0800 Subject: [PATCH 684/835] Pull in latest changes before making more changes. --- docs/generate.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generate.sh b/docs/generate.sh index 3970fe309..ed06cb3a6 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -25,6 +25,7 @@ rm -rf $API_DOCS_DIR && \ # Switch the branch to gh-pages git checkout gh-pages +git pull $REPO_REMOTE_URL gh-pages # Commit the generated API references rm -rf $API_DOCS_DIR From 772998e7cce64adc80bacac74393b1f1792890b6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:14:17 -0800 Subject: [PATCH 685/835] Adding error checking if the pull/merge failed. --- docs/generate.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/generate.sh b/docs/generate.sh index ed06cb3a6..a27c6e404 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -27,6 +27,10 @@ rm -rf $API_DOCS_DIR && \ git checkout gh-pages git pull $REPO_REMOTE_URL gh-pages +if [ $? -ne 0 ]; then + exit 1 +fi + # Commit the generated API references rm -rf $API_DOCS_DIR mv $WORK_DIR $API_DOCS_DIR From 886a95832d1c2c21730a7088a36dcd77dda6e363 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 21 Jan 2015 16:20:58 -0800 Subject: [PATCH 686/835] Attempt merge commit if necessary. --- docs/generate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generate.sh b/docs/generate.sh index a27c6e404..d7965bfb7 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -25,7 +25,7 @@ rm -rf $API_DOCS_DIR && \ # Switch the branch to gh-pages git checkout gh-pages -git pull $REPO_REMOTE_URL gh-pages +git pull --commit $REPO_REMOTE_URL gh-pages if [ $? -ne 0 ]; then exit 1 From 6f23936ff0d62cd570c5038953e116cc5b42b579 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 23 Jan 2015 03:48:41 -0800 Subject: [PATCH 687/835] Removing unnecessary line. --- docs/generate.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/generate.sh b/docs/generate.sh index d7965bfb7..2c88f3eca 100755 --- a/docs/generate.sh +++ b/docs/generate.sh @@ -18,7 +18,6 @@ if [ "$TRAVIS_BRANCH" != "master" ]; then fi # Generate the API references -rm -rf $API_DOCS_DIR && \ ./vendor/bin/apigen generate \ --source $SOURCE_DIR \ --destination $WORK_DIR From df95b789f9f9142e8d3691ba77d038326d7cd6a0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 23 Jan 2015 04:17:32 -0800 Subject: [PATCH 688/835] Adding apigen config file. --- apigen.neon | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 apigen.neon diff --git a/apigen.neon b/apigen.neon new file mode 100644 index 000000000..1907d65f4 --- /dev/null +++ b/apigen.neon @@ -0,0 +1,3 @@ +templateTheme: bootstrap + +accessLevels: [public] From 545c667033f665646c8fe0cc235e5abc806c1fc5 Mon Sep 17 00:00:00 2001 From: Glen Campbell Date: Sat, 7 Feb 2015 11:25:26 -0600 Subject: [PATCH 689/835] Docs said 'COMPLETE' but code uses 'COMPLETED' --- docs/userguide/DNS/Domains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index 827020256..f0d94efee 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -90,7 +90,7 @@ This call provides the BIND (Berkeley Internet Name Domain) 9 formatted contents ```php $asyncResponse = $domain->export(); -$body = $asyncResponse->waitFor('COMPLETE'); +$body = $asyncResponse->waitFor('COMPLETED'); echo $body['contents']; ``` From f5dfde5cb9c360e88cf564de362712e5c23d4247 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 14:20:34 +0100 Subject: [PATCH 690/835] Adding explanation for prefixing user-agents --- docs/userguide/Clients.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 70f1a8131..6f1e35075 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -116,6 +116,26 @@ To set default HTTP headers: $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); ``` +## User agents + +php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: + +> OpenCloud/xxx cURL/yyy PHP/zzz + +where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: + +```php +$client->setUserAgent('MyCustomUserAgent'); +``` + +If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: + +```php +$client->setUserAgent('MyPrefix', true); +``` + +where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. + ## 5. Other functionality For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). From 6e2e78626c782eb807b1409aeb30b4d187aac025 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 15:03:22 +0100 Subject: [PATCH 691/835] Install entire Guzzle package --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 5fdc047a5..fe720466b 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,6 @@ }, "require-dev" : { "phpunit/phpunit": "4.3.*", - "guzzle/guzzle": "~3.8", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", From 37ce5b3c95e987253ce63ae99569fcafaaa554cb Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 25 Feb 2015 20:25:43 +0100 Subject: [PATCH 692/835] Add example outputs --- docs/userguide/Clients.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 6f1e35075..c7270d69e 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -120,7 +120,7 @@ $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: -> OpenCloud/xxx cURL/yyy PHP/zzz +> User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: @@ -128,12 +128,20 @@ where `xxx` is the current version of the SDK, `yyy` is the current version of c $client->setUserAgent('MyCustomUserAgent'); ``` +which will result in: + +> User-Agent: MyCustomUserAgent + If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: ```php $client->setUserAgent('MyPrefix', true); ``` +which will result in: + +> User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. ## 5. Other functionality From e2433c00a57c0a700b6715a7538c628868f9b4e1 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 25 Feb 2015 15:17:22 -0800 Subject: [PATCH 693/835] Return array of DataObjects if such is requested. --- .../ObjectStore/Resource/Container.php | 23 +++++++++++++++---- .../ObjectStore/Resource/ContainerTest.php | 14 +++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 18e541bc7..ef8f27e68 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -30,6 +30,7 @@ use OpenCloud\ObjectStore\Exception\ObjectNotFoundException; use OpenCloud\ObjectStore\Upload\DirectorySync; use OpenCloud\ObjectStore\Upload\TransferBuilder; +use OpenCloud\ObjectStore\Enum\ReturnType; /** * A container is a storage compartment for your data and provides a way for you @@ -475,7 +476,7 @@ public function uploadObject($name, $data, array $headers = array()) * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError * @return \Guzzle\Http\Message\Response */ - public function uploadObjects(array $files, array $commonHeaders = array()) + public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY) { $requests = $entities = array(); @@ -514,11 +515,23 @@ public function uploadObjects(array $files, array $commonHeaders = array()) $responses = $this->getClient()->send($requests); - foreach ($entities as $entity) { - $entity->close(); + if (ReturnType::RESPONSE_ARRAY === $returnType) { + foreach ($entities as $entity) { + $entity->close(); + } + return $responses; + } else { + // Convert responses to DataObjects before returning + $dataObjects = array(); + foreach ($responses as $index => $response) { + $dataObject = $this->dataObject() + ->populateFromResponse($response) + ->setName($files[$index]['name']) + ->setContent($entities[$index]); + $dataObjects[] = $dataObject; + } + return $dataObjects; } - - return $responses; } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 2ada2c41c..5e3171e6e 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -20,6 +20,7 @@ use Guzzle\Http\Message\Response; use OpenCloud\Common\Constants\Size; use OpenCloud\Tests\ObjectStore\ObjectStoreTestCase; +use OpenCloud\ObjectStore\Enum\ReturnType; class ContainerTest extends ObjectStoreTestCase { @@ -225,6 +226,19 @@ public function test_Upload_Multiple() )); } + public function test_Upload_Multiple_Return_DataObject_Array() + { + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test', 'body' => 'FOOBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); + $this->assertEquals('test', $dataObjects[0]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[0]->getContent()); + $this->assertEquals('FOOBAR', (string) $dataObjects[0]->getContent()); + } + public function test_Upload() { $this->assertInstanceOf( From da68b2dc4e03022f1336f7e2b3d2cbff1a0b83d3 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 25 Feb 2015 16:35:05 -0800 Subject: [PATCH 694/835] Adding documentation. --- docs/userguide/ObjectStore/USERGUIDE.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 8bb3f3d45..fa04bdeaf 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -198,7 +198,8 @@ $localFileName = '/path/to/local/php-elephant.jpg'; $remoteFileName = 'php-elephant.jpg'; $fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); +$object = $container->uploadObject($remoteFileName, $fileData); +/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object.php) ] @@ -258,7 +259,10 @@ $objects = array( a ); -$container->uploadObjects($objects); +$responses = $container->uploadObjects($objects); +foreach ($responses as $response) { + /** @var $response \Guzzle\Http\Message\Response **/ +} ``` [ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects.php) ] @@ -266,7 +270,7 @@ In the above example, the contents of two files present on the local filesystem Instead of specifying the `path` key in an element of the `$objects` array, you can specify a `body` key whose value is a string or a stream representation. -Finally, you can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. +You can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. ``` $metadata = array('author' => 'Jane Doe'); @@ -281,6 +285,17 @@ $container->uploadObjects($objects, $allHeaders); In the example above, every object referenced within the `$objects` array will be uploaded with the same metadata. +Finally, if you want the `uploadObjects` method to return an array of `OpenCloud\ObjectStore\Resource\DataObject` objects instead of an array of `Guzzle\Http\Message\Response` objects, you can specify a third parameter to the `uploadObjects` method: + +``` +use OpenCloud\ObjectStore\Enum\ReturnType; + +$dataObjects = $container->uploadObjects($objects, $allHeaders, ReturnType::DATA_OBJECT_ARRAY); +foreach ($dataObjects as $dataObject) { + /** @var $dataObject OpenCloud\ObjectStore\Resource\DataObject **/ +} +``` + ### Large Objects If you want to upload objects larger than 5GB in size, you must use a different upload process. From 5bb0ae44ea676b37a9477c74fb6117f7f89c59db Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 05:57:35 -0800 Subject: [PATCH 695/835] Adding more test data. --- .../ObjectStore/Resource/ContainerTest.php | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 5e3171e6e..e5e002eda 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -228,15 +228,38 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { - $container = $this->container; + $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); + echo $tempFileName; + try { + $tempFile = fopen($tempFileName, 'w+'); + fwrite($tempFile, 'BAZQUX'); + + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test1', 'body' => 'FOOBAR'), + array('name' => 'test2', 'path' => $tempFileName), + array('name' => 'test2', 'body' => 'BARBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + } finally { + fclose($tempFile); + unlink($tempFileName); + } - $dataObjects = $container->uploadObjects(array( - array('name' => 'test', 'body' => 'FOOBAR') - ), array(), ReturnType::DATA_OBJECT_ARRAY); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); - $this->assertEquals('test', $dataObjects[0]->getName()); + $this->assertEquals('test1', $dataObjects[0]->getName()); $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[0]->getContent()); $this->assertEquals('FOOBAR', (string) $dataObjects[0]->getContent()); + + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[1]); + $this->assertEquals('test2', $dataObjects[1]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[1]->getContent()); + $this->assertEquals('BAZQUX', (string) $dataObjects[1]->getContent()); + + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[2]); + $this->assertEquals('test2', $dataObjects[2]->getName()); + $this->assertInstanceOf('Guzzle\Http\EntityBody', $dataObjects[2]->getContent()); + $this->assertEquals('BARBAR', (string) $dataObjects[2]->getContent()); } public function test_Upload() From 928a810df3ca7ebaa90f69566c35f5651d4509d0 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:14:30 -0800 Subject: [PATCH 696/835] Adding inline documentation. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index ef8f27e68..edcea7397 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -472,9 +472,12 @@ public function uploadObject($name, $data, array $headers = array()) * `path' Path to an existing file, OR * `body' Either a string or stream representation of the file contents to be uploaded. * @param array $headers Optional headers that will be sent with the request (useful for object metadata). + * @param string $returnType One of OpenCloud\ObjectStore\Enum\ReturnType::RESPONSE_ARRAY (to return an array of + * Guzzle\Http\Message\Response objects) or OpenCloud\ObjectStore\Enum\ReturnType::DATA_OBJECT_ARRAY + * (to return an array of OpenCloud\ObjectStore\Resource\DataObject objects). * * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError - * @return \Guzzle\Http\Message\Response + * @return Guzzle\Http\Message\Response[] or OpenCloud\ObjectStore\Resource\DataObject[] depending on $returnType */ public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY) { From 5e42a91ca9b2dea3d9062d1098778a0e85b6f2aa Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:14:52 -0800 Subject: [PATCH 697/835] Oops. Forgot to check-in this file! --- lib/OpenCloud/ObjectStore/Enum/ReturnType.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 lib/OpenCloud/ObjectStore/Enum/ReturnType.php diff --git a/lib/OpenCloud/ObjectStore/Enum/ReturnType.php b/lib/OpenCloud/ObjectStore/Enum/ReturnType.php new file mode 100644 index 000000000..ac9193ed4 --- /dev/null +++ b/lib/OpenCloud/ObjectStore/Enum/ReturnType.php @@ -0,0 +1,29 @@ + Date: Thu, 26 Feb 2015 06:15:57 -0800 Subject: [PATCH 698/835] Condensing to one line. --- lib/OpenCloud/ObjectStore/Resource/Container.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index edcea7397..0f7b0df08 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -527,11 +527,10 @@ public function uploadObjects(array $files, array $commonHeaders = array(), $ret // Convert responses to DataObjects before returning $dataObjects = array(); foreach ($responses as $index => $response) { - $dataObject = $this->dataObject() - ->populateFromResponse($response) - ->setName($files[$index]['name']) - ->setContent($entities[$index]); - $dataObjects[] = $dataObject; + $dataObjects[] = $this->dataObject() + ->populateFromResponse($response) + ->setName($files[$index]['name']) + ->setContent($entities[$index]); } return $dataObjects; } From 39309d302ca6bc9cb9de6024c13857f5d8706fdf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 26 Feb 2015 06:16:09 -0800 Subject: [PATCH 699/835] Remove debugging statement. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index e5e002eda..12daa574f 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -229,7 +229,6 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); - echo $tempFileName; try { $tempFile = fopen($tempFileName, 'w+'); fwrite($tempFile, 'BAZQUX'); From 8142bf208f1c537216566506a321d22d503d44bf Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 06:22:06 -0800 Subject: [PATCH 700/835] Adding catch block so this works with PHP 5.4. --- tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 12daa574f..11786c4dc 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -240,6 +240,8 @@ public function test_Upload_Multiple_Return_DataObject_Array() array('name' => 'test2', 'path' => $tempFileName), array('name' => 'test2', 'body' => 'BARBAR') ), array(), ReturnType::DATA_OBJECT_ARRAY); + } catch (Exception $e) { + throw $e; } finally { fclose($tempFile); unlink($tempFileName); From 63118f19311913ea8811cb021b3671e4e5311172 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 07:33:35 -0800 Subject: [PATCH 701/835] Adding directive to let Travis builds finish fast. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index fe792bec4..bdd5f76af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ sudo: false matrix: allow_failures: - php: hhvm + fast_finish: true branches: only: From 9b98d2cd9ab92e7ed694425b43df673306a98831 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 27 Feb 2015 07:38:48 -0800 Subject: [PATCH 702/835] Duh! PHP5.4 and below don't support finally at all. Removing altogether :( --- .../ObjectStore/Resource/ContainerTest.php | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index 11786c4dc..b5d2bc602 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -229,23 +229,19 @@ public function test_Upload_Multiple() public function test_Upload_Multiple_Return_DataObject_Array() { $tempFileName = tempnam(sys_get_temp_dir(), "php-opencloud-test-"); - try { - $tempFile = fopen($tempFileName, 'w+'); - fwrite($tempFile, 'BAZQUX'); - - $container = $this->container; - - $dataObjects = $container->uploadObjects(array( - array('name' => 'test1', 'body' => 'FOOBAR'), - array('name' => 'test2', 'path' => $tempFileName), - array('name' => 'test2', 'body' => 'BARBAR') - ), array(), ReturnType::DATA_OBJECT_ARRAY); - } catch (Exception $e) { - throw $e; - } finally { - fclose($tempFile); - unlink($tempFileName); - } + + $tempFile = fopen($tempFileName, 'w+'); + fwrite($tempFile, 'BAZQUX'); + + $container = $this->container; + + $dataObjects = $container->uploadObjects(array( + array('name' => 'test1', 'body' => 'FOOBAR'), + array('name' => 'test2', 'path' => $tempFileName), + array('name' => 'test2', 'body' => 'BARBAR') + ), array(), ReturnType::DATA_OBJECT_ARRAY); + fclose($tempFile); + unlink($tempFileName); $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\DataObject', $dataObjects[0]); $this->assertEquals('test1', $dataObjects[0]->getName()); From ac1233ee67c61a033e46e5e96e012b9656c87d97 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:02:14 +0100 Subject: [PATCH 703/835] Add gitignore to prevent builds from being pushed --- doc/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/.gitignore diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..69fa449dd --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +_build/ From 310d9c880f22ded01cd56e56da7da10792125518 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:02:47 +0100 Subject: [PATCH 704/835] Add basic config and make scripts --- doc/Makefile | 177 +++++++++++++++++++++++++++++++++++++ doc/conf.py | 63 ++++++++++++++ doc/make.bat | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 doc/Makefile create mode 100644 doc/conf.py create mode 100644 doc/make.bat diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..b7805cd67 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/php-opencloud.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/php-opencloud.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/php-opencloud" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/php-opencloud" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000..7f9e29414 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# +# php-opencloud documentation build configuration file, created by +# sphinx-quickstart on Tue Mar 3 12:28:19 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +from sphinx.highlighting import lexers +from pygments.lexers.web import PhpLexer + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +lexers['php'] = PhpLexer(startinline=True, linenos=1) +lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) +primary_domain = 'php' + +extensions = [] +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'php-opencloud' +copyright = u'2015, Jamie Hannaford, Shaunak Kashyap' +version = '1.12' +release = '1.12.1' +exclude_patterns = ['_build'] +pygments_style = 'sphinx' +html_theme = 'default' + +if not on_rtd: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "_templates"] + +html_static_path = ['_static'] +html_use_index = True + +# Output file base name for HTML help builder. +htmlhelp_basename = 'php-openclouddoc' + +latex_documents = [ + ('index', 'php-opencloud.tex', u'php-opencloud Documentation', + u'Jamie Hannaford, Shaunak Kashyap', 'manual'), +] + +man_pages = [ + ('index', 'php-opencloud', u'php-opencloud Documentation', + [u'Jamie Hannaford, Shaunak Kashyap'], 1) +] + +texinfo_documents = [ + ('index', 'php-opencloud', u'php-opencloud Documentation', + u'Jamie Hannaford, Shaunak Kashyap', 'php-opencloud', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 000000000..219d9213f --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\php-opencloud.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\php-opencloud.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end From 745b717d71ed09814f27cd9a0196ced0fc561648 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:03:16 +0100 Subject: [PATCH 705/835] Add initial AutoScale --- doc/services/autoscale/group-config.rst | 63 ++++++++++++++++ doc/services/autoscale/groups.rst | 90 +++++++++++++++++++++++ doc/services/autoscale/index.rst | 51 +++++++++++++ doc/services/autoscale/policies.rst | 83 +++++++++++++++++++++ doc/services/autoscale/service.sample.rst | 7 ++ doc/services/autoscale/webhooks.rst | 64 ++++++++++++++++ 6 files changed, 358 insertions(+) create mode 100644 doc/services/autoscale/group-config.rst create mode 100644 doc/services/autoscale/groups.rst create mode 100644 doc/services/autoscale/index.rst create mode 100644 doc/services/autoscale/policies.rst create mode 100644 doc/services/autoscale/service.sample.rst create mode 100644 doc/services/autoscale/webhooks.rst diff --git a/doc/services/autoscale/group-config.rst b/doc/services/autoscale/group-config.rst new file mode 100644 index 000000000..a0d7c98f7 --- /dev/null +++ b/doc/services/autoscale/group-config.rst @@ -0,0 +1,63 @@ +Group configurations +==================== + +.. contents:: + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with the functionality of a group's configuration, +you must first retrieve the details of the group itself. To do this, you must +substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get group configuration +----------------------- + +.. code-block:: php + + /** @var */ + $groupConfig = $group->getGroupConfig(); + + +Edit group configuration +------------------------ + +.. code-block:: php + + $groupConfig->update(array( + 'name' => 'New name!' + )); + + +Get launch configuration +------------------------ + +.. code-block:: php + + /** @var */ + $launchConfig = $group->getLaunchConfig(); + + +Edit group/launch configuration +------------------------------- + +.. code-block:: php + + $launchConfig = $group->getLaunchConfig(); + + $server = $launchConfig->args->server; + $server->name = "BRAND NEW SERVER NAME"; + + $launchConfig->update(array + 'args' => array( + 'server' => $server, + 'loadBalancers' => $launchConfig->args->loadBalancers + ) + )); diff --git a/doc/services/autoscale/groups.rst b/doc/services/autoscale/groups.rst new file mode 100644 index 000000000..8e781a2aa --- /dev/null +++ b/doc/services/autoscale/groups.rst @@ -0,0 +1,90 @@ +Groups +====== + +.. contents:: + + +Setup +----- + +.. include:: service.sample.rst + + +List all groups +--------------- + +.. code-block:: php + + $groups = $service->groupList(); + foreach ($group as $group) { + /** @var $group OpenCloud\Autoscale\Resources\Group */ + } + +Please consult the `iterator guide `__ for more information about +iterators. + + +Retrieve group by ID +-------------------- + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Create a new group +------------------ + +.. code-block:: php + + // Set the config object for this autoscale group; contains all of properties + // which determine its behaviour + $groupConfig = array( + 'name' => 'new_autoscale_group', + 'minEntities' => 5, + 'maxEntities' => 25, + 'cooldown' => 60, + ); + + // We need specify what is going to be launched. For now, we'll launch a new server + $launchConfig = array( + 'type' => 'launch_server', + 'args' => array( + 'server' => array( + 'flavorRef' => 3, + 'name' => 'webhead', + 'imageRef' => '0d589460-f177-4b0f-81c1-8ab8903ac7d8' + ), + 'loadBalancers' => array( + array('loadBalancerId' => 2200, 'port' => 8081), + ) + ) + ); + + // Do we want particular scaling policies? + $policy = array( + 'name' => 'scale up by 10', + 'change' => 10, + 'cooldown' => 5, + 'type' => 'webhook', + ); + + $group->create(array( + 'groupConfiguration' => $groupConfig, + 'launchConfiguration' => $launchConfig, + 'scalingPolicies' => array($policy), + )); + +Delete a group +-------------- + +.. code-block:: php + + $group->delete(); + +Get the current state of the scaling group +------------------------------------------ + +.. code-block:: php + + $group->getState(); diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst new file mode 100644 index 000000000..3077a0a2c --- /dev/null +++ b/doc/services/autoscale/index.rst @@ -0,0 +1,51 @@ +Auto Scale v2 +============= + +.. toctree:: + + groups + group-config + policies + webhooks + +Glossary +-------- + +.. glossary:: + + group + The scaling group is at the heart of an Auto Scale deployment. The scaling + group specifies the basic elements of the Auto Scale configuration. It + manages how many servers can participate in the scaling group. It also + specifies information related to load balancers if your configuration uses + a load balancer. + + group configuration + Outlines the basic elements of the Auto Scale configuration. The group + configuration manages how many servers can participate in the scaling group. + It sets a minimum and maximum limit for the number of entities that can be + used in the scaling process. It also specifies information related to load + balancers. + + launch configuration + Creates a blueprint for how new servers will be created. The launch + configuration specifies what type of server image will be started on + launch, what flavor the new server is, and which load balancer the new + server connects to. + + policy + Auto Scale uses policies to define the scaling activity that will take + place, as well as when and how that scaling activity will take place. + Scaling policies specify how to modify the scaling group and its behavior. + You can specify multiple policies to manage a scaling group. + + webhook + A webhook is a reachable endpoint that when visited will execute a scaling + policy for a particular scaling group. + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ + - `API release history `_ diff --git a/doc/services/autoscale/policies.rst b/doc/services/autoscale/policies.rst new file mode 100644 index 000000000..f5d2605f4 --- /dev/null +++ b/doc/services/autoscale/policies.rst @@ -0,0 +1,83 @@ +Scaling Policies +================ + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with the functionality of a group's scaling +policies, you must first retrieve the details of the group itself. To do this, +you must substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get all policies +---------------- + +.. code-block:: php + + $policies = $group->getScalingPolicies(); + + foreach ($policies as $policy) { + printf("Name: %s Type: %s\n", $policy->name, $policy->type); + } + + +Create new scaling policies +--------------------------- + +Creating policies is achieved through passing an array to the ``create`` +method. + +.. code-block:: php + + $policies = array( + array( + 'name' => 'NEW NAME', + 'change' => 1, + 'cooldown' => 150, + 'type' => 'webhook', + ) + ); + + $group->createScalingPolicies($policies); + + +Get an existing scaling policy +------------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + + +Update a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->update(array( + 'name' => 'More relevant name' + )); + + +Delete a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->delete(); + +Execute a scaling policy +------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->execute(); diff --git a/doc/services/autoscale/service.sample.rst b/doc/services/autoscale/service.sample.rst new file mode 100644 index 000000000..7e5d166ab --- /dev/null +++ b/doc/services/autoscale/service.sample.rst @@ -0,0 +1,7 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); diff --git a/doc/services/autoscale/webhooks.rst b/doc/services/autoscale/webhooks.rst new file mode 100644 index 000000000..68b16b0dc --- /dev/null +++ b/doc/services/autoscale/webhooks.rst @@ -0,0 +1,64 @@ +Webhooks +======== + +Setup +----- + +.. include:: service.sample.rst + +Finally, in order to interact with webhooks, you must first retrieve the +details of the group and scaling policy you want to execute: + +.. code-block:: php + + $group = $service->group('{groupId}'); + $policy = $group->getScalingPolicy('{policyId}'); + +Get all webhooks +---------------- + +.. code-block:: php + + $webhooks = $policy->getWebookList(); + +Create a new webhook +-------------------- + +.. code-block:: php + + $policy->createWebhooks(array( + array( + 'name' => 'Alice', + 'metadata' => array( + 'firstKey' => 'foo', + 'secondKey' => 'bar' + ) + ) + )); + +Get webhook +----------- + +.. code-block:: php + + $webhook = $policy->getWebhook('{webhookId}'); + +Update webhook +-------------- + +.. code-block:: php + + // Update the metadata + $metadata = $webhook->metadata; + $metadata->thirdKey = 'blah'; + $webhook->update(array( + 'metadata' => $metadata + )); + + +Delete webhook +-------------- + +.. code-block: php + + $webhook->delete(); From bed631529266a3ca6175b67218e6d75bfc4dfafc Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:03:38 +0100 Subject: [PATCH 706/835] Add initial Common and Compute --- doc/services/common/os-client.sample.rst | 13 ++ doc/services/common/rs-client.sample.rst | 15 ++ doc/services/compute/flavors.rst | 66 ++++++++ doc/services/compute/images.rst | 87 ++++++++++ doc/services/compute/index.rst | 36 ++++ doc/services/compute/keypairs.rst | 71 ++++++++ doc/services/compute/servers.rst | 202 +++++++++++++++++++++++ doc/services/compute/service.sample.rst | 24 +++ 8 files changed, 514 insertions(+) create mode 100644 doc/services/common/os-client.sample.rst create mode 100644 doc/services/common/rs-client.sample.rst create mode 100644 doc/services/compute/flavors.rst create mode 100644 doc/services/compute/images.rst create mode 100644 doc/services/compute/index.rst create mode 100644 doc/services/compute/keypairs.rst create mode 100644 doc/services/compute/servers.rst create mode 100644 doc/services/compute/service.sample.rst diff --git a/doc/services/common/os-client.sample.rst b/doc/services/common/os-client.sample.rst new file mode 100644 index 000000000..b4614474d --- /dev/null +++ b/doc/services/common/os-client.sample.rst @@ -0,0 +1,13 @@ +.. code-block:: php + + '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/services/common/rs-client.sample.rst b/doc/services/common/rs-client.sample.rst new file mode 100644 index 000000000..e33983270 --- /dev/null +++ b/doc/services/common/rs-client.sample.rst @@ -0,0 +1,15 @@ +The first thing to do is pass in your credentials and instantiate a Rackspace +client: + +.. code-block:: php + + '{username}', + 'apiKey' => '{apiKey}', + )); diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst new file mode 100644 index 000000000..f79caa171 --- /dev/null +++ b/doc/services/compute/flavors.rst @@ -0,0 +1,66 @@ +Flavors +======= + +Setup +----- + +.. include:: service.sample.rst + + +Get a flavor +------------ + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + + +List flavors +------------ + +.. code-block:: php + + $flavors = $service->flavorList(); + + foreach ($flavors as $flavor) { + /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ + } + + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the ``flavorList`` method returns full details on all flavors. +However, because of the overhead involved in retrieving all the details, this +function can be slower than might be expected. To disable this feature and +keep bandwidth at a minimum, just pass ``false`` as the first argument: + +.. code-block:: php + + // Name and ID only + $compute->flavorList(false); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+================================================================================================================================================================================================+ +| minDisk | Filters the list of flavors to those with the specified minimum number of gigabytes of disk storage. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| minRam | Filters the list of flavors to those with the specified minimum amount of RAM in megabytes. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to return all flavors over 4GB in RAM: + +.. code-block:: php + + $flavors = $service->flavorList(true, array('minRam' => 4)); diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst new file mode 100644 index 000000000..b2aae4a65 --- /dev/null +++ b/doc/services/compute/images.rst @@ -0,0 +1,87 @@ +Images +====== + +.. note:: + + **Images on Rackspace servers:** with standard servers, the entire disk + (OS and data) is captured in the image. With Performance servers, only the s + ystem disk is captured in the image. The data disks should be backed up using + Cloud Backup or Cloud Block Storage to ensure availability in case you need + to rebuild or restore a server. + +Setup +----- + +.. include:: service.sample.rst + + +List images +----------- + +Below is the simplest usage for retrieving a list of images: + +.. code-block:: php + + $images = $service->imageList(); + + foreach ($images as $image) { + + } + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the only fields returned in a list call are `id` and `name`, but +you can enable more detailed information to be result by passing in `true` as +the first argument of the call, like so: + +.. code-block:: php + + $images = $service->imageList(true); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+====================================================================================================================================================================================================================================================================================================================================================+ +| server | Filters the list of images by server. Specify the server reference by ID or by full URL. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | Filters the list of images by image name. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| status | Filters the list of images by status. In-flight images have a status of ``SAVING`` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the ``OpenCloud\Compute\Constants\ImageState`` class. Images with an ``ACTIVE`` status are available for use. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| changes-since | Filters the list of images to those that have changed since the changes-since time. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| type | Filters base Rackspace images or any custom server images that you have created. Can either be ``BASE`` or ``SNAPSHOT``. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to filter images for a particular server: + +.. code-block:: php + + $images = $service->imageList(false, array('server' => '{serverId}')); + + +Retrieve details about an image +------------------------------- + +.. code-block:: php + + $image = $service->image('{imageId}'); + + +Delete an image +--------------- + +.. code-block:: php + + $image->delete(); diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst new file mode 100644 index 000000000..94c41979f --- /dev/null +++ b/doc/services/compute/index.rst @@ -0,0 +1,36 @@ +Compute v2 +========== + +.. note:: + + This is a joint service that supports both Rackspace Cloud Servers v2 API, and + OpenStack Nova v2 API. + +.. toctree:: + + images + flavors + servers + keypairs + +Glossary +-------- + +.. glossary:: + + image + An image is a collection of files for a specific operating system that you + use to create or rebuild a server. Rackspace provides prebuilt images. You + can also create custom images from servers that you have launched. + + flavor + A flavor is a named definition of certain server parameters such as + the amount of RAM and disk space available. (There are other parameters + set via the flavor, such as the amount of disk space and the number of + virtual CPUs, but a discussion of those is too in-depth for a simple + Getting Started Guide like this one.) + + server + A server is a virtual machine instance in the Cloud Servers environment. + + keypair diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst new file mode 100644 index 000000000..4040c0e65 --- /dev/null +++ b/doc/services/compute/keypairs.rst @@ -0,0 +1,71 @@ +Keypairs +======== + +Setup +----- + +.. include:: service.sample.rst + + +Generate a new keypair +---------------------- + +This operation creates a new keypair under a provided name; the public key +value is automatically generated for you. + +.. code-block:: php + + // Instantiate empty object + $keypair = $service->keypair(); + + // Send to API + $keypair->create(array( + 'name' => 'jamie_keypair_1' + )); + + // Save these! + $pubKey = $keypair->getPublicKey(); + $priKey = $keypair->getPrivateKey(); + + +Upload existing keypair +----------------------- + +This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem. + +.. code-block:: php + + $keypair = $service->keypair(); + + // $key needs to be the string content of the key file, not the filename + $content = file_get_contents('~/.ssh/id_rsa.pub'); + + $keypair->create(array( + 'name' => 'main_key', + 'publicKey' => $content + )); + +List keypairs +------------- + +To list all existing keypairs: + +.. code-block:: php + + $keys = $service->listKeypairs(); + + foreach ($keys as $key) { + + } + + +Delete keypairs +--------------- + +To delete a specific keypair: + +.. code-block:: php + + $keypair->delete(); diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst new file mode 100644 index 000000000..4d2f2363c --- /dev/null +++ b/doc/services/compute/servers.rst @@ -0,0 +1,202 @@ +Servers +======= + +Setup +----- + +.. include:: service.sample.rst + +Get server +---------- + +The easiest way to retrieve a specific server is by its unique ID: + +.. code-block:: php + + $server = $service->server('{serverId}'); + + +List servers +------------ + +You can list servers in two different ways: + +- return an *overview* of each server (ID, name and links) +- return *detailed information* for each server + +Knowing which option to use might help save unnecessary bandwidth and +reduce latency. + +.. code-block:: php + + // overview + $servers = $service->serverList(); + + // detailed + $servers = $service->serverList(true); + +URL parameters for filtering servers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| Name | Description | Type | ++==========================+====================================================================================================================================================================================================================================================================================================================+=================================================+ +| image | The image ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| flavor | The flavor ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| name | The server name | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| status | The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult ``OpenCloud\Compute\Constants\ServerState`` | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| changes-since | Value for checking for changes since a previous request | A valid ISO 8601 dateTime (2011-01-24T17:08Z) | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ + +Create server +------------- + +Using an image +~~~~~~~~~~~~~~ + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Using a bootable volume +~~~~~~~~~~~~~~~~~~~~~~~ + +Firstly we need to find our volume using their IDs. + +.. code-block:: php + + $bootableVolume = $client->volumeService()->volume('{volumeId}'); + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $response = $server->create(array( + 'name' => 'My lovely server', + 'volume' => $bootableVolume, + 'flavorId' => '{flavorId}' + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Create parameters +~~~~~~~~~~~~~~~~~ + ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| Name | Description | Type | Required | ++=============================+=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+===========+==============================+ +| name | The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync. | string | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| flavor | A populated ``OpenCloud\Compute\Resource\Flavor`` object representing your chosen flavor | object | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| image | A populated ``OpenCloud\Compute\Resource\Image`` object representing your chosen image | object | No, if volume is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volume | A populated ``OpenCloud\Volume\Resource\Volume`` object representing your chosen bootable volume | object | No, if image is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volumeDeleteOnTermination | ``true`` if the bootable volume should be deleted when the server is terminated; ``false``, otherwise | boolean | No; default = ``false`` | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| OS-DCF:diskConfig | The disk configuration value. You can use two options: ``AUTO`` or ``MANUAL``. \ ``AUTO`` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. ``AUTO`` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.\ ``MANUAL`` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration. | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| networks | An array of populated ``OpenCloud\Compute\Resource\Network`` objects that indicate which networks your instance resides in. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| metadata | An array of arbitrary data (key-value pairs) that adds additional meaning to your server. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| keypair | You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| personality | Files that you can upload to your newly created instance's filesystem. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ + +Creating a server with keypairs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'New server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'keypair' => 'main_key' + )); + +So, as you can see, you specify the **name** of an existing keypair that +you previously created on the API. + + +Creating a server with personality files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you execute the create operation, you can add "personality" files +to your ``OpenCloud\Compute\Resource\Server`` object. These files are +structured as a flat array. + +.. code-block:: php + + $server->addFile('/var/test_file', 'FILE CONTENT'); + +As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the `official +documentation `__. + +Update server +------------- + +You can update certain attributes of an existing server instance. These +attributes are detailed in the next section. + +.. code-block:: php + + $server->update(array( + 'name' => 'NEW SERVER NAME' + )); + +Updatable attributes +~~~~~~~~~~~~~~~~~~~~ + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | description | ++==============+==================================================================================================================================================+ +| name | The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv4 | The IP version 4 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv6 | The IP version 6 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Delete server +------------- + +.. code-block:: php + + $server->delete(); diff --git a/doc/services/compute/service.sample.rst b/doc/services/compute/service.sample.rst new file mode 100644 index 000000000..c684caf9a --- /dev/null +++ b/doc/services/compute/service.sample.rst @@ -0,0 +1,24 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + + +``{catalogName}`` is the **name** of the service, as it appears in the service +catalog. For Rackspace users, this will default to `cloudServersOpenStack`; for +OpenStack users, you must set your own value since it can depend on your +environment setup. + +``{region}`` is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the `supported regions page`. + +``{urlType}`` is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either `internalURL` or `publicURL`. +The former will execute HTTP transactions over the internal Rackspace network, +reducing latency and the overall bandwidth cost - the caveat is that all of your +resources must be in same region. `publicURL`, however, which is the default, +will operate over the public Internet and is to be used for multi-region +installations. From 6a3dacc72ad189a55b207da41660a294d9dc1816 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:00 +0100 Subject: [PATCH 707/835] Add databases and DNS --- doc/services/database/README.md.rst | 125 ++++++++++++ doc/services/database/index.rst | 0 doc/services/dns/Domains.md.rst | 290 ++++++++++++++++++++++++++++ doc/services/dns/Limits.md.rst | 70 +++++++ doc/services/dns/Records.md.rst | 111 +++++++++++ doc/services/dns/Reverse-DNS.md.rst | 96 +++++++++ doc/services/dns/Service.md.rst | 13 ++ doc/services/dns/index.rst | 0 8 files changed, 705 insertions(+) create mode 100644 doc/services/database/README.md.rst create mode 100644 doc/services/database/index.rst create mode 100644 doc/services/dns/Domains.md.rst create mode 100644 doc/services/dns/Limits.md.rst create mode 100644 doc/services/dns/Records.md.rst create mode 100644 doc/services/dns/Reverse-DNS.md.rst create mode 100644 doc/services/dns/Service.md.rst create mode 100644 doc/services/dns/index.rst diff --git a/doc/services/database/README.md.rst b/doc/services/database/README.md.rst new file mode 100644 index 000000000..3f6bdd3c2 --- /dev/null +++ b/doc/services/database/README.md.rst @@ -0,0 +1,125 @@ +Databases +========= + +A **cloud database** is a MySQL relational database service that allows +customers to programatically provision database instances of varying +virtual resource sizes without the need to maintain and/or update MySQL. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\Rackspace; + use OpenCloud\Common\Constants\State; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Create a database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $databaseService = $client->databaseService('cloudDatabases', 'DFW'); + + $twoGbFlavor = $databaseService->flavor(3); + + $dbInstance = $databaseService->instance(); + $dbInstance->name = 'Demo database instance'; + $dbInstance->volume = new stdClass(); + $dbInstance->volume->size = 20; // GB + $dbInstance->flavor = $twoGbFlavor; + $dbInstance->create(); + + $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { + + printf("Database instance build status: %s\n", $dbInstance->status); + + }); + +The example above creates a database server instance with 20GB of disk +space and 2GB of memory, then waits for it to become ACTIVE. + +3. Create a database on the database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $db = $dbInstance->database(); + $db->name = 'demo_db'; + + $db->create(); + +The example above creates a database named ``demo_db`` on the database +server instance created in the previous step. + +4. Create database user and give it access to database. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $user = $dbInstance->user(); + $user->name = 'demo_user'; + $user->password = 'h@X0r!'; + $user->databases = array('demo_db'); + + $user->create(); + +The example above creates a database user named ``demo_user``, sets its +password and gives it access to the ``demo_db`` database created in the +previous step. + +5. Optional step: Create a load balancer to allow access to the database from the Internet. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The database created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $loadBalancer->name = 'Load balancer - DB'; + $loadBalancer->addNode($dbInstance->hostname, 3306); + $loadBalancer->port = 3306; + $loadBalancer->protocol = 'MYSQL'; + $loadBalancer->addVirtualIp('PUBLIC'); + + $loadBalancer->create(); + + $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { + printf("Load balancer build status: %s\n", $lb->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/dns/Domains.md.rst b/doc/services/dns/Domains.md.rst new file mode 100644 index 000000000..824c05099 --- /dev/null +++ b/doc/services/dns/Domains.md.rst @@ -0,0 +1,290 @@ +Domains +======= + +A domain is an entity/container of all DNS-related information +containing one or more records. + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name. + +.. code:: php + + $domain = $service->domain(12345); + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + +Please consult the `iterator +documentation
          `__ for more information +about iterators. + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the aforementioned search by using the ``name`` parameter +in a key/value array supplied as a method argument. For example, +providing ``array('name' => 'hoola.com')`` will return hoola.com and +similar names such as main.hoola.com and sub.hoola.com. + +.. code:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'hoola.com' + )); + +Filter criteria may consist of: + +- Any letter (A-Za-z) +- Numbers (0-9) +- Hyphen ("-") +- 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +If you know a domain's name, but not its unique identifier, you can do +this: + +.. code:: php + + $domains = $service->domainList(array( + 'name' => 'foo.com' + )); + + foreach ($domains as $domain) { + $id = $domain->id; + } + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. The since parameter is optional and defaults to midnight of +the current day. + +.. code:: php + + $changes = $domain->changes(); + + # Changes since last week + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + +Export domain +------------- + +This call provides the BIND (Berkeley Internet Name Domain) 9 formatted +contents of the requested domain. This call is for a single domain only, +and as such, does not traverse up or down the domain hierarchy for +details (that is, no subdomain information is provided). + +.. code:: php + + $asyncResponse = $domain->export(); + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + +Create domain +------------- + +A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` +records) and an optional list of sub-domains. You will need to specify +these before creating the domain itself: + +.. code:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + $domain->addRecord($aRecord); + + // add optional C record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add optional MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add optional NS records + $nsRecord1 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord1); + + $nsRecord2 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns2.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord2); + + // add optional subdomains + $sub1 = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($sub1); + + // send to API + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + +Clone domain +------------ + +This call will duplicate a single existing domain configuration with a +new domain name for the specified Cloud account. By default, all records +and, optionally, subdomain(s) are duplicated as well. + +The method signature you will need to use is: + +.. code:: php + + cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) + ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Data type | Default | Description | ++======================+==============+============+====================================================================================================================================================================================+ +| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +For example: + +.. code:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true); + +Import domain +------------- + +This call provisions a new DNS domain under the account specified by the +BIND 9 formatted file configuration contents defined in the request +object. + +You will need to ensure that the BIND 9 formatted file configuration +contents are valid by adhering to the following rules: + +- Each record starts on a new line and on the first column. If a record + will not fit on one line, use the BIND\_9 line continuation + convention where you put a left parenthesis and continue the one + record on the next line and put a right parenthesis when the record + ends. For example, + + example2.net. 3600 IN SOA dns1.stabletransit.com. ( + sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +- The attribute values of a record must be separated by a single blank + or tab. No other white space characters. + +- If there are any NS records, the data field should not be + dns1.stabletransit.com or dns2.stabletransit.com. They will result in + "duplicate record" errors. + +For example: + +.. code:: php + + $bind9Data = <<import($bind9Data); + +Modify domain +------------- + +This call modifies DNS domain(s) attributes only. Only the TTL, email +address and comment attributes of a domain can be modified. Records +cannot be added, modified, or removed through this API operation - you +will need to use the `add +records
          `__, `modify +records
          `__ or `remove +records
          `__ operations +respectively. + +.. code:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + +Remove domain +------------- + +.. code:: php + + $domain->delete(); + diff --git a/doc/services/dns/Limits.md.rst b/doc/services/dns/Limits.md.rst new file mode 100644 index 000000000..72f8219a0 --- /dev/null +++ b/doc/services/dns/Limits.md.rst @@ -0,0 +1,70 @@ +Limits +====== + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +List all limits +--------------- + +This call provides a list of all applicable limits for the specified +account. + +.. code:: php + + $limits = $service->limits(); + +Absolute limits +~~~~~~~~~~~~~~~ + +There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain: + +.. code:: php + + $absoluteLimits = $limits->absolute; + + # Domain limit + echo $absoluteLimits->domains; + + # Record limit per domain + echo $absoluteLimits->{'records per domain'}; + +List limit types +---------------- + +To find out the different limit types you can query, run: + +.. code:: php + + $limitTypes = $service->limitTypes(); + +will return: + +:: + + array(3) { + [0] => + string(10) "RATE_LIMIT" + [1] => + string(12) "DOMAIN_LIMIT" + [2] => + string(19) "DOMAIN_RECORD_LIMIT" + } + +Query a specific limit +---------------------- + +.. code:: php + + $limit = $service->limits('DOMAIN_LIMIT'); + + echo $limit->absolute->limits->value; + + >>> 500 + diff --git a/doc/services/dns/Records.md.rst b/doc/services/dns/Records.md.rst new file mode 100644 index 000000000..4e492e8ef --- /dev/null +++ b/doc/services/dns/Records.md.rst @@ -0,0 +1,111 @@ +Records +======= + +A DNS record belongs to a particular domain and is used to specify +information about the domain. + +There are several types of DNS records. Examples include mail exchange +(MX) records, which specify the mail server for a particular domain, and +name server (NS) records, which specify the authoritative name servers +for a domain. + +It is represented by the ``OpenCloud\DNS\Resource\Record`` class. +Records belong to a `Domain `__. + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('NS-1234567'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+---------------+ +| Name | Data type | Default | Description | ++============+==============+========================+===============+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+---------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+---------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+---------------+ + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record(123456); + $record->ttl -= 100; + $record->update(); + +Delete record +------------- + +.. code:: php + + $record->delete(); + diff --git a/doc/services/dns/Reverse-DNS.md.rst b/doc/services/dns/Reverse-DNS.md.rst new file mode 100644 index 000000000..b8a5c0e76 --- /dev/null +++ b/doc/services/dns/Reverse-DNS.md.rst @@ -0,0 +1,96 @@ +Reverse DNS +=========== + +DNS usually determines an IP address associated with a domain name. +Reverse DNS is the opposite process: resolving a domain name from an IP +address. This is usually achieved with a domain name pointer. + +Get PTR record +-------------- + +PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one's PTR record: + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptr = $service->ptrRecord(array( + 'parent' => $parent + )); + +So, in the above example, a ``$parent`` could be an instance of +``OpenCloud\Compute\Resource\Server`` or +``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both +implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please +consult the `server documentation <../Compute/Server.md>`__ and `load +balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +detailed usage instructions. + +List PTR records +---------------- + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptrRecords = $service->ptrRecordList($parent); + + foreach ($ptrRecords as $ptrRecord) { + + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add PTR record +-------------- + +.. code:: php + + $parent = $computeService->server('foo-server-id'); + + $ptr = $dnsService->ptrRecord(array( + 'parent' => $parent, + 'ttl' => 3600, + 'name' => 'example.com', + 'type' => 'PTR', + 'data' => '192.0.2.7' + )); + + $ptr->create(); + +Here is a table that explains the above attributes: + ++-----------+------------------------------------------------------------------------------------+------------+ +| Name | Description | Required | ++===========+====================================================================================+============+ +| type | Specifies the record type as "PTR". | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| name | Specifies the name for the domain or subdomain. Must be a valid domain name. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| data | The data field for PTR records must be a valid IPv4 or IPv6 IP address. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| ttl | If specified, must be greater than 300. Defaults to 3600 if no TTL is specified. | No | ++-----------+------------------------------------------------------------------------------------+------------+ +| comment | If included, its length must be less than or equal to 160 characters. | No | ++-----------+------------------------------------------------------------------------------------+------------+ + +Modify PTR record +----------------- + +.. code:: php + + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + +Delete PTR record +----------------- + +.. code:: php + + $ptr->delete(); + diff --git a/doc/services/dns/Service.md.rst b/doc/services/dns/Service.md.rst new file mode 100644 index 000000000..29ea79193 --- /dev/null +++ b/doc/services/dns/Service.md.rst @@ -0,0 +1,13 @@ +DNS Service +=========== + +To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the `Clients documentation <../Clients.md>`__. + +You will then need to run: + +.. code:: php + + $service = $client->dnsService(); + diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst new file mode 100644 index 000000000..e69de29bb From b2276a9883f50ac7d6ab67db4232e8ac6cacaad8 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:13 +0100 Subject: [PATCH 708/835] Add Identity and Images --- doc/services/identity/Roles.md.rst | 92 +++++++++++++++ doc/services/identity/Service.md.rst | 35 ++++++ doc/services/identity/Tenants.md.rst | 29 +++++ doc/services/identity/Tokens.md.rst | 105 +++++++++++++++++ doc/services/identity/Users.md.rst | 170 +++++++++++++++++++++++++++ doc/services/identity/index.rst | 0 doc/services/image/Images.md.rst | 107 +++++++++++++++++ doc/services/image/Schemas.md.rst | 170 +++++++++++++++++++++++++++ doc/services/image/Sharing.md.rst | 129 ++++++++++++++++++++ doc/services/image/Tags.md.rst | 28 +++++ doc/services/image/index.rst | 0 11 files changed, 865 insertions(+) create mode 100644 doc/services/identity/Roles.md.rst create mode 100644 doc/services/identity/Service.md.rst create mode 100644 doc/services/identity/Tenants.md.rst create mode 100644 doc/services/identity/Tokens.md.rst create mode 100644 doc/services/identity/Users.md.rst create mode 100644 doc/services/identity/index.rst create mode 100644 doc/services/image/Images.md.rst create mode 100644 doc/services/image/Schemas.md.rst create mode 100644 doc/services/image/Sharing.md.rst create mode 100644 doc/services/image/Tags.md.rst create mode 100644 doc/services/image/index.rst diff --git a/doc/services/identity/Roles.md.rst b/doc/services/identity/Roles.md.rst new file mode 100644 index 000000000..96a56488f --- /dev/null +++ b/doc/services/identity/Roles.md.rst @@ -0,0 +1,92 @@ +Roles +===== + +Intro +----- + +A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user's roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service. + +Setup +----- + +Role objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++---------------+------------------------+------------------------+ +| Property | Getter | Setter | ++===============+========================+========================+ +| id | ``getId()`` | ``setId()`` | ++---------------+------------------------+------------------------+ +| name | ``getName()`` | ``setName()`` | ++---------------+------------------------+------------------------+ +| description | ``getDescription()`` | ``setDescription()`` | ++---------------+------------------------+------------------------+ + +List roles +---------- + +This call lists the global roles available within a specified service. + +.. code:: php + + $roles = $service->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get role +-------- + +This call lists detailed information (id, name, description) for a +specified role. + +.. code:: php + + $roleId = '123abc'; + $role = $service->getRole($roleId); + +Add/delete user roles +--------------------- + +To add/remove user roles, you must first instantiate a +`user `__ object: + +.. code:: php + + $roleId = '123abc'; + + // add role to user + $user->addRole($roleId); + + // remove role from user + $user->removeRole($roleId); + +List user global roles +---------------------- + +This call returns a list of global roles associated with a user: + +.. code:: php + + $roles = $user->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Service.md.rst b/doc/services/identity/Service.md.rst new file mode 100644 index 000000000..f7e8e12ba --- /dev/null +++ b/doc/services/identity/Service.md.rst @@ -0,0 +1,35 @@ +Identity service +================ + +Intro +----- + +The Identity service is regionless, so you do not need to specify a +region when instantiating the service object. Although this was +primarily based on Rackspace's implementation of Cloud Identity, it +should also work for OpenStack Keystone. + +A note on object creation +------------------------- + +Normally, when services are created the client handles authenticates +automatically. But because Keystone/Identity is fundamental to the +authentication process itself, it proves difficult to do this procedure +as its normally done. For this reason, you have two options when +creating the service object: + +1: Use the client's factory method + +.. code:: php + + $identity = $client->identityService(); + +2: Authenticate manually + +.. code:: php + + use OpenCloud\Identity\Service as IdentityService; + + $identity = IdentityService::factory($client); + $identity->getClient()->authenticate(); + diff --git a/doc/services/identity/Tenants.md.rst b/doc/services/identity/Tenants.md.rst new file mode 100644 index 000000000..9b58efd1a --- /dev/null +++ b/doc/services/identity/Tenants.md.rst @@ -0,0 +1,29 @@ +Tenants +======= + +Intro +----- + +A tenant is a container used to group or isolate resources and/or +identity objects. Depending on the service operator, a tenant may map to +a customer, account, organization, or project. + +Setup +----- + +Tenant objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +List tenants +------------ + +.. code:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Tokens.md.rst b/doc/services/identity/Tokens.md.rst new file mode 100644 index 000000000..c42ce1573 --- /dev/null +++ b/doc/services/identity/Tokens.md.rst @@ -0,0 +1,105 @@ +Tokens +====== + +Intro +----- + +A token is an opaque string that represents an authorization to access +cloud resources. Tokens may be revoked at any time and are valid for a +finite duration. + +Setup +----- + +Token objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| Property | Description | Getter | Setter | ++============+===========================================+========================================+====================+ +| id | The unique ID of the token | ``getId()`` | ``setId()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +Request body for Rackspace's generate token operation: + +.. code:: json + + { + "auth": { + "RAX-KSKEY:apiKeyCredentials": { + "username": "foo", + "apiKey": "aaaaa-bbbbb-ccccc-12345678" + }, + "tenantId": "1100111" + } + } + +Request body for Keystone's generate token operation: + +.. code:: json + + { + "auth": { + "passwordCredentials":{ + "username":"demoauthor", + "password":"theUsersPassword" + }, + "tenantId": "12345678" + } + } + +The only real differences you'll notice is the name of the object key +(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret +(``apiKey``/``password``). The ``tenantId`` property in both templates +are optional. You can also add ``tenantName`` too. + +.. code:: php + + use OpenCloud\Common\Http\Message\Formatter; + + $template = sprintf( + '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', + 'my_username', + 'my_api_key' + ); + + $response = $service->generateToken($template); + + $body = Formatter::decode($response); + + // service catalog + $catalog = $body->access->serviceCatalog; + + // token + $token = $body->access->token; + + // user + $user = $body->access->user; + +As you will notice, these variables will be stdClass objects - for fully +fledged functionality, let the client authenticate by itself because it +ends up stocking the necessary models for you. + +To see the response body structure, consult the `official +docs `__. + +Revoke token (destroy session) +------------------------------ + +.. code:: php + + $tokenId = '1234567'; + $service->revokeToken($tokenId); + diff --git a/doc/services/identity/Users.md.rst b/doc/services/identity/Users.md.rst new file mode 100644 index 000000000..e8c2e6d63 --- /dev/null +++ b/doc/services/identity/Users.md.rst @@ -0,0 +1,170 @@ +Users +===== + +Intro +----- + +A user is a digital representation of a person, system, or service who +consumes cloud services. Users have credentials and may be assigned +tokens; based on these credentials and tokens, the authentication +service validates that incoming requests are being made by the user who +claims to be making the request, and that the user has the right to +access the requested resources. Users may be directly assigned to a +particular tenant and behave as if they are contained within that +tenant. + +Setup +----- + +User objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| Property | Description | Getter | Setter | ++=================+===============================================================================================================================================================================================================================================================================================================================+============================================+===============================================================================================================+ +| id | The unique ID for this user | ``getId()`` | ``setId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| username | Username for this user | ``getUsername()`` | ``setUsername()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| email | User's email address | ``getEmail()`` | ``setEmail()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| enabled | Whether or not this user can consume API functionality | ``getEnabled()`` or ``isEnabled()`` | ``setEnabled()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| password | Either a user-defined string, or an automatically generated one, that provides security when authenticating. | ``getPassword()`` only valid on creation | ``setPassword()`` to set local property only. To set password on API (retention), use ``updatePassword()``. | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| defaultRegion | Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region. | ``getDefaultRegion()`` | ``setDefaultRegion()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| domainId | Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account. | ``getDomainId()`` | ``setDomainId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ + +List users +---------- + +.. code:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get user +-------- + +There are various ways to get a specific user: by name, ID and email +address. + +.. code:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + // Get user by name + $user1 = $service->getUser('jamie'); + + // Get user by ID + $user2 = $service->getUser(123456, UserConst::MODE_ID); + + // Get user by email + $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); + +Create user +----------- + +There are a few things to remember when creating a user: + +- This operation is available only to users who hold the + ``identity:user-admin`` role. This admin can create a user who holds + the ``identity:default`` user role. + +- The created user **will** have access to APIs but **will not** have + access to the Cloud Control Panel. + +- Within an account, a maximum of 100 account users can be added. + +- If you attempt to add a user who already exists, an HTTP error 409 + results. + +The ``username`` and ``email`` properties are required for creating a +user. Providing a ``password`` is optional; if omitted, one will be +automatically generated and provided in the response. + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + // execute operation + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + } catch (ClientErrorResponseException $e) { + // catch 4xx HTTP errors + echo $e->getResponse()->toString(); + } + + // show generated password + echo $user->getPassword(); + +Update user +----------- + +When updating a user, specify which attribute/property you want to +update: + +.. code:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); + +Updating a user password +~~~~~~~~~~~~~~~~~~~~~~~~ + +Updating a user password requires calling a distinct method: + +.. code:: php + + $user->updatePassword('password123'); + +Delete user +----------- + +.. code:: php + + $user->delete(); + +List credentials +---------------- + +This operation allows you to see your non-password credential types for +all authentication methods available. + +.. code:: php + + $creds = $user->getOtherCredentials(); + +Get user API key +---------------- + +.. code:: php + + echo $user->getApiKey(); + +Reset user API key +------------------ + +When resetting an API key, a new one will be automatically generated for +you: + +.. code:: php + + $user->resetApiKey(); + echo $user->getApiKey(); + diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/image/Images.md.rst b/doc/services/image/Images.md.rst new file mode 100644 index 000000000..ef187f083 --- /dev/null +++ b/doc/services/image/Images.md.rst @@ -0,0 +1,107 @@ +Images +====== + +A virtual machine image is a single file which contains a virtual disk +that has an installed bootable operating system. In the Cloud Images +API, an image is represented by a JSON-encoded data structure (the image +schema) and its raw binary data (the image file). + +An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. + +Setup +----- + +You instantiate an Image object from its ``OpenCloud\Image\Service`` +class, which is available from the OpenStack/Rackspace client: + +.. code:: php + + $service = $client->imageService('cloudImages', 'IAD'); + +View the guides for more information about `clients <../Clients.md>`__ +or `services <../Services.md>`__. + +List images +----------- + +.. code:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Get image details +----------------- + +.. code:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage(''); + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and +``OpenCloud\Image\Resource\Member`` extend the +``AbstractSchemaResource`` abstract class, which offers some unique +functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a way +different than conventional getter/setter methods, and even class +properties. For this reason, they implement SPL's native +```ArrayAccess`` `__ +interface which allows you to access their state as a conventional +array: + +.. code:: php + + $image = $service->getImage(''); + + $id = $image['id']; + $tags = $image['tags']; + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +- If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +- If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code:: php + + $image->delete(); + diff --git a/doc/services/image/Schemas.md.rst b/doc/services/image/Schemas.md.rst new file mode 100644 index 000000000..8f792c4c0 --- /dev/null +++ b/doc/services/image/Schemas.md.rst @@ -0,0 +1,170 @@ +JSON schemas +============ + +The Cloud Images API supplies json documents describing the JSON-encoded +data structures that represent domain objects, so that a client knows +exactly what to expect in an API response. + +A JSON Schema is represented by the +``OpenCloud\Image\Resource\Schema\Schema`` class. + +Schema types +------------ + +There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema. + +Example response from the API +----------------------------- + +A sample response from the API, for an Images schema might be: + +.. code:: json + + { + "name": "images", + "properties": { + "images": { + "items": { + "type": "array", + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + }, + "schema": {"type": "string"}, + "next": {"type": "string"}, + "first": {"type": "string"} + }, + "links": [ + {"href": "{first}", "rel": "first"}, + {"href": "{next}", "rel": "next"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + +The top-level schema is called ``images``, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level ``images`` object. So we know that it will +take this form: + +.. code:: json + + { + "images": [something...] + } + +Within this object, we can see that it contains an array of anonymous +objects, each of which is called ``image`` and has its own set of nested +properties: + +.. code:: json + + { + "images": [ + { + [object 1...] + }, + { + [object 2...] + }, + { + [object 3...] + } + ] + } + +The structure of these nested objects are defined as another schema - +i.e. a *subschema*. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +``private`` or ``public``), etc. + +.. code:: json + + { + "images": [ + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + } + ] + } + +Each nested property of a schema is represented by the +``OpenCloud\Image\Resource\Schema\Property`` class. + +If you would like to find out more about schemas, Guzzle has good +documentation about `service +descriptions `__, +which is fairly analogous. + +JSON Patch +---------- + +The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in `RFC +6902 `__. + +Requests need to use the +``application/openstack-images-v2.1-json-patch`` content-type. + +In order for the operation to occur, the request entity body needs to +contain a very particular structure: + +:: + + [ + {"op": "replace", "path": "/name", "value": "Fedora 17"}, + {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} + ] + +The ``op`` key refers to the type of Operation (see +``OpenCloud\Image\Enum\OperationType`` for a full list). + +The ``path`` key is a JSON pointer to the document property you want to +modify or insert. JSON pointers are defined in `RFC +6901 `__. + +The ``value`` key is the value. + +Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the `official +documentation `__ +for additional information. diff --git a/doc/services/image/Sharing.md.rst b/doc/services/image/Sharing.md.rst new file mode 100644 index 000000000..b1a1ea382 --- /dev/null +++ b/doc/services/image/Sharing.md.rst @@ -0,0 +1,129 @@ +Sharing images +============== + +Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers: + ++-------------+-----------------+-----------------+ +| Operation | Producer can? | Consumer can? | ++=============+=================+=================+ +| Created | Yes | No | ++-------------+-----------------+-----------------+ +| Deleted | Yes | No | ++-------------+-----------------+-----------------+ +| Updated | No | Yes | ++-------------+-----------------+-----------------+ +| Listed | Yes | Yes | ++-------------+-----------------+-----------------+ + +The producer shares an image with the consumer by making the consumer a +*member* of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer's image list. + +Typical workflow +---------------- + +1. The producer posts the availability of specific images on a public + website. + +2. A potential consumer provides the producer with his/her tenant ID and + email address. + +3. The producer `creates a new Image Member <>`__ with the consumer's + details + +4. The producer notifies the consumer via email that the image has been + shared and provides the image's ID. + +5. If the consumer wishes the image to appear in his/her image list, the + consumer `updates their own Member status <>`__ to ``ACCEPTED``. + +Additional notes +~~~~~~~~~~~~~~~~ + +- If the consumer subsequently wishes to hide the image, the consumer + can change their Member status to ``REJECTED``. + +- If the consumer wishes to hide the image, but is open to the + possibility of being reminded by the producer that the image is + available, the consumer can change their Member status to + ``PENDING``. + +- Image producers add or remove image members, but may not modify the + member status of an image member. + +- Image consumers change their own member status, but may not add or + remove themselves as an image member. + +- Image consumers can boot from any image shared by the image producer, + regardless of the member status, as long as the image consumer knows + the image ID. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +List image members +------------------ + +This operation is available for both producers and consumers. + +.. code:: php + + $members = $image->listMembers(); + + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Create image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember($tenantId); + +Delete image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->delete(); + +Update image member status +-------------------------- + +This operation is only available for consumers. + +.. code:: php + + use OpenCloud\Images\Enum\MemberStatus; + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->updateStatus(MemberStatus::ACCEPTED); + +The acceptable states you may pass in are made available to you through +the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` +class. diff --git a/doc/services/image/Tags.md.rst b/doc/services/image/Tags.md.rst new file mode 100644 index 000000000..af5baf157 --- /dev/null +++ b/doc/services/image/Tags.md.rst @@ -0,0 +1,28 @@ +Image tags +========== + +An image tag is a string of characters used to identify a specific image +or images. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +Add image tag +------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); + diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst new file mode 100644 index 000000000..e69de29bb From 686a5bf839ee5b768051535e5905dab5d833345f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:26 +0100 Subject: [PATCH 709/835] Add remaining --- doc/services/load-balancer/README.md.rst | 92 ++ doc/services/load-balancer/USERGUIDE.md.rst | 840 ++++++++++++++++ doc/services/load-balancer/index.rst | 0 doc/services/monitoring/Agents.md.rst | 202 ++++ doc/services/monitoring/Alarms.md.rst | 81 ++ doc/services/monitoring/Changelogs.md.rst | 21 + doc/services/monitoring/Checks.md.rst | 211 ++++ doc/services/monitoring/Entities.md.rst | 77 ++ doc/services/monitoring/Metrics.md.rst | 131 +++ doc/services/monitoring/Notifications.md.rst | 281 ++++++ doc/services/monitoring/Service.md.rst | 23 + doc/services/monitoring/Views.md.rst | 27 + doc/services/monitoring/Zones.md.rst | 67 ++ doc/services/monitoring/index.rst | 0 doc/services/networking/README.md.rst | 108 +++ doc/services/networking/USERGUIDE.md.rst | 582 +++++++++++ doc/services/networking/index.rst | 0 doc/services/object-store/Access.md.rst | 75 ++ doc/services/object-store/Account.md.rst | 33 + .../object-store/Container.md.cdn.rst | 90 ++ .../object-store/Container.md.storage.rst | 218 +++++ .../object-store/Migrating.md.storage.rst | 101 ++ doc/services/object-store/Object.md.cdn.rst | 25 + .../object-store/Object.md.storage.rst | 330 +++++++ doc/services/object-store/README.md.rst | 86 ++ doc/services/object-store/USERGUIDE.md.rst | 900 ++++++++++++++++++ doc/services/object-store/index.rst | 0 doc/services/orchestration/README.md.rst | 98 ++ doc/services/orchestration/USERGUIDE.md.rst | 672 +++++++++++++ doc/services/orchestration/index.rst | 0 doc/services/queues/Claim.md.rst | 164 ++++ doc/services/queues/Message.md.rst | 257 +++++ doc/services/queues/Queue.md.rst | 197 ++++ doc/services/queues/index.rst | 0 doc/services/volume/index.rst | 0 35 files changed, 5989 insertions(+) create mode 100644 doc/services/load-balancer/README.md.rst create mode 100644 doc/services/load-balancer/USERGUIDE.md.rst create mode 100644 doc/services/load-balancer/index.rst create mode 100644 doc/services/monitoring/Agents.md.rst create mode 100644 doc/services/monitoring/Alarms.md.rst create mode 100644 doc/services/monitoring/Changelogs.md.rst create mode 100644 doc/services/monitoring/Checks.md.rst create mode 100644 doc/services/monitoring/Entities.md.rst create mode 100644 doc/services/monitoring/Metrics.md.rst create mode 100644 doc/services/monitoring/Notifications.md.rst create mode 100644 doc/services/monitoring/Service.md.rst create mode 100644 doc/services/monitoring/Views.md.rst create mode 100644 doc/services/monitoring/Zones.md.rst create mode 100644 doc/services/monitoring/index.rst create mode 100644 doc/services/networking/README.md.rst create mode 100644 doc/services/networking/USERGUIDE.md.rst create mode 100644 doc/services/networking/index.rst create mode 100644 doc/services/object-store/Access.md.rst create mode 100644 doc/services/object-store/Account.md.rst create mode 100644 doc/services/object-store/Container.md.cdn.rst create mode 100644 doc/services/object-store/Container.md.storage.rst create mode 100644 doc/services/object-store/Migrating.md.storage.rst create mode 100644 doc/services/object-store/Object.md.cdn.rst create mode 100644 doc/services/object-store/Object.md.storage.rst create mode 100644 doc/services/object-store/README.md.rst create mode 100644 doc/services/object-store/USERGUIDE.md.rst create mode 100644 doc/services/object-store/index.rst create mode 100644 doc/services/orchestration/README.md.rst create mode 100644 doc/services/orchestration/USERGUIDE.md.rst create mode 100644 doc/services/orchestration/index.rst create mode 100644 doc/services/queues/Claim.md.rst create mode 100644 doc/services/queues/Message.md.rst create mode 100644 doc/services/queues/Queue.md.rst create mode 100644 doc/services/queues/index.rst create mode 100644 doc/services/volume/index.rst diff --git a/doc/services/load-balancer/README.md.rst b/doc/services/load-balancer/README.md.rst new file mode 100644 index 000000000..862cf20d2 --- /dev/null +++ b/doc/services/load-balancer/README.md.rst @@ -0,0 +1,92 @@ +Load Balancers +============== + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Retrieve the server instances you want to add as nodes of the load balancer. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); + + $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); + $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); + +The example above uses two server instances that have already been +created. It retrieves the server instances using their IDs. See also: +`creating server instances <>`__. + +3. Obtain a Load Balancer service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This object will be used to first define the load balancer nodes and +later create the load balancer itself. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +4. Define a load balancer node for each server. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + +In the example above, each node runs a service that listens on port +8080. Further, each node will start out as ``ENABLED``, which means it +will be ready to receive network traffic from the load balancer as soon +as it is created. + +5. Create the load balancer with the two nodes. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +In the example above, the load balancer will have a virtual IP address +accessible from the public Internet. Also notice that the port the load +balancer listens on (80) does not need to match the ports of its nodes +(8080). + +Next steps +---------- + +Once you have created a load balancer, there is a lot you can do with +it. See the `complete user guide for load balancers `__. diff --git a/doc/services/load-balancer/USERGUIDE.md.rst b/doc/services/load-balancer/USERGUIDE.md.rst new file mode 100644 index 000000000..788651f2b --- /dev/null +++ b/doc/services/load-balancer/USERGUIDE.md.rst @@ -0,0 +1,840 @@ +The Complete User Guide to Load Balancers +========================================= + +Prerequisites +------------- + +Client +~~~~~~ + +To use the load balancers service, you must first instantiate a +``Rackspace`` client object. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Load Balancer Service +~~~~~~~~~~~~~~~~~~~~~ + +All operations on load balancers are done via a load balancer service +object. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +Cloud Servers +~~~~~~~~~~~~~ + +Many of the examples in this document use two cloud servers as nodes for +the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer +to these two cloud servers. + +Load Balancers +-------------- + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Create Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +List Load Balancer Details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single load balancer's details by using its ID. + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer('254889'); + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $loadBalancers = $loadBalancerService->loadBalancerList(); + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + +Update Load Balancer Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +Updating a single attribute of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name' + )); + +Updating multiple attributes of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it. + +.. code:: php + + $loadBalancer->delete(); + +Nodes +----- + +A **node** is a backend device that provides a service on specified IP +and port. An example of a load balancer node might be a web server +serving HTTP traffic on port 8080. + +A load balancer typically has multiple nodes attached to it so it can +distribute incoming network traffic amongst them. + +List Nodes +~~~~~~~~~~ + +You can list the nodes attached to a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodes = $loadBalancer->nodeList(); + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + +Add Nodes +~~~~~~~~~ + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| --------------\| -------------- \| +----------------- \| +|  1 \| IP address of node \| String \| Yes \| - \| +|  2 \| Port used by node's service \| Integer \| Yes \| - \| +|  3 \| Starting condition of node: +|  4 \| Type of node to add: +|  5 \| Weight, between 1 and 100, given to node when distributing +traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the +``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| +No \| 1 \| + +Modify Nodes +~~~~~~~~~~~~ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +Modifying a single attribute of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + + $node->update(array( + 'condition' => NodeCondition::DISABLED + )); + +Modifying multiple attributes of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + +Remove Nodes +~~~~~~~~~~~~ + +There are two ways to remove a node. + +Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $node->delete(); + +Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->removeNode(490639); + +The ``removeNode`` method, as shown above, accepts the following +arguments: + ++------------+---------------+-------------+-------------+-----------------+ +| Position | Description | Data type | Required? | Default value | ++============+===============+=============+=============+=================+ +| 1 | ID of node | Integer | Yes | - | ++------------+---------------+-------------+-------------+-----------------+ + +View Node Service Events +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can view events associated with the activity between a node and a +load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } + +Virtual IPs +----------- + +A **virtual IP (VIP)** makes a load balancer accessible by clients. The +load balancing service supports either a public VIP address +(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP +address (``SERVICENET``), routable only within the region in which the +load balancer resides. + +List Virtual IPs +~~~~~~~~~~~~~~~~ + +You can list the VIPs associated with a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $vips = $loadBalancer->virtualIpList(); + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + +Add Virtual IPv6 +~~~~~~~~~~~~~~~~ + +You can add additional IPv6 VIPs to a load balancer. + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +The ``addVirtualIp`` method, as shown above, accepts the following +arguments: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| -------------- \|-------------- \| +----------------- \| +|  1 \| Type of VIP: +|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| + +Remove Virtual IPs +~~~~~~~~~~~~~~~~~~ + +You can remove a VIP from a load balancer. + +.. code:: php + + $vip->remove(); + +Please note that a load balancer must have at least one VIP associated +with it. If you try to remove a load balancer's last VIP, a +``ClientErrorResponseException`` will be thrown. + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported load balancing algorithms using a load +balancer service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $algorithms = $loadBalancerService->algorithmList(); + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols using a load balancer +service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $protocols = $loadBalancerService->protocolList(); + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + +Session Persistence +------------------- + +**Session persistence** is a feature of the load balancing service that +forces multiple requests, of the same protocol, from clients to be +directed to the same node. This is common with many web applications +that do not inherently share application state between back-end servers. + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +List Session Persistence Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistenceType = $sessionPersistence->persistenceType; + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + +Enable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + +Disable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); + +Connection Logging +------------------ + +The **connection logging** feature allows logs to be delivered to a +Cloud Files account every hour. For HTTP-based protocol traffic, these +are Apache-style access logs. For all other traffic, this is connection +and transfer logging. + +Check Logging Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $connectionLogging bool **/ + + $connectionLogging = $loadBalancer->hasConnectionLogging(); + +In the example above: + +- If connection logging is enabled, the value of ``$connectionLogging`` + is ``true``. +- If connection logging is disabled, the value of + ``$connectionLogging`` is ``false``. + +Enable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(true); + +Disable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(false); + +Error Page +---------- + +An **error page** is the html file that is shown to the end user when an +error in the service has been thrown. By default every virtual server is +provided with the default error file. It is also possible to set a +custom error page for a load balancer. + +View Error Page Content +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + +Set Custom Error Page +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + +Delete Custom Error Page +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); + +Allowed Domains +--------------- + +**Allowed domains** are a restricted set of domain names that are +allowed to add load balancer nodes. + +List Allowed Domains +~~~~~~~~~~~~~~~~~~~~ + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code:: php + + $allowedDomains = $loadBalancerService->allowedDomainList(); + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + +Access Lists +------------ + +**Access Lists** allow fine-grained network access to a load balancer's +VIP. Using access lists, network traffic to a load balancer's VIP can be +allowed or denied from a single IP address, multiple IP addresses or +entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` +network items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +View Access List +~~~~~~~~~~~~~~~~ + +You can view a load balancer's access list. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $accessList = $loadBalancer->accessList(); + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + +Add Network Items To Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can add network items to a load balancer's access list very easily: + +.. code:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + +Remove Network Item From Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You an remove a network item from a load balancer's access list. + +.. code:: php + + $networkItem->delete(); + +Content Caching +--------------- + +When **content caching** is enabled on a load balancer, +recently-accessed files are stored on the load balancer for easy +retrieval by web clients. Requests to the load balancer for these files +are serviced by the load balancer itself, which reduces load off its +back-end nodes and improves response times as well. + +Check Content Caching Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $contentCaching bool **/ + + $contentCaching = $loadBalancer->hasContentCaching(); + +In the example above: + +- If content caching is enabled, the value of ``$contentCaching`` is + ``true``. +- If content caching is disabled, the value of ``$contentCaching`` is + ``false``. + +Enable Content Caching +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(true); + +Disable Content Caching +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(false); + +SSL Termination +--------------- + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +View current SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + + $sslConfig = $loadBalancer->SSLTermination(); + +Update SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Delete SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->delete(); + +Metadata +-------- + +Metadata can be associated with each load balancer and each node for the +client's personal use. It is defined using key-value pairs where the key +and value consist of alphanumeric characters. A key is unique per load +balancer. + +List metadata +~~~~~~~~~~~~~ + +.. code:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add metadata +~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + +Modify metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + +Remove metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem->delete(); + +Monitors +-------- + +The load balancing service includes a health monitoring operation which +periodically checks your back-end nodes to ensure they are responding +correctly. If a node is not responding, it is removed from rotation +until the health monitor determines that the node is functional. In +addition to being performed periodically, the health check also is +performed against every node that is added to ensure that the node is +operating properly before allowing it to service traffic. Only one +health monitor is allowed to be enabled on a load balancer at a time. + +.. code:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Update or delete +~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + // Delete + $healthMonitor->delete(); + +Statistics +---------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect\_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load + balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive\_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + + $stats = $loadBalancer->stats(); + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } + diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/monitoring/Agents.md.rst b/doc/services/monitoring/Agents.md.rst new file mode 100644 index 000000000..943983f7e --- /dev/null +++ b/doc/services/monitoring/Agents.md.rst @@ -0,0 +1,202 @@ +Agents +====== + +Intro +----- + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Setup +----- + +.. code:: php + + $agentId = '00-agent.example.com'; + $agent = $service->getAgent($agentId); + +You can view the `service page `__ for more information +about setting up the Cloud Monitoring service. + +List agents +----------- + +.. code:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +List connections +---------------- + +.. code:: php + + $connections = $agent->getConnections(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Get connection +-------------- + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('cntl4qsIbA'); + +Agent Connection properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------+---------------+--------+---------------------------+ +| Name | Description | Type | Method | ++====================+===============+========+===========================+ +| id | - | - | ``getId()`` | ++--------------------+---------------+--------+---------------------------+ +| guid | - | - | ``getGuid()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_id | - | - | ``getAgentId()`` | ++--------------------+---------------+--------+---------------------------+ +| endpoint | - | - | ``getEndpoint()`` | ++--------------------+---------------+--------+---------------------------+ +| process\_version | - | - | ``getProcessVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| bundle\_version | - | - | ``getBundleVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_ip | - | - | ``getAgentIp()`` | ++--------------------+---------------+--------+---------------------------+ + +Agent tokens +============ + +Intro +----- + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Setup +----- + +.. code:: php + + $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; + $agentToken = $service->getAgentToken($tokenId); + +Create agent token +------------------ + +.. code:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + +List agent tokens +----------------- + +.. code:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +Update and delete Agent Token +----------------------------- + +.. code:: php + + // Update + $token->update(array( + 'label' => 'New label' + )); + + // Delete + $token->delete(); + +Agent Host Information +====================== + +Info +---- + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code:: php + + $host = $monitoringService->getAgentHost(); + +Get some metrics +---------------- + +.. code:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Info +---- + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } + diff --git a/doc/services/monitoring/Alarms.md.rst b/doc/services/monitoring/Alarms.md.rst new file mode 100644 index 000000000..2918637e9 --- /dev/null +++ b/doc/services/monitoring/Alarms.md.rst @@ -0,0 +1,81 @@ +Alarms +====== + +Info +---- + +Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (``OK``, +``WARNING`` or ``CRITICAL``) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the `reference +documentation `__. + +Setup +----- + +Alarms are sub-resources of Entities: + +.. code:: php + + $alarmId = 'alAAAA'; + $alarm = $check->getAlarm(); + +For more information about working with Checks, please see the +`appropriate documentation `__. + +Attributes +---------- + ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| Name | Description | Required? | Method | ++==========================+=============================================================================+=============+=================================+ +| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| disabled | Disable processing and alerts on this alarm | Optional | ``isDisabled()`` <``bool``\ > | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| label | A friendly label for an alarm. | Optional | ``getLabel()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + +Create Alarm +------------ + +.. code:: php + + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); + +List Alarms +----------- + +.. code:: php + + $alarms = $entity->getAlarms(); + + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } + +Update and delete Alarm +----------------------- + +.. code:: php + + // Update + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + // Delete + $alarm->delete(); + diff --git a/doc/services/monitoring/Changelogs.md.rst b/doc/services/monitoring/Changelogs.md.rst new file mode 100644 index 000000000..2e9059e26 --- /dev/null +++ b/doc/services/monitoring/Changelogs.md.rst @@ -0,0 +1,21 @@ +Changelogs +========== + +Info +---- + +The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information. + +Working with Changelogs +----------------------- + +.. code:: php + + $changelog = $service->getChangelog(); + + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } + diff --git a/doc/services/monitoring/Checks.md.rst b/doc/services/monitoring/Checks.md.rst new file mode 100644 index 000000000..0e8e902a5 --- /dev/null +++ b/doc/services/monitoring/Checks.md.rst @@ -0,0 +1,211 @@ +Checks +====== + +Info +---- + +A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information: + +- A name for the check +- The check's parent entity +- The type of check you're creating +- Details of the check +- The monitoring zones that will launch the check + +The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate). + +Setup +----- + +Checks are sub-resources of Entities: + +.. code:: php + + $checkId = 'chAAAA'; + $check = $entity->getCheck($checkId); + +Attributes +---------- + ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| Name | Description | Required? | Data type | ++============+=============================================================================================================+=============+==========================================+ +| type | The type of check. | Required | Valid check type. String (1..25 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| details | Details specific to the check type. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| disabled | Disables the check. | Optional | Boolean | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| label | A friendly label for a check. | Optional | String (1..255 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| period | The period in seconds for a check. The value must be greater than the minimum period set on your account. | Optional | Integer (30..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ + +Attributes used for remote Checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| Name | Description | Required? | Data type | ++===========================+========================================================================================================================================================+=============+============================================================+ +| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ + +Test parameters (before create) +------------------------------- + +.. code:: php + + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); + + // You can do a test to see what would happen + // if a Check is launched with these params + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + +Create a Check +-------------- + +.. code:: php + + $entity->createCheck($params); + +Test existing Check +------------------- + +.. code:: php + + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; + +List Checks +----------- + +.. code:: php + + $checks = $entity->getChecks(); + + foreach ($checks as $check) { + echo $check->getId(); + } + +Update and delete Check +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $check->update(array('period' => 500)); + + // Delete + $check->delete(); + +Check types +=========== + +Info +---- + +Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. **Note:** Users cannot create, update or delete +check types. + +Check types for commonly encountered web protocols, such as HTTP +(``remote.http``), IMAP (``remote.imap-banner``) , SMTP +(``remote.stmp``), and DNS (``remote.dns``) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(``remote.mysql-banner``) and PostgreSQL (``remote.postgresql-banner``) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the 'extract' attribute to define the format. + +In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +`official API +documentation `__. + +Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check's parameters. For example, +using the 'extract' attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided. + +Setup +----- + +If you want to see the type for an existing Check: + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + +Alternatively, you can retrieve a specific type based on its ID: + +.. code:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); + +Attributes +---------- + ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| Name | Description | Data type | Method | ++========================+==========================================================================================================================================================================================+=============+===============================+ +| type | The name of the supported check type. | String | ``getType()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| fields | Check type fields. | Array | ``getFields()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ + +List all possible check types +----------------------------- + +.. code:: php + + $checkTypes = $service->getCheckTypes(); + + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } + diff --git a/doc/services/monitoring/Entities.md.rst b/doc/services/monitoring/Entities.md.rst new file mode 100644 index 000000000..068aec122 --- /dev/null +++ b/doc/services/monitoring/Entities.md.rst @@ -0,0 +1,77 @@ + Entities +========= + +Info +---- + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Setup +----- + +.. code:: php + + $entity = $service->getEntity(); + +For more information about setting up the ``$service`` object, please +see the userguide tutorial for `services `__. + +Attributes +---------- + ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| Name | Description | Required? | Data type | Method | ++=================+=========================================================================+=============+=====================================================+========================+ +| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ + +Create Entity +------------- + +.. code:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + +Update and delete Entity +------------------------ + +.. code:: php + + // Update + $entity->update(array( + 'label' => 'New label for my entity' + )); + + // Delete + $entity->delete(); + diff --git a/doc/services/monitoring/Metrics.md.rst b/doc/services/monitoring/Metrics.md.rst new file mode 100644 index 000000000..4231cf914 --- /dev/null +++ b/doc/services/monitoring/Metrics.md.rst @@ -0,0 +1,131 @@ + Metrics +======== + +Info +---- + +When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points. + +Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time. + +Data Granularity +---------------- + +Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. + +When you fetch metrics data points, you specify several parameters to +control the granularity of data returned: + +- A time range for the points +- Either the number of points you want returned OR the resolution of + the data you want returned + +When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution. + +**Note:** Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request. + +Consider that you want to query data for a 48-hour time range between +the timestamps ``from=1354647221000`` and ``to=1358794421000`` ( +**specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970** ). The following table shows the number +of points that the API returns for a given resolution. + +Specifying resolution to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-----------------------------+-------------------------+ +| You specify resolution... | API returns points... | ++=============================+=========================+ +| FULL | 5760 | ++-----------------------------+-------------------------+ +| MIN5 | 576 | ++-----------------------------+-------------------------+ +| MIN20 | 144 | ++-----------------------------+-------------------------+ +| MIN60 | 48 | ++-----------------------------+-------------------------+ +| MIN240 | 12 | ++-----------------------------+-------------------------+ +| MIN1440 | 2 | ++-----------------------------+-------------------------+ + +Specifying number of points to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++--------------------------------------+-----------------------------+ +| You specify points in the range... | API calculates resolution | ++======================================+=============================+ +| 3168-∞ | FULL | ++--------------------------------------+-----------------------------+ +| 360-3167 | MIN5 | ++--------------------------------------+-----------------------------+ +| 96-359 | MIN20 | ++--------------------------------------+-----------------------------+ +| 30-95 | MIN60 | ++--------------------------------------+-----------------------------+ +| 7-29 | MIN240 | ++--------------------------------------+-----------------------------+ +| 0-6 | MIN1440 | ++--------------------------------------+-----------------------------+ + +Data Point Expiration +^^^^^^^^^^^^^^^^^^^^^ + +Cloud Monitoring expires data points according to the following +schedule: + ++--------------+--------------+ +| Resolution | Expiration | ++==============+==============+ +| FULL | 2 days | ++--------------+--------------+ +| MIN5 | 7 days | ++--------------+--------------+ +| MIN20 | 15 days | ++--------------+--------------+ +| MIN60 | 30 days | ++--------------+--------------+ +| MIN240 | 60 days | ++--------------+--------------+ +| MIN1440 | 365 days | ++--------------+--------------+ + + Setup +------ + +Metrics are sub-resources of Checks. For more information about working +with Checks, please see the `relevant documentation `__. + +List all metrics +---------------- + +.. code:: php + + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } + +Fetch data points +----------------- + +.. code:: php + + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); + diff --git a/doc/services/monitoring/Notifications.md.rst b/doc/services/monitoring/Notifications.md.rst new file mode 100644 index 000000000..f361e3c9c --- /dev/null +++ b/doc/services/monitoring/Notifications.md.rst @@ -0,0 +1,281 @@ +Notifications +============= + +Info +---- + +A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time. + +For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from ``OK`` -> ``CRITICAL`` and so on). + +Setup +----- + +.. code:: php + + $id = 'ntAAAA'; + $notification = $service->getNotification($id); + +Attributes +---------- + ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| Name | Description | Data type | Method | ++===========+===========================================================================+===========================================================+====================+ +| details | A hash of notification specific details based on the notification type. | Array | ``getDetails()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| label | Friendly name for the notification. | String (1..255 chars) | ``getLabel()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ + +Test parameters +--------------- + +.. code:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + // Test it + $response = $notification->testParams($params); + + if ($response->status == 'Success') { + echo $response->message; + } + +Create Notification +------------------- + +.. code:: php + + $notification->create($params); + +Test existing notification +-------------------------- + +.. code:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; + +List Notifications +------------------ + +.. code:: php + + $notifications = $service->getNotifications(); + + foreach ($notifications as $notification) { + echo $notification->getId(); + } + +Update and delete Notifications +------------------------------- + +.. code:: php + + // Update + $notification->update(array( + 'label' => 'New notification label' + )); + + // Delete + $notification->delete(); + +Notification types +================== + +Info +---- + +Pretty self-explanatory. Rackspace Cloud Monitoring currently supports +the following notification types: + +Webhook +^^^^^^^ + +Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes: + ++-----------+------------------------------------------+---------------+ +| Name | Description | Data type | ++===========+==========================================+===============+ +| address | Email address to send notifications to | Valid email | ++-----------+------------------------------------------+---------------+ + +Email +^^^^^ + +Email alerts where the message is delivered to a specified address. It +has these attributes: + ++--------+-----------------------------------+-------------+ +| Name | Description | Data type | ++========+===================================+=============+ +| url | An HTTP or HTTPS URL to POST to | Valid URL | ++--------+-----------------------------------+-------------+ + +Setup +----- + +If you've already set up a main Notification object, and want to access +functionality for this Notification's particular Notification Type, you +can access its property: + +.. code:: php + + $type = $notification->getNotificationType(); + +Alternatively, you can retrieve an independent resource using the ID: + +.. code:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); + +List all possible notification types +------------------------------------ + +.. code:: php + + $types = $service->getNotificationTypes(); + + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } + +Notification plans +================== + +Info +---- + +A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications. + +Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states: + +- Critical +- Warning +- OK + +A notification plan, ``npTechnicalContactsEmail``, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change. + +Setup +----- + +.. code:: php + + $planId = 'npAAAA'; + $plan = $service->getNotificationPlan(); + +Attributes +---------- + ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| Name | Description | Required? | Data type | Method | ++===================+====================================================================+=============+=========================+==========================+ +| label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ + +Create Notification Plan +------------------------ + +.. code:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + +Update and delete Notification Plan +----------------------------------- + +.. code:: php + + // Update + $plan->update(array( + 'label' => 'New label for my plan' + )); + + // Delete + $plan->delete(); + +Alarm Notification History +========================== + +Info +---- + +The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm's statuses. + +Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information. + + Setup +------ + +Notification History is a sub-resource of an Alarm. For more information +about working with Alarms, please consult the relevant +`documentation `__. + +Discover which Checks have a Notification History +------------------------------------------------- + +This operation list checks for which alarm notification history is +available: + +.. code:: php + + $checks = $alarm->getRecordedChecks(); + +List Alarm Notification History for a particular Check +------------------------------------------------------ + +.. code:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); + +Get a particular Notification History item +------------------------------------------ + +.. code:: php + + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + diff --git a/doc/services/monitoring/Service.md.rst b/doc/services/monitoring/Service.md.rst new file mode 100644 index 000000000..09c25c21d --- /dev/null +++ b/doc/services/monitoring/Service.md.rst @@ -0,0 +1,23 @@ +Cloud Monitoring Service +======================== + +Initializing the Cloud Monitoring is easy - and can be done in a similar +way to all other Rackspace services: + +1. Create client and pass in auth details. For more information about + creating clients, please consult the `Client + documentation <../Clients.md>`__. +2. Use the factory method, specifying additional parameters where + necessary: + +.. code:: php + + $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); + +All three parameters are optional - if not specified, it will revert to +the service's default values which are: + +- Name = ``cloudMonitoring`` +- Region = ``DFW`` +- URL type = ``publicURL`` + diff --git a/doc/services/monitoring/Views.md.rst b/doc/services/monitoring/Views.md.rst new file mode 100644 index 000000000..a5b7245fa --- /dev/null +++ b/doc/services/monitoring/Views.md.rst @@ -0,0 +1,27 @@ +Views +===== + +Info +---- + +Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint. + +List all Views +-------------- + +\`\`\`php $views = $service->getViews(); + +foreach ($views as $view) { $entity = $view->getEntity(); + +:: + + echo $view->getTimestamp(); + +} \`\`\` + +Please consult the `iterator doc `__ for +more information about iterators. diff --git a/doc/services/monitoring/Zones.md.rst b/doc/services/monitoring/Zones.md.rst new file mode 100644 index 000000000..ffe4bba1e --- /dev/null +++ b/doc/services/monitoring/Zones.md.rst @@ -0,0 +1,67 @@ +Zones +===== + +Info +---- + +A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". +It is an abstraction for a general location from which data is +collected. + +An "endpoint," also known as a "collector," collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range. + +A check references a list of monitoring zones it should be run from. + + Setup +------ + +.. code:: php + + $zoneId = 'mzAAAAA'; + $zone = $monitoringService->getMonitoringZone($zoneId); + +Attributes +---------- + ++-----------------+------------------+-----------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+==================+===================================+========================+ +| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| label | Label | String | ``getLabel()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| source\_ips | Source IP list | Array | ``getSourceIps()`` | ++-----------------+------------------+-----------------------------------+------------------------+ + + List all zones +--------------- + +.. code:: php + + $zones = $service->getMonitoringZones(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Perform a traceroute +-------------------- + +.. code:: php + + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); + + // How many hops? + echo count($traceroute); + + // What was the first hop's IP? + echo $traceroute[0]->ip; + diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/networking/README.md.rst b/doc/services/networking/README.md.rst new file mode 100644 index 000000000..605805fe1 --- /dev/null +++ b/doc/services/networking/README.md.rst @@ -0,0 +1,108 @@ +Networking +========== + +**Networking** is a service that you can use to create virtual networks +and attach cloud devices such as servers to these networks. + +Concepts +-------- + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: A network is an isolated virtual layer-2 broadcast + domain that is typically reserved for the tenant who created it + unless you configure the network to be shared. The network is the + main entity in the Networking service. Ports and subnets are always + associated with a network. + +- **Subnet**: A subnet represents an IP address block that can be used + to assign IP addresses to virtual instances (such as servers created + using the Compute service). Each subnet must have a CIDR and must be + associated with a network. + +- **Port**: A port represents a virtual switch port on a logical + network switch. Virtual instances (such as servers created using the + Compute service) attach their interfaces into ports. The port also + defines the MAC address and the IP address(es) to be assigned to the + interfaces plugged into them. When IP addresses are associated to a + port, this also implies the port is associated with a subet, as the + IP address is taken from the allocation pool for a specific subnet. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Networking service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via an *networking service object*. +To instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance will be stored in the cloud region +specified by ``$region``. + +3. Create a network. +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a network, there is more you can do with it. See +`complete user guide for networking `__. diff --git a/doc/services/networking/USERGUIDE.md.rst b/doc/services/networking/USERGUIDE.md.rst new file mode 100644 index 000000000..bfeb27774 --- /dev/null +++ b/doc/services/networking/USERGUIDE.md.rst @@ -0,0 +1,582 @@ +Complete User Guide for the Networking Service +============================================== + +Networking is a service that you can use to create virtual networks and +attach cloud devices such as servers to these networks. + +This user guide introduces you the entities in the Networking service — +networks, subnets, and ports — and shows you how to create and manage +these entities. + +Table of contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ + + - `Client <#client>`__ + - `Networking service <#networking-service>`__ + +- `Networks <#networks>`__ + + - `Create a network <#create-a-network>`__ + - `Create multiple networks <#create-multiple-networks>`__ + - `List networks <#list-networks>`__ + - `Get a network <#get-a-network>`__ + - `Update a network <#update-a-network>`__ + - `Delete a network <#delete-a-network>`__ + +- `Subnets <#subnets>`__ + + - `Create a subnet <#create-a-subnet>`__ + - `Create multiple subnets <#create-multiple-subnets>`__ + - `List subnets <#list-subnets>`__ + - `Get a subnet <#get-a-subnet>`__ + - `Update a subnet <#update-a-subnet>`__ + - `Delete a subnet <#delete-a-subnet>`__ + +- `Ports <#ports>`__ + + - `Create a port <#create-a-port>`__ + - `Create multiple ports <#create-multiple-ports>`__ + - `List ports <#list-ports>`__ + - `Get a port <#get-a-port>`__ + - `Update a port <#update-a-port>`__ + - `Delete a port <#delete-a-port>`__ + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: An isolated virtual layer-2 broadcast domain that is + typically reserved for the tenant who created it unless it is + configured to be shared. The network is the main entity in the + Networking service. Ports and subnets are always associated with a + network. + +- **Subnet**: An IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the + Compute service). Each subnet must have a CIDR and must be associated + with a network. + +- **Port**: A virtual switch port on a logical network switch. Virtual + instances (such as servers created using the Compute service) attach + their interfaces into ports. The port also defines the MAC address + and the IP address or addresses to be assigned to the interfaces + plugged into them. When IP addresses are associated with a port, this + also implies the port is associated with a subnet because the IP + address is taken from the allocation pool for a specific subnet. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate an + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Networking service +~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via a *networking service object*. To +instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes the following arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance are stored in the cloud region specified +by ``$region``. + +Networks +-------- + +A network is an isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it unless it is configured +to be shared. The network is the main entity in the Networking service. +Ports and subnets are always associated with a network. + +Create a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple networks +~~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List networks +~~~~~~~~~~~~~ + +You can list all the networks to which you have access as shown in the +following example: + +.. code:: php + + $networks = $networkingService->listNetworks(); + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a network +~~~~~~~~~~~~~ + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code:: php + + $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code:: php + + $network->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Subnets +------- + +A subnet represents an IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the +Compute service). Each subnet must have a CIDR and must be associated +with a network. + +Create a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code:: php + + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple subnets +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List subnets +~~~~~~~~~~~~ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a subnet +~~~~~~~~~~~~ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code:: php + + $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a subnet +~~~~~~~~~~~~~~~ + +You can delete a subnet as shown in the following example: + +.. code:: php + + $subnet->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Ports +----- + +A port represents a virtual switch port on a logical network switch. +Virtual instances (such as servers created using the Compute service) +attach their interfaces into ports. The port also defines the MAC +address and the IP address or addresses to be assigned to the interfaces +plugged into them. When IP addresses are associated with a port, this +also implies the port is associated with a subnet because the IP address +is taken from the allocation pool for a specific subnet. + +Create a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code:: php + + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple ports +~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List ports +~~~~~~~~~~ + +You can list all the ports to which you have access as shown in the +following example: + +.. code:: php + + $ports = $networkingService->listPorts(); + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a port +~~~~~~~~~~ + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code:: php + + $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a port +~~~~~~~~~~~~~ + +You can delete a port as shown in the following example: + +.. code:: php + + $port->delete(); + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/object-store/Access.md.rst b/doc/services/object-store/Access.md.rst new file mode 100644 index 000000000..ee85a938e --- /dev/null +++ b/doc/services/object-store/Access.md.rst @@ -0,0 +1,75 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want + +Temporary URLs +-------------- + +Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they're CDN-enabled. + +Set "temporary URL" metadata key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You must set this "secret" value on your account, where it can be used +in a global state: + +.. code:: php + + $account = $service->getAccount(); + $account->setTempUrlSecret('my_secret'); + + echo $account->getTempUrlSecret(); + +The string argument of ``setTempUrlSecret()`` is optional - if left out, +the SDK will generate a random hashed secret for you. + +Create a temporary URL +~~~~~~~~~~~~~~~~~~~~~~ + +Once you've set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute: + +.. code:: php + + $object->getTemporaryUrl(60, 'GET'); + +To allow PUT access for 1 hour: + +.. code:: php + + $object->getTemporaryUrl(360, 'PUT'); + +Hosting websites on CloudFiles +------------------------------ + +To host a static (i.e. HTML) website on CloudFiles, you must follow +these steps: + +1. CDN-enable a container +2. Upload all HTML content. You can use nested directory structures. +3. Tell CloudFiles what to use for your default index page like this: + +.. code:: php + + $container->setStaticIndexPage('index.html'); + +4. (Optional) Tell CloudFiles which error page to use by default: + +.. code:: php + + $container->setStaticErrorPage('error.html'); + +Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object. diff --git a/doc/services/object-store/Account.md.rst b/doc/services/object-store/Account.md.rst new file mode 100644 index 000000000..a2d380b2a --- /dev/null +++ b/doc/services/object-store/Account.md.rst @@ -0,0 +1,33 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +View Account Details +-------------------- + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +.. code:: php + + $account = $service->getAccount(); + + // Either return the full Metadata object + $details = $account->getDetails(); + + // or individual values + $account->getContainerCount(); + $account->getObjectCount(); + $account->getBytesUsed(); + diff --git a/doc/services/object-store/Container.md.cdn.rst b/doc/services/object-store/Container.md.cdn.rst new file mode 100644 index 000000000..2f1c8ea68 --- /dev/null +++ b/doc/services/object-store/Container.md.cdn.rst @@ -0,0 +1,90 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +To access the CDN functionality of a particular container: + +.. code:: php + + $container = $service->getContainer('foo_bar'); + + $cdn = $container->getCdn(); + +List CDN-enabled container +-------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code:: php + + $cdnService = $service->getCdnService(); + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + + } + +CDN-enable and -disable a container +----------------------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code:: php + + $container->enableCdn(); + $container->disableCdn(); + +Serving containers through SSL +------------------------------ + +.. code:: php + + $cdn->getCdnSslUri(); + +Streaming CDN-enabled containers +-------------------------------- + +.. code:: php + + $cdn->getCdnStreamingUri(); + +iOS streaming +------------- + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code:: php + + $cdn->getIosStreamingUri(); + +CDN logging +----------- + +To enable and disable logging for your CDN: + +.. code:: php + + $cdn->enableCdnLogging(); + $cdn->disableCdnLogging(); + diff --git a/doc/services/object-store/Container.md.storage.rst b/doc/services/object-store/Container.md.storage.rst new file mode 100644 index 000000000..89798fdd0 --- /dev/null +++ b/doc/services/object-store/Container.md.storage.rst @@ -0,0 +1,218 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + // Create a client object to communicate with various Rackspace Cloud services. + $client = new Rackspace(RACKSPACE_US, array( + 'username' => 'Replace this with your Rackspace Cloud user name', + 'apiKey' => 'Replace this with your Rackspace Cloud API key' + )); + + // Create a service object to use the object store service. The sample code + // creates the object store in the 'DFW' region. + $service = $client->objectStoreService('cloudFiles', 'DFW'); + +Create container +---------------- + +To create a new container, you just need to define its name: + +.. code:: php + + $container = $service->createContainer('my_amazing_container'); + +If the response returned is ``FALSE``, there was an API error - most +likely due to the fact you have a naming collision. + +Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +List containers +--------------- + +Return a list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $containerList = $service->listContainers(); + + while ($container = $containerList->next()) { + // Do stuff; some examples below + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } + +Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite's +memcmp() function, regardless of text encoding. + +The list is limited to 10,000 containers at a time. See 1.3 for ways to +limit and navigate this list. + +Return a formatted list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, the SDK only supports JSON-formatted responses. + +Controlling a large list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may limit and control this list of results by using the ``marker`` +and ``end_marker`` parameters. The former parameter (``marker``) tells +the API where to begin the list, and the latter (``end_marker``) tells +it where to end the list. You may use either of them independently or +together. You may also use the ``limit`` parameter to fix the number of +containers returned. + +To list a set of containers between two fixed points: + +.. code:: php + + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); + +Or to return a limited set: + +.. code:: php + + $someContainers = $service->listContainers(array('limit' => 560)); + +Get container +------------- + +To retrieve a certain container, either to access its object or +metadata: + +.. code:: php + + $container = $service->getContainer('container_name'); + + echo $container->getObjectCount(); + echo $container->getBytesUsed(); + +Delete container +---------------- + +Deleting a container is easy: + +.. code:: php + + $container->delete(); + +Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +``$deleteObjects`` parameter to ``TRUE`` like so: + +.. code:: php + + $container->delete(TRUE); + +You can also do it manually: + +.. code:: php + + $container->deleteAllObjects(); + $container->delete(); + +Create or update container metadata +----------------------------------- + +.. code:: php + + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); + +Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To *append* values +to the current metadata: + +.. code:: php + + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); + +If you only want to set the metadata to the local object, and not +immediately retain these values on the API, you can use a standard +setter method - which can contribute to eventual actions like an update: + +.. code:: php + + $container->setMetadata(array('Foo' => 'Bar')); + +Container quotas +---------------- + +The container\_quotas middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check. + +To set quotas for a container: + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); + +And to retrieve them: + +.. code:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); + +Access log delivery +------------------- + +To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage). + +.. code:: php + + $container->enableLogging(); + $container->disableLogging(); + +Syncing containers +------------------ + +You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory: + +.. code:: php + + $container->uploadDirectory('/home/Jamie/blog'); + +There are four scenarios you should be aware of: + ++------------------------+-----------------------+----------------------+--------------------------------+ +| Local | Remote | Comparison | Action | ++========================+=======================+======================+================================+ +| File exists | File exists | Identical checksum | No action | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File exists | Different checksum | Local file overwrites remote | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File does not exist | - | Local file created in Swift | ++------------------------+-----------------------+----------------------+--------------------------------+ +| Files does not exist | File exists | - | Remote file deleted | ++------------------------+-----------------------+----------------------+--------------------------------+ + diff --git a/doc/services/object-store/Migrating.md.storage.rst b/doc/services/object-store/Migrating.md.storage.rst new file mode 100644 index 000000000..99738554b --- /dev/null +++ b/doc/services/object-store/Migrating.md.storage.rst @@ -0,0 +1,101 @@ +Migrating containers (across regions) +===================================== + +Introduction +------------ + +Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a ``COPY`` +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality. + +You **will** be charged for bandwidth between regions, so it's advisable +to use ServiceNet where possible (which is free). + +Requirements +------------ + +- You must install the full Guzzle package, so that the process can + take advantage of Guzzle's batching functionality (it allows parallel + requests to be batched for greater efficiency). You can do this by + running: + +.. code:: bash + + php composer.phar install --dev + +- Depending on the size and number of transfer items, you will need to + raise PHP's memory limit: + +.. code:: php + + ini_set('memory_limit', '512M'); + +- You will need to enact some kind of backoff/retry strategy for rate + limits. Guzzle comes with a convenient feature that just needs to be + added as a normal subscriber: + +.. code:: php + + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + +This tells the client to retry up to ``10`` times for failed requests +have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + +Setup +----- + +You can access all this functionality by executing: + +.. code:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); + + $iadService->migrateContainer($oldContainer, $newContainer); + +It's advisable to do this process in a Cloud Server in one of the two +regions you're migrating to/from. This allows you to use ``privateURL`` +as the third argument in the ``objectStoreService`` methods like this: + +.. code:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + +This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free. + +Options +------- + +You can pass in an array of arguments to the method: + +.. code:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); + + $iadService->migrateContainer($oldContainer, $newContainer, $options); + +Options explained +~~~~~~~~~~~~~~~~~ + ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| Name | Description | Default | ++========================+===============================================================================================================================================================================================================================================================================================================================================+===========+ +| ``read.pageLimit`` | When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional ``objectList`` method, which calls the ``PaginatedIterator``. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API) | 10,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``read.batchLimit`` | After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together. | 1,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ + diff --git a/doc/services/object-store/Object.md.cdn.rst b/doc/services/object-store/Object.md.cdn.rst new file mode 100644 index 000000000..64ac143b3 --- /dev/null +++ b/doc/services/object-store/Object.md.cdn.rst @@ -0,0 +1,25 @@ +Setup +----- + +You will need to instantiate the container object and access its CDN +functionality as `documented +here `__. + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); + diff --git a/doc/services/object-store/Object.md.storage.rst b/doc/services/object-store/Object.md.storage.rst new file mode 100644 index 000000000..16dab18f4 --- /dev/null +++ b/doc/services/object-store/Object.md.storage.rst @@ -0,0 +1,330 @@ +Setup +----- + +Conceptually, a container contains objects (also known as files). In +order to work with objects, you will need to instantiate a container +object first as `documented +here `__. + +Note on object properties +------------------------- + +Please be aware that you cannot directly access the properties of +DataObject anymore, you **must** use appropriate getter/ setter methods: + ++----------------------+------------------------+ +| Property | Method | ++======================+========================+ +| Parent container | ``getContainer`` | ++----------------------+------------------------+ +| Name | ``getName`` | ++----------------------+------------------------+ +| Body of file | ``getContent`` | ++----------------------+------------------------+ +| Size of file | ``getContentLength`` | ++----------------------+------------------------+ +| Type of file | ``getContentType`` | ++----------------------+------------------------+ +| ETag checksum | ``getEtag`` | ++----------------------+------------------------+ +| Last modified date | ``getLastModified`` | ++----------------------+------------------------+ + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + + **Note:** Unlike previous versions, you do not need to manually + specify your object's content type. The API will do this for you. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +To upload a single/basic file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $data = fopen('/path/to/sample.mp3', 'r+'); + + // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) + + $meta = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow' + ); + + $metaHeaders = DataObject::stockHeaders($meta); + $customHeaders = array(); + $allHeaders = $metaHeaders + $customHeaders; + + $container->uploadObject('sample.mp3', $data, $allHeaders); + +To upload multiple small-to-mid sized files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + +To upload large files +~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your +transfer, upon which you can execute your upload functionality. For your +convenience, the Container resource object contains a simple method to +do this heavy lifting for you: + +.. code:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/jamie/video.mov', + 'metadata' => array( + 'Author' => 'Jamie' + ), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifest is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + // ... do something + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code:: php + + $container->objectList(array('prefix' => 'logFile_')); + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code:: php + + $file = $container->getObject('summer_vacation.mp4'); + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +:: + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + +Update an existing object +------------------------- + +Updating content is easy: + +.. code:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + +Copy object +----------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code:: php + + $object->copy('/container_2/new_object_name'); + +Delete object +------------- + +.. code:: php + + $object->delete(); + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code:: php + + $container->getPartialObject('summer_vacation.mp4'); + +In order to access the metadata on a partial or complete object, use: + +.. code:: php + + $object->getMetadata(); + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code:: php + + $object->refresh(); + +You can also update to get the latest metadata: + +.. code:: php + + $object->retrieveMetadata(); + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + +Bulk delete +----------- + +Bulk delete a set of paths: + +.. code:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); + diff --git a/doc/services/object-store/README.md.rst b/doc/services/object-store/README.md.rst new file mode 100644 index 000000000..01cf26733 --- /dev/null +++ b/doc/services/object-store/README.md.rst @@ -0,0 +1,86 @@ +Object Store +============ + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Specifically, a cloud is made up of one or more regions. Each region can +have several **containers**, created by a user. Each container can +container several **objects** (sometimes referred to as files), uploaded +by the user. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choose one of the following two options: + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Object Store service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +3. Create a container for your objects (also referred to as files). +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +4. Upload an object to the container. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +There is a lot more you can do with containers and objects. See the +`complete user guide to the Object Store service `__. diff --git a/doc/services/object-store/USERGUIDE.md.rst b/doc/services/object-store/USERGUIDE.md.rst new file mode 100644 index 000000000..d9732ea36 --- /dev/null +++ b/doc/services/object-store/USERGUIDE.md.rst @@ -0,0 +1,900 @@ +The Complete User Guide to the Object Store Service +=================================================== + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the object store service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'apiKey' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Object Store Service +~~~~~~~~~~~~~~~~~~~~ + +All operations on the object store are done via an object store service +object. + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +Containers +---------- + +A **container** defines a namespace for **objects**. An object with the +same name in two different containers represents two different objects. + +For example, you may create a container called ``logos`` to hold all the +image files for your blog. + +A container may contain zero or more objects in it. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Create Container +~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Details +~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single container's details by using its name. An +instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. + +.. code:: php + + $container = $objectStoreService->getContainer('logos'); + + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + +[ `Get the executable PHP script for this +example `__ ] + +List Containers +~~~~~~~~~~~~~~~ + +You can retrieve a list of all your containers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $containers = $objectStoreService->listContainers(); + foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Set or Update Container Metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can set metadata on a container. + +.. code:: php + + $container->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a container. + +.. code:: php + + $containerMetadata = $container->getMetadata(); + +[ `Get the executable PHP script for this +example `__ ] + +Delete Container +~~~~~~~~~~~~~~~~ + +When you no longer have a need for the container, you can remove it. + +If the container is empty (that is, it has no objects in it), you can +remove it as shown below: + +.. code:: php + + $container->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +If the container is not empty (that is, it has objects in it), you have +two choices in how to remove it: + +- `Individually remove each object <#delete-object>`__ in the + container, then remove the container itself as shown above, or + +- Remove the container and all the objects within it as shown below: + + .. code:: php + + $container->delete(true); + + [ `Get the executable PHP script for this + example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in a container. + +.. code:: php + + $containerObjectCount = $container->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerObjectCount`` will contain the number +of objects in the container represented by ``$container``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by a container, in bytes. + +.. code:: php + + $containerSizeInBytes = $container->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerSizeInBytes`` will contain the space +used, in bytes, by the container represented by ``$container``. + +Container Quotas +~~~~~~~~~~~~~~~~ + +Set Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum number of objects that may be stored +in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = 25; + $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Set Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum total space (in bytes) used by +objects in a container. + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; + $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum number of objects that may be +stored in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum total space (in bytes) used +by objects in a container. + +.. code:: php + + $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Objects +------- + +An **object** (sometimes referred to as a file) is the unit of storage +in an Object Store. An object is a combination of content (data) and +metadata. + +For example, you may upload an object named ``php-elephant.jpg``, a JPEG +image file, to the ``logos`` container. Further, you may assign metadata +to this object to indicate that the author of this object was someone +named Jane Doe. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Upload Object +~~~~~~~~~~~~~ + +Once you have created a container, you can upload objects to it. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +It is also possible to upload an object and associate metadata with it. + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData, $allHeaders); + +[ `Get the executable PHP script for this +example `__ ] + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Pseudo-hierarchical Folders +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Although you cannot nest directories in an Object Store, you can +simulate a hierarchical structure within a single container by adding +forward slash characters (``/``) in the object name. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'languages/php/elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. Within that container, the filename is +``languages/php/elephant.jpg``, where ``languages/php/`` is a +pseudo-hierarchical folder hierarchy. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Upload Multiple Objects +^^^^^^^^^^^^^^^^^^^^^^^ + +You can upload more than one object at a time to a container. + +.. code:: php + + $objects = array( + array( + 'name' => 'php-elephant.jpg', + 'path' => '/path/to/local/php-elephant.jpg' + ), + array( + 'name' => 'python-snake.jpg', + 'path' => '/path/to/local/python-snake.jpg' + ), + a + ); + + $container->uploadObjects($objects); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, the contents of two files present on the local +filesystem are uploaded as objects to the container referenced by +``$container``. + +Instead of specifying the ``path`` key in an element of the ``$objects`` +array, you can specify a ``body`` key whose value is a string or a +stream representation. + +Finally, you can pass headers as the second parameter to the +``uploadObjects`` method. These headers will be applied to every object +that is uploaded. + +:: + + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $container->uploadObjects($objects, $allHeaders); + +[ `Get the executable PHP script for this +example `__ +] + +In the example above, every object referenced within the ``$objects`` +array will be uploaded with the same metadata. + +Large Objects +~~~~~~~~~~~~~ + +If you want to upload objects larger than 5GB in size, you must use a +different upload process. + +.. code:: php + + $options = array( + 'name' => 'san_diego_vacation_video.mp4', + 'path' => '/path/to/local/videos/san_diego_vacation.mp4' + ); + $objectTransfer = $container->setupObjectTransfer($options); + $objectTransfer->upload(); + +[ `Get the executable PHP script for this +example `__ ] + +The process shown above will automatically partition your large object +into small chunks and upload them concurrently to the container +represented by ``$container``. + +You can tune the parameters of this process by specifying additional +options in the ``$options`` array. Here is a complete listing of keys +that can be specified in the ``$options`` array: + ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ +| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ + +Auto-extract Archive Files +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can upload a tar archive file and have the Object Store service +automatically extract it into a container. + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $localArchiveFileName = '/path/to/local/image_files.tar.gz'; + $remotePath = 'images/'; + + $fileData = fopen($localArchiveFileName, 'r'); + $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, a local archive file named ``image_files.tar.gz`` +is uploaded to an Object Store container named ``images`` (defined by +the ``$remotePath`` variable). + +Note that while we call ``fopen`` to open a file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``bulkExtract`` call. + +The third parameter to ``bulkExtract`` is the type of the archive file +being uploaded. The acceptable values for this are: + +- ``UrlType::TAR`` for tar archive files, *or*, +- ``UrlType:TAR_GZ`` for tar archive files that are compressed with + gzip, *or* +- ``UrlType::TAR_BZ`` for tar archive file that are compressed with + bzip + +Note that the value of ``$remotePath`` could have been a +(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as +``images/blog`` as well. + +List Objects in a Container +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all the objects stored in a container. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $objects = $container->objectList(); + foreach ($objects as $object) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +You can list only those objects in the container whose names start with +a certain prefix. + +.. code:: php + + $options = array( + 'prefix' => 'php' + ); + + $objects = $container->objectList($options); + +[ `Get the executable PHP script for this +example `__ ] + +In general, the ``objectList()`` method described above takes an +optional parameter (``$options`` in the example above). This parameter +is an associative array of various options. Here is a complete listing +of keys that can be specified in the ``$options`` array: + ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++==================+=============================================================================+=============+=============+=================+=========================+ +| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ + +Retrieve Object +~~~~~~~~~~~~~~~ + +You can retrieve an object and its metadata, given the object's +container and name. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getObject($objectName); + + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + + $objectContent = $object->getContent(); + + /** @var $objectContent Guzzle\Http\EntityBody **/ + + // Write object content to file on local filesystem. + $objectContent->rewind(); + $stream = $objectContent->getStream(); + $localFilename = tempnam("/tmp", 'php-opencloud-'); + file_put_contents($localFilename, $stream); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$object`` is the object named +``php-elephant.jpg`` in the container represented by ``$container``. +Further, ``$objectContent`` represents the contents of the object. It is +of type +```Guzzle\Http\EntityBody`` `__. + +Retrieve Object Metadata +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve just an object's metadata without retrieving its +contents. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getPartialObject($objectName); + $objectMetadata = $object->getMetadata(); + + /** @var $objectMetadata \OpenCloud\Common\Metadata **/ + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, while ``$object`` is an instance of +``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only +partially populated. Specifically, only properties of the instance +relating to object metadata are populated. + +Temporary URLs +~~~~~~~~~~~~~~ + +The Temporary URL feature allows you to create limited-time Internet +addresses that allow you to grant limited access to your Object Store +account. Using this feature, you can allow others to retrieve or place +objects in your Object Store account for a specified amount of time. +Access to the temporary URL is independent of whether or not your +account is `CDN-enabled <#cdn-containers>`__. Even if you do not +CDN-enable a container, you can still grant temporary public access +through a temporary URL. + +First, you must set the temporary URL secret on your account. This is a +one-time operation; you only need to perform it the very first time you +wish to use the temporary URLs feature. + +.. code:: php + + $account->setTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation is carried out on ``$account``, which is an +instance of ``OpenCloud\ObjectStore\Resource\Account``, a class +representing `your object store account <#accounts>`__. + +The above operation will generate a random secret and set it on your +account. Instead of a random secret, if you wish to provide a secret, +you can supply it as a parameter to the ``setTempUrlSecret`` method. + +.. code:: php + + $account->setTempUrlSecret(''); + +[ `Get the executable PHP script for this +example `__ +] + +Once a temporary URL secret has been set on your account, you can +generate a temporary URL for any object in your Object Store. + +.. code:: php + + $expirationTimeInSeconds = 3600; // one hour from now + $httpMethodAllowed = 'GET'; + $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, a temporary URL for the object is generated. This +temporary URL will provide public access to the object for an hour (3600 +seconds), as specified by the ``$expirationTimeInSeconds`` variable. +Further, only GET HTTP methods will be allowed on this URL, as specified +by the ``$httpMethodAllowed`` variable. The other value allowed for the +``$httpMethodAllowed`` variable would be ``PUT``. + +You can also retrieve the temporary URL secret that has been set on your +account. + +.. code:: php + + $tempUrlSecret = $account->getTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Update Object +~~~~~~~~~~~~~ + +You can update an object's contents (as opposed to `updating its +metadata <#update-object-metadata>`__) by simply re-\ `uploading the +object <#upload-object>`__ to its container using the same object name +as before. + +Update Object Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can update an object's metadata after it has been uploaded to a +container. + +.. code:: php + + $object->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Copy Object +~~~~~~~~~~~ + +You can copy an object from one container to another, provided the +destination container already exists. + +.. code:: php + + $object->copy('logos_copy/php.jpg'); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, both the name of the destination container +(``logos_copy``)and the name of the destination object (``php.jpg``) +have to be specified, separated by a ``/``. + +Delete Object +~~~~~~~~~~~~~ + +When you no longer need an object, you can delete it. + +.. code:: php + + $object->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Bulk Delete +~~~~~~~~~~~ + +While you can delete individual objects as shown above, you can also +delete objects and empty containers in bulk. + +.. code:: php + + $objectStoreService->bulkDelete(array( + 'logos/php-elephant.png', + 'logos/python-snakes.png', + 'some_empty_container' + )); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, two objects (``some_container/object_a.png``, +``some_other_container/object_z.png``) and one empty container +(``some_empty_container``) are all being deleted in bulk via a single +command. + +CDN Containers +-------------- + +Note: The functionality described in this section is available only on +the Rackspace cloud. It will not work as described when working with a +vanilla OpenStack cloud. + +Any container can be converted to a CDN-enabled container. When this is +done, the objects within the container can be accessed from anywhere on +the Internet via a URL. + +Enable CDN Container +~~~~~~~~~~~~~~~~~~~~ + +To take advantage of CDN capabilities for a container and its objects, +you must CDN-enable that container. + +.. code:: php + + $container->enableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Public URLs +~~~~~~~~~~~ + +Once you have CDN-enabled a container, you can retrieve a +publicly-accessible URL for any of its objects. There are four types of +publicly-accessible URLs for each object. Each type of URL is meant for +a different purpose. The sections below describe each of these URL types +and how to retrieve them. + +HTTP URL +^^^^^^^^ + +You can use this type of URL to access the object over HTTP. + +:: + + $httpUrl = $object->getPublicUrl(); + +[ `Get the executable PHP script for this +example `__ ] + +Secure HTTP URL +^^^^^^^^^^^^^^^ + +You can use this type of URL to access the object over HTTP + TLS/SSL. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $httpsUrl = $object->getPublicUrl(UrlType::SSL); + +[ `Get the executable PHP script for this +example `__ ] + +Streaming URL +^^^^^^^^^^^^^ + +You can use this type of URL to stream a video or audio object using +Adobe's HTTP Dynamic Streaming. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +IOS Streaming URL +^^^^^^^^^^^^^^^^^ + +You can use this type of URL to stream an audio or video object to an +iOS device. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +Update CDN Container TTL +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update the TTL of a CDN-enabled container. + +.. code:: php + + $cdnContainer = $container->getCdn(); + $cdnContainer->setTtl(); + +[ `Get the executable PHP script for this +example `__ ] + +Disable CDN Container +~~~~~~~~~~~~~~~~~~~~~ + +If you no longer need CDN capabilities for a container, you can disable +them. + +.. code:: php + + $container->disableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Accounts +-------- + +An **account** defines a namespace for **containers**. An account can +have zero or more containers in it. + +Retrieve Account +~~~~~~~~~~~~~~~~ + +You must retrieve the account before performing any operations on it. + +.. code:: php + + $account = $objectStoreService->getAccount(); + +Get Container Count +~~~~~~~~~~~~~~~~~~~ + +You can quickly find out how many containers are in your account. + +.. code:: php + + $accountContainerCount = $account->getContainerCount(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in your account. + +.. code:: php + + $accountObjectCount = $account->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountObjectCount`` will contain the number of +objects in the account represented by ``$account``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by your account, in bytes. + +.. code:: php + + $accountSizeInBytes = $account->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountSizeInBytes`` will contain the space +used, in bytes, by the account represented by ``$account``. diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/orchestration/README.md.rst b/doc/services/orchestration/README.md.rst new file mode 100644 index 000000000..1a983d187 --- /dev/null +++ b/doc/services/orchestration/README.md.rst @@ -0,0 +1,98 @@ +Orchestration +============= + +**Orchestration** is a service that can be used to create and manage +cloud resources. Examples of such resources are databases, load +balancers, servers and software installed on them. + +Concepts +-------- + +To use the Orchestration service effectively, you should understand +several key concepts: + +- **Template**: An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + +- **Resource**: A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + +- **Stack**: A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Orchestration service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object as shown in the following example: + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +3. Create a stack from a template. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a stack, there is more you can do with it. See +`complete user guide for orchestration `__. diff --git a/doc/services/orchestration/USERGUIDE.md.rst b/doc/services/orchestration/USERGUIDE.md.rst new file mode 100644 index 000000000..98bd51db7 --- /dev/null +++ b/doc/services/orchestration/USERGUIDE.md.rst @@ -0,0 +1,672 @@ +Complete User Guide for the Orchestration Service +================================================= + +Orchestration is a service that you can use to create and manage cloud +resources such as databases, load balancers, and servers, and the +software installed on servers. + +Table of Contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ +- `Client <#client>`__ +- `Orchestration service <#orchestration-service>`__ +- `Templates <#templates>`__ +- `Validate template <#validate-template>`__ + + - `Validate a template from a + file <#validate-a-template-from-a-file>`__ + - `Validate Template from URL <#validate-template-from-url>`__ + +- `Stacks <#stacks>`__ +- `Preview stack <#preview-stack>`__ + + - `Preview a stack from a template + file <#preview-a-stack-from-a-template-file>`__ + - `Preview a stack from a template + URL <#preview-a-stack-from-a-template-url>`__ + +- `Create stack <#create-stack>`__ + + - `Create a stack from a template + file <#create-a-stack-from-a-template-file>`__ + - `Create a stack from a template + URL <#create-a-stack-from-a-template-url>`__ + +- `List stacks <#list-stacks>`__ +- `Get stack <#get-stack>`__ +- `Get stack template <#get-stack-template>`__ +- `Update stack <#update-stack>`__ + + - `Update a stack from a template + file <#update-a-stack-from-a-template-file>`__ + - `Update Stack from Template + URL <#update-stack-from-template-url>`__ + +- `Delete stack <#delete-stack>`__ +- `Abandon Stack <#abandon-stack>`__ +- `Adopt stack <#adopt-stack>`__ +- `Stack resources <#stack-resources>`__ +- `List stack resources <#list-stack-resources>`__ +- `Get stack resource <#get-stack-resource>`__ +- `Get stack resource metadata <#get-stack-resource-metadata>`__ +- `Stack resource events <#stack-resource-events>`__ +- `List stack events <#list-stack-events>`__ +- `List stack resource events <#list-stack-resource-events>`__ +- `Get stack resource event <#get-stack-resource-event>`__ +- `Resource types <#resource-types>`__ +- `List resource types <#list-resource-types>`__ +- `Get resource type <#get-resource-type>`__ +- `Get resource type template <#get-resource-type-template>`__ +- `Build info <#build-info>`__ +- `Get build info <#get-build-info>`__ + +Concepts +-------- + +To use the Orchestration service effectively, you should understand the +following key concepts: + +- **Template**: A JSON or YAML document that describes how a set of + resources should be assembled to produce a working deployment. The + template specifies the resources to use, the attributes of these + resources that are parameterized and the information that is sent to + the user when a template is instantiated. + +- **Resource**: Some component of your architecture (a cloud server, a + group of scaled cloud servers, a load balancer, some configuration + management system, and so on) that is defined in a template. + +- **Stack**: A running instance of a template. When a stack is created, + the resources specified in the template are created. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+==========================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +Templates +--------- + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validate template +~~~~~~~~~~~~~~~~~ + +Before you use a template to create a stack, you might want to validate +it. + +Validate a template from a file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Validate Template from URL +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Stacks +------ + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + +Preview stack +~~~~~~~~~~~~~ + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Preview a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create stack +~~~~~~~~~~~~ + +You can create a stack from a template. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +List stacks +~~~~~~~~~~~ + +You can list all the stacks that you have created as shown in the +following example: + +.. code:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack +~~~~~~~~~ + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code:: php + + $stack = $orchestrationService->getStack('simple-lamp-setup'); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack template +~~~~~~~~~~~~~~~~~~ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code:: php + + $stackTemplate = $stack->getTemplate(); + /** @var $stackTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update stack +~~~~~~~~~~~~ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + +Update a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code:: php + + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Update Stack from Template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code:: php + + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Delete stack +~~~~~~~~~~~~ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code:: php + + $stack->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Abandon Stack +~~~~~~~~~~~~~ + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code:: php + + $abandonStackData = $stack->abandon(); + /** @var $abandonStackData string **/ + + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation returns data about the abandoned stack as a +string. You can use this data to recreate the stack by using the `adopt +stack <#adopt-stack>`__ operation. + +Adopt stack +~~~~~~~~~~~ + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code:: php + + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resources +--------------- + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + +List stack resources +~~~~~~~~~~~~~~~~~~~~ + +You can list all the resources for a stack as shown in the following +example: + +.. code:: php + + $resources = $stack->listResources(); + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource +~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code:: php + + $resource = $stack->getResource('load-balancer'); + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceMetadata = $resource->getMetadata(); + /** @var $resourceMetadata \stdClass **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resource events +--------------------- + +Operations on resources within a stack (such as the creation of a +resource) produce events. + +List stack events +~~~~~~~~~~~~~~~~~ + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code:: php + + $stackEvents = $stack->listEvents(); + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List stack resource events +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceEvents = $resource->listEvents(); + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource event +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code:: php + + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + +[ `Get the executable PHP script for this +example `__ ] + +Resource types +-------------- + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +~~~~~~~~~~~~~~~~~~~ + +You can list all supported resource types as shown in the following +example: + +.. code:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type +~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code:: php + + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type template +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code:: php + + $resourceTypeTemplate = $resourceType->getTemplate(); + /** @var $resourceTypeTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Build info +---------- + +Get build info +~~~~~~~~~~~~~~ + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code:: php + + $buildInfo = $orchestrationService->getBuildInfo(); + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/queues/Claim.md.rst b/doc/services/queues/Claim.md.rst new file mode 100644 index 000000000..42161536d --- /dev/null +++ b/doc/services/queues/Claim.md.rst @@ -0,0 +1,164 @@ +1. Introduction +--------------- + +A **Claim** is the process of a worker checking out a message to perform +a task. Claiming a message prevents other workers from attempting to +process the same messages. + +2. Setup +-------- + +A Claim is initialized on its parent object, a Queue: + +.. code:: php + + // To initialize an empty object: + $claim = $queue->getClaim(); + + // or retrieve a specific claim: + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +3. Claim messages +----------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +``204 No Content`` message. + +When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it. + +The age given for a claim is relative to the server's clock. The claim's +age is useful for determining how quickly messages are getting processed +and whether a given message's claim is about to expire. + +When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message. + +3.2 Attributes +~~~~~~~~~~~~~~ + +The ``ttl`` attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request. + +The ``grace`` attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted. + +The ``limit`` attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional. + +3.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); + +4. Query claim +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation queries the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +4.2 Attributes +~~~~~~~~~~~~~~ + +Claim ID. + +4.3 Code +~~~~~~~~ + +.. code:: php + + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +5. Update claim +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific **Claim** and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL. + +5.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +5.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); + +6. Release claim +---------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored. + +This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers. + +6.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +6.3 Code +~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/services/queues/Message.md.rst b/doc/services/queues/Message.md.rst new file mode 100644 index 000000000..ca67e22bb --- /dev/null +++ b/doc/services/queues/Message.md.rst @@ -0,0 +1,257 @@ +1. Introduction +--------------- + +A **Message** is a task, a notification, or any meaningful data that a +producer or publisher sends to the queue. A message exists until it is +deleted by a recipient or automatically by the system based on a TTL +(time-to-live) value. + +2. Setup +-------- + +A message is initialized from its parent object, a Queue: + +.. code:: php + + // Setup an empty object + $message = $queue->getMessage(); + + // or retrieve an existing one + $message = $queue->getMessage(''); + +3. Post message +--------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation posts the specified message or messages. You can submit +up to 10 messages in a single request. + +When posting new messages, you specify only the ``body`` and ``ttl`` for +the message. The API will insert metadata, such as ID and age. + +3.2 Parameters +~~~~~~~~~~~~~~ + +How you pass through the array structure depends on whether you are +executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the +same. + +The ``body`` attribute specifies an arbitrary document that constitutes +the body of the message being sent. The size of this body is limited to +256 KB, excluding whitespace. + +The ``ttl`` attribute specifies how long the server waits before marking +the message as expired and removing it from the queue. The value of ttl +must be between 60 and 1209600 seconds (14 days). Note that the server +might not actually delete the message until its age has reached up to +(ttl + 60) seconds, to allow for flexibility in storage implementations. + +3.3 Code samples +~~~~~~~~~~~~~~~~ + +3.3.1 Posting a single message +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + use OpenCloud\Common\Constants\Datetime; + + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013 + ), + 'ttl' => 2 * Datetime::DAY + )); + +3.3.2 Post a batch of messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Please note that the list of messages will be truncated at 10. For more, +please execute another method call. + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); + + $queue->createMessages($messages); + +4. Get messages +--------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the message or messages in the specified queue. + +Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations. + +Results are ordered by age, oldest message first. + +4.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++====================+=========+============+============================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ +| marker | Query | String | Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +5. Get a set of messages by ID +------------------------------ + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format +multiple message ID values by separating them with commas +(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies +the claim ID with which the message is associated. Optional.\| +\|----\|-----\|----\|-----------\| + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $messages = $queue->listMessages(array('ids' => $ids)); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +6. Delete a set of messages by ID +--------------------------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted. + +6.2 Parameters +~~~~~~~~~~~~~~ + +An array of IDs. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $response = $queue->deleteMessages($ids); + +7. Get a specific message +------------------------- + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the specified message from the specified queue. + +7.2 Parameters +~~~~~~~~~~~~~~ + +Message ID. + +7.3 Object properties +~~~~~~~~~~~~~~~~~~~~~ + +``href`` is an opaque relative URI that the client can use to uniquely +identify a message resource and interact with it. + +``ttl`` is the TTL that was set on the message when it was posted. The +message expires after (ttl - age) seconds. + +``age`` is the number of seconds relative to the server's clock. + +``body`` is the arbitrary document that was submitted with the original +request to post the message. + +7.4 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message = $queue->getMessage('51db6f78c508f17ddc924357'); + +8. Delete message +----------------- + +8.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified message. + +8.2 Parameters +~~~~~~~~~~~~~~ + +None. + +8.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/services/queues/Queue.md.rst b/doc/services/queues/Queue.md.rst new file mode 100644 index 000000000..1fe9f7036 --- /dev/null +++ b/doc/services/queues/Queue.md.rst @@ -0,0 +1,197 @@ +1. Introduction +--------------- + +A Queue is an entity that holds messages. Ideally, a queue is created +per work type. For example, if you want to compress files, you would +create a queue dedicated to this job. Any application that reads from +this queue would only compress files. + +2. Setup +-------- + +.. code:: php + + $service = $client->queuesService('cloudQueues', 'ORD'); + +3. Client IDs +------------- + +With most of Marconi's operation, you must specify a **Client ID** which +will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + +4. List queues +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation lists queues for the project. The queues are sorted +alphabetically by name. + +4.2 Parameters +~~~~~~~~~~~~~~ + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|marker\|Query\|​String\|Specifies the name of the last queue received +in a previous request, or none to get the first page of results. +Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to +return. The default value for the number of queues returned is 10. If +you do not specify this parameter, the default number of queues is +returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether +queue metadata is included in the response. The default value for this +parameter is false, which excludes the metadata. Optional.\| +\|----\|-----\|----\|-----------\| + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queues = $service->listQueues(); + + while ($queue = $queues->next()) { + echo $queue->getName() . PHP_EOL; + } + +5. Create queue +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation creates a new queue. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A string representation of the name for your new Queue. The name must +not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->createQueue('new_queue'); + +6. Retrieve queue +----------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +Returns a ``Queue`` object for use. + +6.2 Parameters +~~~~~~~~~~~~~~ + +Queue name. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->getQueue('new_queue'); + +7. Check queue existence +------------------------ + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +7.2 Parameters +~~~~~~~~~~~~~~ + +7.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + +8. Update queue metadata (permanently to the API) +------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +4.2 Parameters +~~~~~~~~~~~~~~ + +Hash of key pairs. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + +9. Retrieve the queue metadata (fresh from the API) +--------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns metadata, such as message TTL, for the queue. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadata = $queue->retrieveMetadata(); + + print_r($metadata->toArray()); + +10. Get queue stats +------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->getStats(); + diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst new file mode 100644 index 000000000..e69de29bb diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst new file mode 100644 index 000000000..e69de29bb From f65cda63ea035bf992215276b1cf4e43710c5a16 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 3 Mar 2015 20:04:47 +0100 Subject: [PATCH 710/835] Add remaining top-level things --- doc/caching-creds.rst | 44 +++++++++++ doc/debugging.rst | 99 +++++++++++++++++++++++ doc/index.rst | 23 ++++++ doc/iterators.rst | 178 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+) create mode 100644 doc/caching-creds.rst create mode 100644 doc/debugging.rst create mode 100644 doc/index.rst create mode 100644 doc/iterators.rst diff --git a/doc/caching-creds.rst b/doc/caching-creds.rst new file mode 100644 index 000000000..3c3e8df23 --- /dev/null +++ b/doc/caching-creds.rst @@ -0,0 +1,44 @@ +Caching credentials +=================== + +You can speed up your API operations by caching your credentials in a +(semi-)permanent location, such as your DB or local filesystem. This +enable subsequent requests to access a shared resource, instead of +repetitively having to re-authenticate on every thread of execution. + +Tokens are valid for 24 hours, so you can effectively re-use the same +cached value for that period. If you try to use a cached version that +has expired, an authentication request will be made. + +Filesystem example +------------------ + +In this example, credentials will be saved to a file in the local +filesystem. Be sure to exclude it from your VCS. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); + + $cacheFile = __DIR__ . '/.opencloud_token'; + + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } + + $token = $client->getTokenObject(); + + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } + +In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/debugging.rst b/doc/debugging.rst new file mode 100644 index 000000000..445915284 --- /dev/null +++ b/doc/debugging.rst @@ -0,0 +1,99 @@ +Debugging +========= + +There are two important debugging strategies to use when encountering +problems with HTTP transactions. + +Strategy 1: Meaningful exception handling +----------------------------------------- + +If the API returns a ``4xx`` or ``5xx`` status code, it indicates that +there was an error with the sent request, meaning that the transaction +cannot be adequately completed. + +The Guzzle HTTP component, which forms the basis of our SDK's transport +layer, utilizes `numerous exception +classes `__ +to handle this error logic. + +The two most common exception classes are: + +- ``Guzzle\Http\Exception\ClientErrorResponseException``, which is + thrown when a ``4xx`` response occurs + +- ``Guzzle\Http\Exception\ServerErrorResponseException``, which is + thrown when a ``5xx`` response occurs + +Both of these classes extend the base ``BadResponseException`` class. + +This provides you with the granularity you need to debug and handle +exceptions. + +An example with Swift +~~~~~~~~~~~~~~~~~~~~~ + +If you're trying to retrieve a Swift resource, such as a Data Object, +and you're not completely certain that it exists, it makes sense to wrap +your call in a try/catch block: + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + // Okay, the resource probably does not exist + return false; + } catch (\Exception $e) { + // Some other exception was thrown, probably critical + $this->logException($e); + $this->alertDevs(); + } + +Both ``ClientErrorResponseException`` and +``ServerErrorResponseException`` have two methods that allow you to +access the HTTP transaction: + +.. code:: php + + // Find out the faulty request + $request = $e->getRequest(); + + // Display everything by casting as string + echo (string) $request; + + // Find out the HTTP response + $response = $e->getResponse(); + + // Output that too + echo (string) $response; + +Strategy 2: Wire logging +------------------------ + +Guzzle provides a `Log +plugin `__ +that allows you to log everything over the wire, which is useful if you +don't know what's going on. + +Here's how you enable it: + +Install the plugin +^^^^^^^^^^^^^^^^^^ + +.. code:: bash + + php composer.phar require guzzle/plugin-log:~3.8 + +Add to your client +^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use Guzzle\Plugin\Log\LogPlugin; + + $client->addSubscriber(LogPlugin::getDebugPlugin()); + +The above will add a generic logging subscriber to your client, which +will be notified every time a relevant HTTP event is fired off. diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 000000000..45c453a40 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,23 @@ +.. php-opencloud documentation master file, created by + sphinx-quickstart on Tue Mar 3 12:28:19 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to php-opencloud's documentation! +========================================= + +Contents: + +.. toctree:: + :glob: + :maxdepth: 2 + + services/**/index + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/iterators.rst b/doc/iterators.rst new file mode 100644 index 000000000..e5d3c198d --- /dev/null +++ b/doc/iterators.rst @@ -0,0 +1,178 @@ +Iterators +========= + +Intro +----- + +Iterators allow you to traverse over collections of your resources in an +efficient and easy way. Currently there are two Iterators provided by +the SDK: + +- **ResourceIterator**. The standard iterator class that implements + SPL's standard + `Iterator `__, + `ArrayAccess `__ + and `Countable `__ + interfaces. In short, this allows you to traverse this object (using + ``foreach``), count its internal elements like an array (using + ``count`` or ``sizeof``), and access its internal elements like an + array (using ``$iterator[1]``). + +- **PaginatedIterator**. This is a child of ResourceIterator, and as + such inherits all of its functionality. The difference however is + that when it reaches the end of the current collection, it attempts + to construct a URL to access the API based on predictive paginated + collection templates. + +Common behaviour +---------------- + +.. code:: php + + $iterator = $computeService->flavorList(); + +There are two ways to traverse an iterator. The first is the longer, +more traditional way: + +.. code:: php + + while ($iterator->valid()) { + $flavor = $iterator->current(); + + // do stuff.. + echo $flavor->id; + + $iterator->next(); + } + +There is also a shorter and more intuitive version: + +.. code:: php + + foreach ($iterator as $flavor) { + // do stuff... + echo $flavor->id; + } + +Because the iterator implements PHP's native ``Iterator`` interface, it +can inherit all the native functionality of traversible data structures +with ``foreach``. + +Very important note +------------------- + +Until now, users have been expected to do this: + +.. code:: php + + while ($flavor = $iterator->next()) { + // ... + } + +which is **incorrect**. The single responsibility of ``next`` is to move +the internal pointer forward. It is the job of ``current`` to retrieve +the current element. + +For your convenience, these two Iterator classes are fully backward +compatible: they exhibit all the functionality you'd expect from a +correctly implemented iterator, but they also allow previous behaviour. + +Using paginated collections +--------------------------- + +For large collections, such as retrieving DataObjects from +CloudFiles/Swift, you need to use pagination. Each resource will have a +different limit per page; so once that page is traversed, there needs to +be another API call to retrieve to *next* page's resources. + +There are two key concepts: + +- **limit** is the amount of resources returned per page +- **marker** is the way you define a starting point. It is some form of + identifier that allows the collection to begin from a specific + resource + +Resource classes +~~~~~~~~~~~~~~~~ + +When the iterator returns a current element in the internal list, it +populates the relevant resource class with all the data returned to the +API. In most cases, a ``stdClass`` object will become an instance of +``OpenCloud\Common\PersistentObject``. + +In order for this instantiation to happen, the ``resourceClass`` option +must correspond to some method in the parent class that creates the +resource. For example, if we specify 'ScalingPolicy' as the +``resourceClass``, the parent object (in this case +``OpenCloud\Autoscale\Group``, needs to have some method will allows the +iterator to instantiate the child resource class. These are all valid: + +1. ``Group::scalingGroup($data);`` + +2. ``Group::getScalingGroup($data);`` + +3. ``Group::resource('ScalingGroup', $data);`` + +where ``$data`` is the standard object. This list runs in order of +precedence. + +Setting up a PaginatedIterator +------------------------------ + +.. code:: php + + use OpenCloud\Common\Collection\PaginatedIterator; + + $service = $client->computeService(); + + $flavors = PaginatedIterator::factory($service, array( + 'resourceClass' => 'Flavor', + 'baseUrl' => $service->getUrl('flavors') + 'limit.total' => 350, + 'limit.page' => 100, + 'key.collection' => 'flavors' + )); + + foreach ($flavors as $flavor) { + echo $flavor->getId(); + } + +As you can see, there are a lot of configuration parameters to pass in - +and getting it right can be quite fiddly, involving a lot of API +research. For this reason, using the convenience methods like +``flavorList`` is recommended because it hides the complexity. + +PaginatedIterator options +~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are certain configuration options that the paginated iterator +needs to work. These are: + ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| Name | Description | Type | Required | Default | ++=========================+===================================================================================================================================================================================================================================================+==============================+============+===============+ +| resourceClass | The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator. | string | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| baseUrl | The base URL that is used for making new calls to the API for new pages | ``Guzzle\Http\Url`` | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.total | The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list | int | No | 10000 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.page | The amount of resources each page contains | int | No | 100 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.links | Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is ``images_links``. | string | No | links | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collection | The top-level key for the array of resources. For example, servers are returned with this data structure: ``{"servers": [...]}``. The **key.collection** value in this case would be ``servers``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collectionElement | Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: ``{"keypairs": [ {"keypair": {...}} ] }``. So in this case the collectionElement key would be ``keypair``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.marker | The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is ``id`` or ``name``. | string | No | name | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.method | The HTTP method used when making API calls for new pages | string | No | GET | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.headers | The HTTP headers to send when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.body | The HTTP entity body to send when making API calls for new pages | ``Guzzle\Http\EntityBody`` | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ + From 31b39641c2ef3009ddbeeebc6dcf9f6825fa4a06 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:49:42 +0100 Subject: [PATCH 711/835] Shuffle structure --- doc/services/autoscale/group-config.rst | 6 +----- doc/services/autoscale/groups.rst | 9 --------- doc/services/autoscale/index.rst | 18 +++++++++++++++++ doc/services/autoscale/policies.rst | 4 +--- doc/services/autoscale/service.sample.rst | 7 ------- doc/services/autoscale/webhooks.rst | 4 +--- doc/services/common/service-args.rst | 16 +++++++++++++++ doc/services/compute/flavors.rst | 6 ------ doc/services/compute/images.rst | 5 ----- doc/services/compute/index.rst | 18 +++++++++++++++++ doc/services/compute/keypairs.rst | 6 ------ doc/services/compute/servers.rst | 5 ----- doc/services/compute/service.sample.rst | 24 ----------------------- 13 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 doc/services/autoscale/service.sample.rst create mode 100644 doc/services/common/service-args.rst delete mode 100644 doc/services/compute/service.sample.rst diff --git a/doc/services/autoscale/group-config.rst b/doc/services/autoscale/group-config.rst index a0d7c98f7..5dcfb6ce4 100644 --- a/doc/services/autoscale/group-config.rst +++ b/doc/services/autoscale/group-config.rst @@ -1,14 +1,10 @@ Group configurations ==================== -.. contents:: - Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with the functionality of a group's configuration, +In order to interact with the functionality of a group's configuration, you must first retrieve the details of the group itself. To do this, you must substitute `{groupId}` for your group's ID: diff --git a/doc/services/autoscale/groups.rst b/doc/services/autoscale/groups.rst index 8e781a2aa..a6caf3f9d 100644 --- a/doc/services/autoscale/groups.rst +++ b/doc/services/autoscale/groups.rst @@ -1,15 +1,6 @@ Groups ====== -.. contents:: - - -Setup ------ - -.. include:: service.sample.rst - - List all groups --------------- diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst index 3077a0a2c..d6ec512ac 100644 --- a/doc/services/autoscale/index.rst +++ b/doc/services/autoscale/index.rst @@ -1,6 +1,23 @@ Auto Scale v2 ============= +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); + +.. include:: ../common/service-args.rst + + +Operations +---------- + .. toctree:: groups @@ -8,6 +25,7 @@ Auto Scale v2 policies webhooks + Glossary -------- diff --git a/doc/services/autoscale/policies.rst b/doc/services/autoscale/policies.rst index f5d2605f4..e103d328e 100644 --- a/doc/services/autoscale/policies.rst +++ b/doc/services/autoscale/policies.rst @@ -4,9 +4,7 @@ Scaling Policies Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with the functionality of a group's scaling +In order to interact with the functionality of a group's scaling policies, you must first retrieve the details of the group itself. To do this, you must substitute `{groupId}` for your group's ID: diff --git a/doc/services/autoscale/service.sample.rst b/doc/services/autoscale/service.sample.rst deleted file mode 100644 index 7e5d166ab..000000000 --- a/doc/services/autoscale/service.sample.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. include:: ../common/rs-client.sample.rst - -Now, set up the Auto Scale service: - -.. code-block:: php - - $service = $client->autoscaleService(); diff --git a/doc/services/autoscale/webhooks.rst b/doc/services/autoscale/webhooks.rst index 68b16b0dc..9133aa9ed 100644 --- a/doc/services/autoscale/webhooks.rst +++ b/doc/services/autoscale/webhooks.rst @@ -4,9 +4,7 @@ Webhooks Setup ----- -.. include:: service.sample.rst - -Finally, in order to interact with webhooks, you must first retrieve the +In order to interact with webhooks, you must first retrieve the details of the group and scaling policy you want to execute: .. code-block:: php diff --git a/doc/services/common/service-args.rst b/doc/services/common/service-args.rst new file mode 100644 index 000000000..696f32f2a --- /dev/null +++ b/doc/services/common/service-args.rst @@ -0,0 +1,16 @@ +``{catalogName}`` is the **name** of the service, as it appears in the service +catalog. For Rackspace users, a default will be provided if you pass ``null`` +in for this argument. For OpenStack users, you cannot do this: you must instead +set your own value since it can depend on your environment setup. + +``{region}`` is the Compute region the service will operate in. For Rackspace +users, you can select one of the following from the `supported regions page +`_. + +``{urlType}`` is the type of URL to use, depending on what endpoints your +catalog provides. For Rackspace, you may use either ``internalURL`` or +``publicURL``. The former will execute HTTP transactions over the internal +network configured for your service, possibly reducing latency and the overall +bandwidth cost - the caveat is that all of your resources must be in same region. +``publicURL``, however, which is the default, will operate over the public +Internet and is to be used for multi-region installations. diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst index f79caa171..eeb447d3c 100644 --- a/doc/services/compute/flavors.rst +++ b/doc/services/compute/flavors.rst @@ -1,12 +1,6 @@ Flavors ======= -Setup ------ - -.. include:: service.sample.rst - - Get a flavor ------------ diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst index b2aae4a65..fb68af7f7 100644 --- a/doc/services/compute/images.rst +++ b/doc/services/compute/images.rst @@ -9,11 +9,6 @@ Images Cloud Backup or Cloud Block Storage to ensure availability in case you need to rebuild or restore a server. -Setup ------ - -.. include:: service.sample.rst - List images ----------- diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 94c41979f..207151d50 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -6,6 +6,23 @@ Compute v2 This is a joint service that supports both Rackspace Cloud Servers v2 API, and OpenStack Nova v2 API. +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Compute service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + .. toctree:: images @@ -13,6 +30,7 @@ Compute v2 servers keypairs + Glossary -------- diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst index 4040c0e65..f7e67b113 100644 --- a/doc/services/compute/keypairs.rst +++ b/doc/services/compute/keypairs.rst @@ -1,12 +1,6 @@ Keypairs ======== -Setup ------ - -.. include:: service.sample.rst - - Generate a new keypair ---------------------- diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 4d2f2363c..229e8e4ff 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -1,11 +1,6 @@ Servers ======= -Setup ------ - -.. include:: service.sample.rst - Get server ---------- diff --git a/doc/services/compute/service.sample.rst b/doc/services/compute/service.sample.rst deleted file mode 100644 index c684caf9a..000000000 --- a/doc/services/compute/service.sample.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. include:: ../common/rs-client.sample.rst - -Now, set up the Auto Scale service: - -.. code-block:: php - - $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); - - -``{catalogName}`` is the **name** of the service, as it appears in the service -catalog. For Rackspace users, this will default to `cloudServersOpenStack`; for -OpenStack users, you must set your own value since it can depend on your -environment setup. - -``{region}`` is the Compute region the service will operate in. For Rackspace -users, you can select one of the following from the `supported regions page`. - -``{urlType}`` is the type of URL to use, depending on what endpoints your -catalog provides. For Rackspace, you may use either `internalURL` or `publicURL`. -The former will execute HTTP transactions over the internal Rackspace network, -reducing latency and the overall bandwidth cost - the caveat is that all of your -resources must be in same region. `publicURL`, however, which is the default, -will operate over the public Internet and is to be used for multi-region -installations. From 9ece52bcd87ba09cdf432bd89eb42ec5f683cf95 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:03 +0100 Subject: [PATCH 712/835] Turn on extension --- doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/conf.py b/doc/conf.py index 7f9e29414..f49381d42 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -23,7 +23,7 @@ lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) primary_domain = 'php' -extensions = [] +extensions = ['sphinxcontrib.phpdomain'] templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' From 5afe71fcf52fff14939c24d451f49ad9acaaaeee Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:07 +0100 Subject: [PATCH 713/835] Add regions --- doc/regions.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 doc/regions.rst diff --git a/doc/regions.rst b/doc/regions.rst new file mode 100644 index 000000000..82edf28ec --- /dev/null +++ b/doc/regions.rst @@ -0,0 +1,15 @@ +Rackspace regions +================= + +Below are the supported regions on the Rackspace network: + ++======+===========+ +| code | location | ++======+===========+ +| IAD | Virginia | +| ORD | Chicago | +| DFW | Dallas | +| LON | London | +| SYD | Sydney | +| HKG | Hong Kong | ++------+-----------+ From 0f0a1aef4f41b6d3f66693fe06ed7b259ab385c5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:13 +0100 Subject: [PATCH 714/835] Add DNS docs --- doc/services/dns/Domains.md.rst | 290 ------------------ doc/services/dns/Records.md.rst | 111 ------- doc/services/dns/Service.md.rst | 13 - doc/services/dns/domains.rst | 288 +++++++++++++++++ doc/services/dns/index.rst | 51 +++ .../dns/{Limits.md.rst => limits.rst} | 37 +-- doc/services/dns/records.rst | 113 +++++++ .../{Reverse-DNS.md.rst => reverse-dns.rst} | 24 +- 8 files changed, 474 insertions(+), 453 deletions(-) delete mode 100644 doc/services/dns/Domains.md.rst delete mode 100644 doc/services/dns/Records.md.rst delete mode 100644 doc/services/dns/Service.md.rst create mode 100644 doc/services/dns/domains.rst rename doc/services/dns/{Limits.md.rst => limits.rst} (59%) create mode 100644 doc/services/dns/records.rst rename doc/services/dns/{Reverse-DNS.md.rst => reverse-dns.rst} (82%) diff --git a/doc/services/dns/Domains.md.rst b/doc/services/dns/Domains.md.rst deleted file mode 100644 index 824c05099..000000000 --- a/doc/services/dns/Domains.md.rst +++ /dev/null @@ -1,290 +0,0 @@ -Domains -======= - -A domain is an entity/container of all DNS-related information -containing one or more records. - -Setup ------ - -Limit methods will be called on the DNS service, an instance of -``OpenCloud\DNS\Service``. Please see the `DNS service `__ -documentation for setup instructions. - -Get domain ----------- - -To retrieve a specific domain, you will need the domain's **id**, not -its domain name. - -.. code:: php - - $domain = $service->domain(12345); - -If you are having trouble remembering or accessing the domain ID, you -can do a domain list search for your domain and then access its ID. - -List domains ------------- - -These calls provide a list of all DNS domains manageable by a given -account. The resulting list is flat, and does not break the domains down -hierarchically by subdomain. All representative domains are included in -the list, even if a domain is conceptually a subdomain of another domain -in the list. - -.. code:: php - - $domains = $service->domainList(); - - # Return detailed information for each domain - $domains = $service->domainList(true); - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Filter parameters -~~~~~~~~~~~~~~~~~ - -You can filter the aforementioned search by using the ``name`` parameter -in a key/value array supplied as a method argument. For example, -providing ``array('name' => 'hoola.com')`` will return hoola.com and -similar names such as main.hoola.com and sub.hoola.com. - -.. code:: php - - $hoolaDomains = $service->domainList(array( - 'name' => 'hoola.com' - )); - -Filter criteria may consist of: - -- Any letter (A-Za-z) -- Numbers (0-9) -- Hyphen ("-") -- 1 to 63 characters - -Filter criteria should not include any of the following characters: - - ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò - -Finding a domain ID -~~~~~~~~~~~~~~~~~~~ - -If you know a domain's name, but not its unique identifier, you can do -this: - -.. code:: php - - $domains = $service->domainList(array( - 'name' => 'foo.com' - )); - - foreach ($domains as $domain) { - $id = $domain->id; - } - -List domain changes -------------------- - -This call shows all changes to the specified domain since the specified -date/time. The since parameter is optional and defaults to midnight of -the current day. - -.. code:: php - - $changes = $domain->changes(); - - # Changes since last week - $since = date('c', strtotime('last week')); - $changes = $domain->changes($since); - - foreach ($changes->changes as $change) { - printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); - - foreach ($change->changeDetails as $detail) { - printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); - } - } - -Export domain -------------- - -This call provides the BIND (Berkeley Internet Name Domain) 9 formatted -contents of the requested domain. This call is for a single domain only, -and as such, does not traverse up or down the domain hierarchy for -details (that is, no subdomain information is provided). - -.. code:: php - - $asyncResponse = $domain->export(); - $body = $asyncResponse->waitFor('COMPLETED'); - echo $body['contents']; - -Create domain -------------- - -A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` -records) and an optional list of sub-domains. You will need to specify -these before creating the domain itself: - -.. code:: php - - // get empty object - $domain = $service->domain(); - - // add A record - $aRecord = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 - )); - $domain->addRecord($aRecord); - - // add optional C record - $cRecord = $domain->record(array( - 'type' => 'CNAME', - 'name' => 'www.example.com', - 'data' => 'example.com', - 'ttl' => 3600 - )); - $domain->addRecord($cRecord); - - // add optional MX record - $mxRecord = $domain->record(array( - 'type' => 'MX', - 'data' => 'mail.example.com', - 'name' => 'example.com', - 'ttl' => 3600, - 'priority' => 5 - )); - $domain->addRecord($mxRecord); - - // add optional NS records - $nsRecord1 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns1.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 - )); - $domain->addRecord($nsRecord1); - - $nsRecord2 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns2.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 - )); - $domain->addRecord($nsRecord2); - - // add optional subdomains - $sub1 = $domain->subdomain(array( - 'emailAddress' => 'foo@example.com', - 'name' => 'dev.example.com', - 'comment' => 'Dev portal' - )); - $domain->addSubdomain($sub1); - - // send to API - $domain->create(array( - 'emailAddress' => 'webmaster@example.com', - 'ttl' => 3600, - 'name' => 'example.com', - 'comment' => 'Optional comment' - )); - -Clone domain ------------- - -This call will duplicate a single existing domain configuration with a -new domain name for the specified Cloud account. By default, all records -and, optionally, subdomain(s) are duplicated as well. - -The method signature you will need to use is: - -.. code:: php - - cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) - -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Name | Data type | Default | Description | -+======================+==============+============+====================================================================================================================================================================================+ -| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | -+----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -For example: - -.. code:: php - - $asyncResponse = $domain->cloneDomain('new-name.com', true); - -Import domain -------------- - -This call provisions a new DNS domain under the account specified by the -BIND 9 formatted file configuration contents defined in the request -object. - -You will need to ensure that the BIND 9 formatted file configuration -contents are valid by adhering to the following rules: - -- Each record starts on a new line and on the first column. If a record - will not fit on one line, use the BIND\_9 line continuation - convention where you put a left parenthesis and continue the one - record on the next line and put a right parenthesis when the record - ends. For example, - - example2.net. 3600 IN SOA dns1.stabletransit.com. ( - sample@rackspace.com. 1308874739 3600 3600 3600 3600) - -- The attribute values of a record must be separated by a single blank - or tab. No other white space characters. - -- If there are any NS records, the data field should not be - dns1.stabletransit.com or dns2.stabletransit.com. They will result in - "duplicate record" errors. - -For example: - -.. code:: php - - $bind9Data = <<import($bind9Data); - -Modify domain -------------- - -This call modifies DNS domain(s) attributes only. Only the TTL, email -address and comment attributes of a domain can be modified. Records -cannot be added, modified, or removed through this API operation - you -will need to use the `add -records `__, `modify -records `__ or `remove -records `__ operations -respectively. - -.. code:: php - - $domain->update(array( - 'ttl' => ($domain->ttl + 100), - 'emailAddress' => 'new_dev@foo.com' - )); - -Remove domain -------------- - -.. code:: php - - $domain->delete(); - diff --git a/doc/services/dns/Records.md.rst b/doc/services/dns/Records.md.rst deleted file mode 100644 index 4e492e8ef..000000000 --- a/doc/services/dns/Records.md.rst +++ /dev/null @@ -1,111 +0,0 @@ -Records -======= - -A DNS record belongs to a particular domain and is used to specify -information about the domain. - -There are several types of DNS records. Examples include mail exchange -(MX) records, which specify the mail server for a particular domain, and -name server (NS) records, which specify the authoritative name servers -for a domain. - -It is represented by the ``OpenCloud\DNS\Resource\Record`` class. -Records belong to a `Domain `__. - -Get record ----------- - -In order to retrieve details for a specific DNS record, you will need -its **id**: - -.. code:: php - - $record = $domain->record('NS-1234567'); - -If you do not have this ID at your disposal, you can traverse the record -collection and do a string comparison (detailed below). - -List records ------------- - -This call lists all records configured for the specified domain. - -.. code:: php - - $records = $domain->recordList(); - - foreach ($records as $record) { - printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); - } - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Query parameters -~~~~~~~~~~~~~~~~ - -You can pass in an array of query parameters for greater control over -your search: - -+------------+--------------+------------------------+---------------+ -| Name | Data type | Default | Description | -+============+==============+========================+===============+ -| ``type`` | ``string`` | The record type | -+------------+--------------+------------------------+---------------+ -| ``name`` | ``string`` | The record name | -+------------+--------------+------------------------+---------------+ -| ``data`` | ``string`` | Data for this record | -+------------+--------------+------------------------+---------------+ - -Find a record ID from its name -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For example: - -.. code:: php - - $records = $domain->recordList(array( - 'name' => 'imap.example.com', - 'type' => 'MX' - )); - - foreach ($records as $record) { - $recordId = $record->id; - } - -Add record ----------- - -This call adds a new record to the specified domain: - -.. code:: php - - $record = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 - )); - - $record->create(); - -Please be aware that records that are added with a different hostname -than the parent domain might fail silently. - -Modify record -------------- - -.. code:: php - - $record = $domain->record(123456); - $record->ttl -= 100; - $record->update(); - -Delete record -------------- - -.. code:: php - - $record->delete(); - diff --git a/doc/services/dns/Service.md.rst b/doc/services/dns/Service.md.rst deleted file mode 100644 index 29ea79193..000000000 --- a/doc/services/dns/Service.md.rst +++ /dev/null @@ -1,13 +0,0 @@ -DNS Service -=========== - -To instantiate a Compute service object, you first need to setup a -Rackspace/OpenStack client. To do this, or for more information, please -consult the `Clients documentation <../Clients.md>`__. - -You will then need to run: - -.. code:: php - - $service = $client->dnsService(); - diff --git a/doc/services/dns/domains.rst b/doc/services/dns/domains.rst new file mode 100644 index 000000000..0a7b83b96 --- /dev/null +++ b/doc/services/dns/domains.rst @@ -0,0 +1,288 @@ +Domains +======= + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code-block:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the search by using the ``name`` parameter in a key/value array +supplied as a method argument. For example, to retrieve domains named ``foo.com``, +along with any subdomains like ``bar.foo.com``: + +.. code-block:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'foo.com' + )); + +Filter criteria may consist of: + +* Any letter (A-Za-z) +* Numbers (0-9) +* Hyphen ("-") +* 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +Once you have a list of domains, to retrieve a domain's ID: + +.. code-block:: php + + foreach ($domains as $domain) { + $id = $domain->id; + } + + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. To list all available changes for a domain for the current day: + +.. code-block:: php + + $changes = $domain->changes(); + + +For more granular control, you can manually define the ``since`` parameter like +so: + +.. code-block:: php + + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + +Once you have a set of changes, you can iterate over them like so: + +.. code-block:: php + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + + +Create domain +------------- + +The first thing you will need to do is instantiate a new object and set the +primary ``A`` record for the DNS domain, like so: + +.. code-block:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $domain->addRecord($aRecord); + +You also have the option of adding more types of DNS records such as ``CNAME``, +``MX`` and ``NS`` records. This step is completely optional and depends on +your requirements: + +.. code-block:: php + + // add CNAME record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add NS record + $nsRecord = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord); + +You can also add sub-domains to your new DNS domain. Again, this is completely +optional: + +.. code-block:: php + + $subdomain = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($subdomain); + +Once you've finished configuring how your DNS domain will work, you're ready +to specify the essential details and send it to the API for creation: + +.. code-block:: php + + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + + +Clone domain +------------ + +This call will duplicate an existing domain under a new name. By default, all +records and, optionally, subdomains are duplicated as well. + +The method signature you will need to use is: + +.. function:: cloneDomain( $newDomainName[, $subdomains[, $comments[, $email[, $records]]]] ) + + Clone a domain + + :param string $newDomainName: The name of the new domain entry + :param bool $subdomains: Set to ``true`` to clone all the subdomains for this domain + :param bool $comments: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. + :param bool $email: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + :param bool $records: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + + +For example: + +.. code-block:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true, false, true, false); + + +Export domain +------------- + +This call provides access to the `BIND `_ +(Berkeley Internet Name Domain) 9 for the requested domain. This call is for a +single domain only, and as such, does not traverse up or down the domain +hierarchy for details: + +.. code-block:: php + + $asyncResponse = $domain->export(); + + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + + +Import domain +------------- + +This operation will create a new DNS domain according to a `BIND `_ +(Berkeley Internet Name Domain) 9 formatted value. + +In order for the BIND value to be considered valid, it needs to adhere to the +following rules: + +* Each record starts on a new line and on the first column. If a record will + not fit on one line, use the BIND\_9 line continuation convention where you put + a left parenthesis and continue the one record on the next line and put a right + parenthesis when the record ends. For example: + + example2.net. 3600 IN SOA dns1.stabletransit.com. (sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +* The attribute values of a record must be separated by a single blank or tab. + No other white space characters. + +* If there are any NS records, the data field should not be + ``dns1.stabletransit.com`` or ``dns2.stabletransit.com``. They will result in + "duplicate record" errors. + +For example: + +.. code-block:: php + + $bind9Data = <<import($bind9Data); + + +Modify domain +------------- + +Only the TTL, email address and comment attributes of a domain can be modified. +Records cannot be added, modified, or removed through this API operation - you +will need to use the `add records `__, `modify records +`__ or `remove records `__ +operations respectively. + +.. code-block:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + + +Delete domain +------------- + +.. code-block:: php + + $domain->delete(); diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index e69de29bb..4caca9fe5 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -0,0 +1,51 @@ +DNS v1 +====== + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the DNS service: + +.. code-block:: php + + $service = $client->dnsService(); + + +Operations +---------- + +.. toctree:: + + records + domains + limits + reverse-dns + + +Glossary +-------- + + domain + A domain is an entity/container of all DNS-related information containing + one or more records. + + record + A DNS record belongs to a particular domain and is used to specify + information about the domain. There are several types of DNS records. Each + record type contains particular information used to describe that record's + purpose. Examples include mail exchange (MX) records, which specify the + mail server for a particular domain, and name server (NS) records, which + specify the authoritative name servers for a domain. + + subdomain + Subdomains are domains within a parent domain, and subdomains cannot be + registered. Subdomains allow you to delegate domains. Subdomains can + themselves have subdomains, so third-level, fourth-level, fifth-level, and + deeper levels of nesting are possible. + + pointer records + DNS usually determines an IP address associated with a domain name. + Reverse DNS is the opposite process: resolving a domain name from an IP + address. This is usually achieved with a domain name pointer. diff --git a/doc/services/dns/Limits.md.rst b/doc/services/dns/limits.rst similarity index 59% rename from doc/services/dns/Limits.md.rst rename to doc/services/dns/limits.rst index 72f8219a0..289c3888d 100644 --- a/doc/services/dns/Limits.md.rst +++ b/doc/services/dns/limits.rst @@ -1,22 +1,15 @@ Limits ====== -Setup ------ - -Limit methods will be called on the DNS service, an instance of -``OpenCloud\DNS\Service``. Please see the `DNS service `__ -documentation for setup instructions. - List all limits --------------- -This call provides a list of all applicable limits for the specified -account. +This call provides a list of all applicable limits for the specified account. .. code:: php - $limits = $service->limits(); + $limits = $service->limits(); + Absolute limits ~~~~~~~~~~~~~~~ @@ -29,12 +22,13 @@ domain: $absoluteLimits = $limits->absolute; - # Domain limit + // Domain limit echo $absoluteLimits->domains; - # Record limit per domain + // Record limit per domain echo $absoluteLimits->{'records per domain'}; + List limit types ---------------- @@ -42,20 +36,17 @@ To find out the different limit types you can query, run: .. code:: php - $limitTypes = $service->limitTypes(); + $limitTypes = $service->limitTypes(); will return: :: - array(3) { - [0] => - string(10) "RATE_LIMIT" - [1] => - string(12) "DOMAIN_LIMIT" - [2] => - string(19) "DOMAIN_RECORD_LIMIT" - } + array(3) { + [0] => string(10) "RATE_LIMIT" + [1] => string(12) "DOMAIN_LIMIT" + [2] => string(19) "DOMAIN_RECORD_LIMIT" + } Query a specific limit ---------------------- @@ -63,8 +54,4 @@ Query a specific limit .. code:: php $limit = $service->limits('DOMAIN_LIMIT'); - echo $limit->absolute->limits->value; - - >>> 500 - diff --git a/doc/services/dns/records.rst b/doc/services/dns/records.rst new file mode 100644 index 000000000..26e17ee5e --- /dev/null +++ b/doc/services/dns/records.rst @@ -0,0 +1,113 @@ +Records +======= + +Setup +----- + +In order to interact with the functionality of records, you must first +retrieve the details of the domain itself. To do this, you must substitute +`{domainId}` for your domain's ID: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('{recordId}'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+ +| Name | Data type | Description | ++============+==============+========================+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+ + + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record('{recordId}'); + $record->ttl -= 100; + $record->update(); + + +Delete record +------------- + +.. code:: php + + $record->delete(); diff --git a/doc/services/dns/Reverse-DNS.md.rst b/doc/services/dns/reverse-dns.rst similarity index 82% rename from doc/services/dns/Reverse-DNS.md.rst rename to doc/services/dns/reverse-dns.rst index b8a5c0e76..4d0eb671c 100644 --- a/doc/services/dns/Reverse-DNS.md.rst +++ b/doc/services/dns/reverse-dns.rst @@ -1,9 +1,6 @@ Reverse DNS =========== -DNS usually determines an IP address associated with a domain name. -Reverse DNS is the opposite process: resolving a domain name from an IP -address. This is usually achieved with a domain name pointer. Get PTR record -------------- @@ -20,14 +17,15 @@ formed resource object in order to retrieve either one's PTR record: 'parent' => $parent )); -So, in the above example, a ``$parent`` could be an instance of +So, in the above example, the ``$parent`` object could be an instance of ``OpenCloud\Compute\Resource\Server`` or ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please -consult the `server documentation <../Compute/Server.md>`__ and `load -balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +consult the `server documentation <../compute>`__ and `load +balancer documentation <../load-balancer>`__ for more detailed usage instructions. + List PTR records ---------------- @@ -41,9 +39,6 @@ List PTR records } -Please consult the `iterator -documentation `__ for more information -about iterators. Add PTR record -------------- @@ -78,19 +73,20 @@ Here is a table that explains the above attributes: | comment | If included, its length must be less than or equal to 160 characters. | No | +-----------+------------------------------------------------------------------------------------+------------+ + Modify PTR record ----------------- .. code:: php - $ptr->update(array( - 'ttl' => $ptr->ttl * 2 - )); + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + Delete PTR record ----------------- .. code:: php - $ptr->delete(); - + $ptr->delete(); From f46fb834cdaa94351778bd80c5a959f9c13d7ae4 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 13:50:23 +0100 Subject: [PATCH 715/835] Add DB docs --- doc/services/database/README.md.rst | 125 ------------------ doc/services/database/configurations.rst | 127 +++++++++++++++++++ doc/services/database/databases.rst | 60 +++++++++ doc/services/database/datastores.rst | 59 +++++++++ doc/services/database/index.rst | 69 ++++++++++ doc/services/database/instances.rst | 155 +++++++++++++++++++++++ doc/services/database/users.rst | 67 ++++++++++ 7 files changed, 537 insertions(+), 125 deletions(-) delete mode 100644 doc/services/database/README.md.rst create mode 100644 doc/services/database/configurations.rst create mode 100644 doc/services/database/databases.rst create mode 100644 doc/services/database/datastores.rst create mode 100644 doc/services/database/instances.rst create mode 100644 doc/services/database/users.rst diff --git a/doc/services/database/README.md.rst b/doc/services/database/README.md.rst deleted file mode 100644 index 3f6bdd3c2..000000000 --- a/doc/services/database/README.md.rst +++ /dev/null @@ -1,125 +0,0 @@ -Databases -========= - -A **cloud database** is a MySQL relational database service that allows -customers to programatically provision database instances of varying -virtual resource sizes without the need to maintain and/or update MySQL. - -Getting started ---------------- - -1. Instantiate a Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - use OpenCloud\Rackspace; - use OpenCloud\Common\Constants\State; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Create a database server instance. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $databaseService = $client->databaseService('cloudDatabases', 'DFW'); - - $twoGbFlavor = $databaseService->flavor(3); - - $dbInstance = $databaseService->instance(); - $dbInstance->name = 'Demo database instance'; - $dbInstance->volume = new stdClass(); - $dbInstance->volume->size = 20; // GB - $dbInstance->flavor = $twoGbFlavor; - $dbInstance->create(); - - $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { - - printf("Database instance build status: %s\n", $dbInstance->status); - - }); - -The example above creates a database server instance with 20GB of disk -space and 2GB of memory, then waits for it to become ACTIVE. - -3. Create a database on the database server instance. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $db = $dbInstance->database(); - $db->name = 'demo_db'; - - $db->create(); - -The example above creates a database named ``demo_db`` on the database -server instance created in the previous step. - -4. Create database user and give it access to database. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $user = $dbInstance->user(); - $user->name = 'demo_user'; - $user->password = 'h@X0r!'; - $user->databases = array('demo_db'); - - $user->create(); - -The example above creates a database user named ``demo_user``, sets its -password and gives it access to the ``demo_db`` database created in the -previous step. - -5. Optional step: Create a load balancer to allow access to the database from the Internet. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The database created in the previous step can only be accessed from the -Rackspace private network (aka ``SERVICENET``). If you have a cloud -server instance in the same region as the database server instance, you -will be able to connect to the database from that cloud server instance. - -If, however, you would like to access the database from the Internet, -you will need to create a load balancer with an IP address that is -routable from the Internet and attach the database server instance as a -back-end node of this load balancer. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $loadBalancer->name = 'Load balancer - DB'; - $loadBalancer->addNode($dbInstance->hostname, 3306); - $loadBalancer->port = 3306; - $loadBalancer->protocol = 'MYSQL'; - $loadBalancer->addVirtualIp('PUBLIC'); - - $loadBalancer->create(); - - $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { - printf("Load balancer build status: %s\n", $lb->status); - }); - - foreach ($loadBalancer->virtualIps as $vip) { - if ($vip->type == 'PUBLIC') { - printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); - } - } - -In the example above, a load balancer is created with the database -server instance as its only back-end node. Further, this load balancer -is configured to listen for MySQL connections on port 3306. Finally a -virtual IP address (VIP) is configured in the ``PUBLIC`` network address -space so that this load balancer may receive connections from the -Internet. - -Once the load balancer is created and becomes ``ACTIVE``, it's -Internet-accessible IP addresses are printed out. If you connect to any -of these IP addresses on port 3306 using the MySQL protocol, you will be -connected to the database created in step 3. diff --git a/doc/services/database/configurations.rst b/doc/services/database/configurations.rst new file mode 100644 index 000000000..edfb9dc89 --- /dev/null +++ b/doc/services/database/configurations.rst @@ -0,0 +1,127 @@ +Configurations +============== + +Creating a configuration +------------------------ + +.. code-block:: php + + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + $configuration = $service->configuration(); + + $configuration->create(array( + 'name' => 'example-configuration-name', + 'description' => 'An example configuration', + 'values' => array( + 'collation_server' => 'latin1_swedish_ci', + 'connect_timeout' => 120 + ), + 'datastore' => array( + 'type' => '10000000-0000-0000-0000-000000000001', + 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' + ) + )); + +`Get the executable PHP script for this example `__ + + +Listing configurations +---------------------- + +You can list out all the configurations you have created as shown below: + +.. code-block:: php + + $configurations = $service->configurationList(); + foreach ($configurations as $configuration) { + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a configuration +-------------------------- + +You can retrieve a specific configuration, using its ID, as shown below: + +.. code-block:: php + + $configuration = $service->configuration('{configId}'); + /** @var OpenCloud\Database\Resource\Configuration **/ + +`Get the executable PHP script for this example `__ + + +Updating a configuration +------------------------ + +You have two choices when updating a configuration: + +* you can `patch a configuration <#patching-a-configuration>`__ to change only +some configuration parameters +* you can `entirely replace a configuration <#replacing-a-configuration>`__ to +replace all configuration parameters with new ones + + +Patching a configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can patch a configuration as shown below: + +.. code-block:: php + + $configuration->patch(array( + 'values' => array( + 'connect_timeout' => 30 + ) + )); + +`Get the executable PHP script for this example `__ + + +Replacing a configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can replace a configuration as shown below: + +.. code-block:: php + + $configuration->update(array( + 'values' => array( + 'collation_server' => 'utf8_general_ci', + 'connect_timeout' => 60 + ) + )); + +`Get the executable PHP script for this example `__ + + +Deleting a configuration +------------------------ + +.. code-block:: php + + $configuration->delete(); + +`Get the executable PHP script for this example `__ + +.. note:: + + You cannot delete a configuration if it is in use by a running instance. + + +Listing instances using a configuration +--------------------------------------- + +You can list all instances using a specific configuration, using its ID, +as shown below: + +.. code-block:: php + + $instances = $configuration->instanceList(); + foreach ($instances as $instance) { + /** @var $instance OpenCloud\Database\Resource\Instance **/ + } + +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/databases.rst b/doc/services/database/databases.rst new file mode 100644 index 000000000..83d07c5e2 --- /dev/null +++ b/doc/services/database/databases.rst @@ -0,0 +1,60 @@ +Databases +========= + +Setup +----- + +In order to interact with the functionality of databases, you must first +retrieve the details of the instance itself. To do this, you must substitute +`{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating a new database +----------------------- + +To create a new database, you must supply it with a name; you can +optionally specify its character set and collating sequence: + +.. code-block:: php + + // Create an empty object + $database = $instance->database(); + + // Send to API + $database->create(array( + 'name' => 'production', + 'character_set' => 'utf8', + 'collate' => 'utf8_general_ci' + )); + +You can find values for ``character_set`` and ``collate`` at `the MySQL +website `__. + + +Deleting a database +------------------- + +.. code-block:: php + + $database->delete(); + +.. note:: + + This is a destructive operation: all your data will be wiped away and will + not be retrievable. + + +Listing databases +----------------- + +.. code-block:: php + + $databases = $service->databaseList(); + + foreach ($databases as $database) { + /** @param $database OpenCloud\Database\Resource\Database */ + } diff --git a/doc/services/database/datastores.rst b/doc/services/database/datastores.rst new file mode 100644 index 000000000..6ca45daed --- /dev/null +++ b/doc/services/database/datastores.rst @@ -0,0 +1,59 @@ +Datastores +========== + +Listing datastores +------------------ + +You can list out all the datastores available as shown below: + +.. code-block:: php + + $datastores = $service->datastoreList(); + foreach ($datastores as $datastore) { + /** @var $datastore OpenCloud\Database\Resource\Datastore **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore +---------------------- + +You can retrieve a specific datastore's information, using its ID, as +shown below: + +.. code-block:: php + + /** @var OpenCloud\Database\Resource\Datastore **/ + $datastore = $service->datastore('{datastoreId}'); + +`Get the executable PHP script for this example `__ + + +Listing datastore versions +-------------------------- + +You can list out all the versions available for a specific datastore, as +shown below: + +.. code-block:: php + + $versions = $datastore->versionList(); + foreach ($versions as $version) { + /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore version +------------------------------ + +You a retrieve a specific datastore version, using its ID, as shown +below: + +.. code-block:: php + + $datastoreVersion = $datastore->version('{versionId}'); + +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index e69de29bb..850e56c8a 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -0,0 +1,69 @@ +Databases v1 +============ + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Database service: + +.. code-block:: php + + $service = $client->databaseService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + instances + databases + users + datastores + + +Glossary +-------- + +.. glossary:: + + configuration group + A configuration group is a collection of key/value pairs which configure a + database instance. Some directives are capable of being applied dynamically, + while other directives require a server restart to take effect. The + configuration group can be applied to an instance at creation or applied to + an existing instance to modify the behavior of the running datastore on the + instance. + + flavor + A flavor is an available hardware configuration for a database instance. + Each flavor has a unique combination of memory capacity and priority for + CPU time. + + instance + A database instance is an isolated MySQL instance in a single tenant + environment on a shared physical host machine. Also referred to as + instance. + + database + A database is a local MySQL database running on an instance. + + user + A user is a local MySQL user that can access a database running on an + instance. + + datastore + The database engine running on your instance. Currently, there is support + for MySQL 5.6, MySQL 5.1, Percona 5.6 and MariaDB 10. + + volume + A volume is user-specified storage that contains the database engine data + directory. Volumes are automatically provisioned on shared Internet Small + Computer System Interface (iSCSI) storage area networks (SAN) that provide + for increased performance, scalability, availability and manageability. + Applications with high I/O demands are performance optimized and data is + protected through both local and network RAID-10. diff --git a/doc/services/database/instances.rst b/doc/services/database/instances.rst new file mode 100644 index 000000000..082e56c05 --- /dev/null +++ b/doc/services/database/instances.rst @@ -0,0 +1,155 @@ +Instances +========= + +Create a new instance +--------------------- + +.. code-block:: php + + // Create an empty object + $instance = $service->instance(); + + // Send to the API + $instance->create(array( + 'name' => '{name}', + 'flavor' => $service->flavor('{flavorId}'), + 'volume' => array('size' => 4) // 4GB of volume disk + )); + +`Get the executable PHP script for this sample `__ + + +Waiting for the instance to build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The SDK provides a blocking operation that will wait until your instance resource +has transitioned into an ``ACTIVE`` state. During this period, it will +continuously poll the API and break the loop when the state has been achieved: + +.. code-block:: php + + $instance->waitFor('ACTIVE', null, function ($instance) { + // This will be executed continuously + printf("Database instance build status: %s\n", $instance->status); + }); + + +Connecting an instance to a load balancer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The instance created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code-block:: php + + $lbService = $client->loadBalancerService(null, '{region}'); + + // Create empty object + $loadBalancer = $lbService->loadBalancer(); + + // Associate this LB with the instance as a "node" + $loadBalancer->addNode($instance->hostname, 3306); + $loadBalancer->addVirtualIp('PUBLIC'); + + // Configure other parameters and send to the API + $loadBalancer->create(array( + 'name' => 'DB Load Balancer', + 'port' => 3306, + 'protocol' => 'MYSQL', + )); + + // Wait for the resource to create + $loadBalancer->waitFor('ACTIVE', null, function ($loadBalancer) { + printf("Load balancer build status: %s\n", $loadBalancer->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. + + +Retrieving an instance +---------------------- + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + +`Get the executable PHP script for this example `__ + + +Updating an instance +-------------------- + +An instance can be updated to use a specific `configuration `__ as shown below. + +.. code-block:: php + + $instance->update(array( + 'configuration' => '{configurationId}' + )); + +.. note:: + + If any parameters in the associated configuration require a restart, then you + will need to `restart the instance <#restarting-an-instance>`__ after the update. + + +Deleting an instance +-------------------- + +.. code-block:: php + + $instance->delete(); + + +Restarting an instance +---------------------- + +.. code-block:: php + + $instance->restart(); + + +Resizing an instance's RAM +-------------------------- + +To change the amount of RAM allocated to the instance: + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + $instance->resize($flavor); + + +Resizing an instance's volume +----------------------------- + +You can also independently change the volume size to increase the disk +space: + +.. code-block:: php + + // Increase to 8GB disk + $instance->resizeVolume(8); diff --git a/doc/services/database/users.rst b/doc/services/database/users.rst new file mode 100644 index 000000000..0343c4904 --- /dev/null +++ b/doc/services/database/users.rst @@ -0,0 +1,67 @@ +Users +===== + +Setup +----- + +Finally, in order to interact with the functionality of databases, you must +first retrieve the details of the instance itself. To do this, you must +substitute `{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating users +-------------- + +Database users exist at the ``Instance`` level, but can be associated +with a specific ``Database``. They are represented by the +``OpenCloud\Database\Resource\User`` class. + +.. code-block:: php + + // New instance of OpenCloud\Database\Resource\User + $user = $instance->user(); + + // Send to API + $user->create(array( + 'name' => 'Alice', + 'password' => 'fooBar' + 'databases' => array('production') + )); + + +Deleting a user +--------------- + +.. code-block:: php + + $user->delete(); + + +The root user +------------- + +By default, Cloud Databases does not enable the root user. In most +cases, the root user is not needed, and having one can leave you open to +security violations. However, if you do want to enable access to the root user: + +.. code-block:: php + + $rootUser = $instance->enableRootUser(); + + +This returns a regular ``User`` object with the ``name`` attribute set +to ``root`` and the ``password`` attribute set to an auto-generated +password. + + +Check if root user is enabled +----------------------------- + +.. code-block:: php + + // true for yes, false for no + $instance->isRootEnabled(); From f29826dc43bf6aac97eb67a3e87f3ce7560f9b5c Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 4 Mar 2015 15:16:07 +0100 Subject: [PATCH 716/835] Identity docs --- doc/services/identity/Service.md.rst | 35 ----- doc/services/identity/Tenants.md.rst | 29 ---- doc/services/identity/Tokens.md.rst | 105 ------------- doc/services/identity/index.rst | 46 ++++++ .../identity/{Roles.md.rst => roles.rst} | 0 doc/services/identity/tenants.rst | 26 ++++ doc/services/identity/tokens.rst | 101 +++++++++++++ .../identity/{Users.md.rst => users.rst} | 139 ++++++++---------- 8 files changed, 238 insertions(+), 243 deletions(-) delete mode 100644 doc/services/identity/Service.md.rst delete mode 100644 doc/services/identity/Tenants.md.rst delete mode 100644 doc/services/identity/Tokens.md.rst rename doc/services/identity/{Roles.md.rst => roles.rst} (100%) create mode 100644 doc/services/identity/tenants.rst create mode 100644 doc/services/identity/tokens.rst rename doc/services/identity/{Users.md.rst => users.rst} (79%) diff --git a/doc/services/identity/Service.md.rst b/doc/services/identity/Service.md.rst deleted file mode 100644 index f7e8e12ba..000000000 --- a/doc/services/identity/Service.md.rst +++ /dev/null @@ -1,35 +0,0 @@ -Identity service -================ - -Intro ------ - -The Identity service is regionless, so you do not need to specify a -region when instantiating the service object. Although this was -primarily based on Rackspace's implementation of Cloud Identity, it -should also work for OpenStack Keystone. - -A note on object creation -------------------------- - -Normally, when services are created the client handles authenticates -automatically. But because Keystone/Identity is fundamental to the -authentication process itself, it proves difficult to do this procedure -as its normally done. For this reason, you have two options when -creating the service object: - -1: Use the client's factory method - -.. code:: php - - $identity = $client->identityService(); - -2: Authenticate manually - -.. code:: php - - use OpenCloud\Identity\Service as IdentityService; - - $identity = IdentityService::factory($client); - $identity->getClient()->authenticate(); - diff --git a/doc/services/identity/Tenants.md.rst b/doc/services/identity/Tenants.md.rst deleted file mode 100644 index 9b58efd1a..000000000 --- a/doc/services/identity/Tenants.md.rst +++ /dev/null @@ -1,29 +0,0 @@ -Tenants -======= - -Intro ------ - -A tenant is a container used to group or isolate resources and/or -identity objects. Depending on the service operator, a tenant may map to -a customer, account, organization, or project. - -Setup ------ - -Tenant objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -List tenants ------------- - -.. code:: php - - $tenants = $service->getTenants(); - - foreach ($tenants as $tenant) { - // ... - } - -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. diff --git a/doc/services/identity/Tokens.md.rst b/doc/services/identity/Tokens.md.rst deleted file mode 100644 index c42ce1573..000000000 --- a/doc/services/identity/Tokens.md.rst +++ /dev/null @@ -1,105 +0,0 @@ -Tokens -====== - -Intro ------ - -A token is an opaque string that represents an authorization to access -cloud resources. Tokens may be revoked at any time and are valid for a -finite duration. - -Setup ------ - -Token objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -Useful object properties/methods --------------------------------- - -+------------+-------------------------------------------+----------------------------------------+--------------------+ -| Property | Description | Getter | Setter | -+============+===========================================+========================================+====================+ -| id | The unique ID of the token | ``getId()`` | ``setId()`` | -+------------+-------------------------------------------+----------------------------------------+--------------------+ -| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | -+------------+-------------------------------------------+----------------------------------------+--------------------+ - -Create token (authenticate) ---------------------------- - -In order to generate a token, you must pass in the JSON template that is -sent to the API. This is because Rackspace's operation expects a -slightly different entity body than OpenStack Keystone. - -Request body for Rackspace's generate token operation: - -.. code:: json - - { - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "foo", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1100111" - } - } - -Request body for Keystone's generate token operation: - -.. code:: json - - { - "auth": { - "passwordCredentials":{ - "username":"demoauthor", - "password":"theUsersPassword" - }, - "tenantId": "12345678" - } - } - -The only real differences you'll notice is the name of the object key -(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret -(``apiKey``/``password``). The ``tenantId`` property in both templates -are optional. You can also add ``tenantName`` too. - -.. code:: php - - use OpenCloud\Common\Http\Message\Formatter; - - $template = sprintf( - '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', - 'my_username', - 'my_api_key' - ); - - $response = $service->generateToken($template); - - $body = Formatter::decode($response); - - // service catalog - $catalog = $body->access->serviceCatalog; - - // token - $token = $body->access->token; - - // user - $user = $body->access->user; - -As you will notice, these variables will be stdClass objects - for fully -fledged functionality, let the client authenticate by itself because it -ends up stocking the necessary models for you. - -To see the response body structure, consult the `official -docs `__. - -Revoke token (destroy session) ------------------------------- - -.. code:: php - - $tokenId = '1234567'; - $service->revokeToken($tokenId); - diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index e69de29bb..35c27131b 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -0,0 +1,46 @@ +Identity v2 +=========== + +Setup +----- + +.. include:: ../common/rs-client.sample + +.. code-block:: php + + $service = $client->identityService(); + + +Operations +---------- + +.. toctree:: + + tokens + users + tenants + +Glossary +-------- + +.. glossary:: + + token + A token is an opaque string that represents an authorization to access + cloud resources. Tokens may be revoked at any time and are valid for a + finite duration. + + tenant + A tenant is a container used to group or isolate resources and/or + identity objects. Depending on the service operator, a tenant may map to + a customer, account, organization, or project. + + user + A user is a digital representation of a person, system, or service who + consumes cloud services. Users have credentials and may be assigned + tokens; based on these credentials and tokens, the authentication + service validates that incoming requests are being made by the user who + claims to be making the request, and that the user has the right to + access the requested resources. Users may be directly assigned to a + particular tenant and behave as if they are contained within that + tenant. diff --git a/doc/services/identity/Roles.md.rst b/doc/services/identity/roles.rst similarity index 100% rename from doc/services/identity/Roles.md.rst rename to doc/services/identity/roles.rst diff --git a/doc/services/identity/tenants.rst b/doc/services/identity/tenants.rst new file mode 100644 index 000000000..a27417cca --- /dev/null +++ b/doc/services/identity/tenants.rst @@ -0,0 +1,26 @@ +Tenants +======= + +List tenants +------------ + +.. code-block:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +Tenant object properties and methods +------------------------------------ + +Once you have a ``OpenCloud\Identity\Resource\Tenant`` object, you can retrieve +information like so: + +.. code-block:: php + + $tenant->getId(); + $tenant->getName(); + $tenant->getDescription(); + $tenant->isEnabled(); diff --git a/doc/services/identity/tokens.rst b/doc/services/identity/tokens.rst new file mode 100644 index 000000000..f49484105 --- /dev/null +++ b/doc/services/identity/tokens.rst @@ -0,0 +1,101 @@ +Tokens +====== + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +To do this, and then generate a token: + +.. code-block:: php + + $json = $client->getCredentials(); + + /** @var $response Guzzle\Http\Message\Response */ + $response = $service->generateToken($json); + $jsonBody = $response->json(); + +When a token is generated by the API, there are a few things returned: + +* a `service catalog `_ + outlining all of the services you can interact with, + including their names, service types, and endpoint URLs. Which services + make up your catalog, and how your catalog is structured, will depend on + your service provider. + +* details about your token, such as its ID, created and expiration date + +* details about your user account + +* details about your tenant + +Interacting with the service catalog +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have the ``$jsonBody``, you can construct a ``Catalog`` object for +easier interaction: + +.. code-block:: php + + $data = $jsonBody->access->serviceCatalog; + $catalog = OpenCloud\Common\Service\Catalog::factory($data); + + foreach ($catalog->getItems() as $service) { + /** @param $service OpenCloud\Common\Service\CatalogItem */ + printf("Catalog item: Name [%s] Type [%s]\n", $service->getName(), $service->getType()); + + foreach ($service->getEndpoints() as $endpoint) { + printf(" Endpoint provided: Region [%s] PublicURL [%s] PrivateURL [%s]\n", + $endpoint->getRegion(), $endpoint->getPublicUrl(), $endpoint->getPrivateUrl()); + } + } + +Interacting with tokens +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->token; + $token = $service->resource('Token', $data); + + printf("Token ID: %s - Token expiry %s", $token->getId(), $token->getExpires()); + + if ($token->hasExpired()) { + // ... + } + +Interacting with users +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->user; + $user = $service->resource('User', $data); + +To see which methods you can call on ``$user`` (which implements +``OpenCloud\Identity\Resource\User``), see our `user documentation `_ +which accompanies this guide. + + +Interacting with tenants +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->tenant; + $tenant = $service->resource('Tenant', $data); + +To see which methods you can call on ``$tenant`` (which implements +``OpenCloud\Identity\Resource\Tenant``), see our `user documentation `_ +which accompanies this guide. + + +Revoke token (destroy session) +------------------------------ + +.. code-block:: php + + $service->revokeToken('{tokenId}'); diff --git a/doc/services/identity/Users.md.rst b/doc/services/identity/users.rst similarity index 79% rename from doc/services/identity/Users.md.rst rename to doc/services/identity/users.rst index e8c2e6d63..b55236a74 100644 --- a/doc/services/identity/Users.md.rst +++ b/doc/services/identity/users.rst @@ -1,26 +1,9 @@ Users ===== -Intro ------ -A user is a digital representation of a person, system, or service who -consumes cloud services. Users have credentials and may be assigned -tokens; based on these credentials and tokens, the authentication -service validates that incoming requests are being made by the user who -claims to be making the request, and that the user has the right to -access the requested resources. Users may be directly assigned to a -particular tenant and behave as if they are contained within that -tenant. - -Setup ------ - -User objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - -Useful object properties/methods --------------------------------- +Object properties/methods +------------------------- +-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ | Property | Description | Getter | Setter | @@ -43,74 +26,78 @@ Useful object properties/methods List users ---------- -.. code:: php +.. code-block:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } - $users = $service->getUsers(); - foreach ($users as $user) { - // ... - } +Retrieve a user by username +--------------------------- -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. +.. code-block:: php -Get user --------- + $user = $service->getUser('jamie'); -There are various ways to get a specific user: by name, ID and email -address. -.. code:: php +Retrieve a user by user ID +-------------------------- - use OpenCloud\Identity\Constants\User as UserConst; +.. code-block:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{userId}', UserConst::MODE_ID); + + +Retrieve a user by email address +-------------------------------- - // Get user by name - $user1 = $service->getUser('jamie'); +.. code-block:: php - // Get user by ID - $user2 = $service->getUser(123456, UserConst::MODE_ID); + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); - // Get user by email - $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); Create user ----------- -There are a few things to remember when creating a user: +There are a few things to bear in mind when creating a user: -- This operation is available only to users who hold the +* This operation is available only to users who hold the ``identity:user-admin`` role. This admin can create a user who holds the ``identity:default`` user role. -- The created user **will** have access to APIs but **will not** have +* The created user **will** have access to APIs but **will not** have access to the Cloud Control Panel. -- Within an account, a maximum of 100 account users can be added. +* A maximum of 100 account users can be added per account. -- If you attempt to add a user who already exists, an HTTP error 409 +* If you attempt to add a user who already exists, an HTTP error 409 results. + The ``username`` and ``email`` properties are required for creating a user. Providing a ``password`` is optional; if omitted, one will be automatically generated and provided in the response. -.. code:: php - use Guzzle\Http\Exception\ClientErrorResponseException; +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; - try { - // execute operation - $user = $service->createUser(array( - 'username' => 'newUser', - 'email' => 'foo@bar.com' - )); - } catch (ClientErrorResponseException $e) { - // catch 4xx HTTP errors - echo $e->getResponse()->toString(); - } + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + + // show generated password + echo $user->getPassword(); - // show generated password - echo $user->getPassword(); Update user ----------- @@ -118,27 +105,30 @@ Update user When updating a user, specify which attribute/property you want to update: -.. code:: php +.. code-block:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); - $user->update(array( - 'email' => 'new_email@bar.com' - )); Updating a user password -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ Updating a user password requires calling a distinct method: -.. code:: php +.. code-block:: php + + $user->updatePassword('password123'); - $user->updatePassword('password123'); Delete user ----------- -.. code:: php +.. code-block:: php + + $user->delete(); - $user->delete(); List credentials ---------------- @@ -146,16 +136,18 @@ List credentials This operation allows you to see your non-password credential types for all authentication methods available. -.. code:: php +.. code-block:: php + + $creds = $user->getOtherCredentials(); - $creds = $user->getOtherCredentials(); Get user API key ---------------- -.. code:: php +.. code-block:: php + + echo $user->getApiKey(); - echo $user->getApiKey(); Reset user API key ------------------ @@ -163,8 +155,7 @@ Reset user API key When resetting an API key, a new one will be automatically generated for you: -.. code:: php - - $user->resetApiKey(); - echo $user->getApiKey(); +.. code-block:: php + $user->resetApiKey(); + echo $user->getApiKey(); From 305bd1ea72415d202f4b7e63ca67f1a5b091db0a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 22:43:10 -0800 Subject: [PATCH 717/835] Adding unit tests + implementation for Neutron Security Groups and Security Group Rules. --- lib/OpenCloud/Networking/Service.php | 100 ++++++++++++++++++ .../Tests/Networking/NetworkingTestCase.php | 10 ++ .../Tests/Networking/ServiceTest.php | 46 ++++++++ 3 files changed, 156 insertions(+) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 3c1f879e2..12bb7477f 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -22,6 +22,8 @@ use OpenCloud\Networking\Resource\Network; use OpenCloud\Networking\Resource\Subnet; use OpenCloud\Networking\Resource\Port; +use OpenCloud\Networking\Resource\SecurityGroup; +use OpenCloud\Networking\Resource\SecurityGroupRule; /** * The Networking class represents the OpenNetwork Neutron service. @@ -290,6 +292,104 @@ public function listPorts(array $params = array()) return $this->resourceList('Port', $url); } + /** + * Returns a SecurityGroup object associated with this Networking service + * + * @param string $id ID of security group to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroup object + */ + public function securityGroup($id = null) + { + return $this->resource('SecurityGroup', $id); + } + + /** + * Creates a new SecurityGroup and returns it. + * + * @param array $params SecurityGroup creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group + * @return \OpenCloud\Networking\Resource\SecurityGroup Object representing created security group + */ + public function createSecurityGroup(array $params = array()) + { + $securityGroup = $this->securityGroup(); + $securityGroup->create($params); + return $securityGroup; + } + + /** + * Returns a SecurityGroup object associated with this Networking service + * + * @param string $id ID of security group to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroup object + */ + public function getSecurityGroup($id) + { + return $this->securityGroup($id); + } + + /** + * Returns a list of security groups you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSecurityGroups(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(SecurityGroup::resourceName())->setQuery($params); + + return $this->resourceList('SecurityGroup', $url); + } + + /** + * Returns a SecurityGroupRule object associated with this Networking service + * + * @param string $id ID of security group rule to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroupRule object + */ + public function securityGroupRule($id = null) + { + return $this->resource('SecurityGroupRule', $id); + } + + /** + * Creates a new SecurityGroupRule and returns it. + * + * @param array $params SecurityGroupRule creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule + * @return \OpenCloud\Networking\Resource\SecurityGroupRule Object representing created security group rule + */ + public function createSecurityGroupRule(array $params = array()) + { + $securityGroupRule = $this->securityGroupRule(); + $securityGroupRule->create($params); + return $securityGroupRule; + } + + /** + * Returns a SecurityGroupRule object associated with this Networking service + * + * @param string $id ID of security group rule to retrieve + * @return \OpenCloud\Networking\Resource\SecurityGroupRule object + */ + public function getSecurityGroupRule($id) + { + return $this->securityGroupRule($id); + } + + /** + * Returns a list of security group rules you created + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function listSecurityGroupRules(array $params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(SecurityGroupRule::resourceName())->setQuery($params); + + return $this->resourceList('SecurityGroupRule', $url); + } + /** * Return namespaces. * diff --git a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php index be6b304c4..399094b6e 100644 --- a/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php +++ b/tests/OpenCloud/Tests/Networking/NetworkingTestCase.php @@ -48,4 +48,14 @@ protected function assertIsPort($object) { $this->assertInstanceOf('OpenCloud\Networking\Resource\Port', $object); } + + protected function assertIsSecurityGroup($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\SecurityGroup', $object); + } + + protected function assertIsSecurityGroupRule($object) + { + $this->assertInstanceOf('OpenCloud\Networking\Resource\SecurityGroupRule', $object); + } } diff --git a/tests/OpenCloud/Tests/Networking/ServiceTest.php b/tests/OpenCloud/Tests/Networking/ServiceTest.php index 92b793e19..f2ab2cbfe 100644 --- a/tests/OpenCloud/Tests/Networking/ServiceTest.php +++ b/tests/OpenCloud/Tests/Networking/ServiceTest.php @@ -146,4 +146,50 @@ public function testGetPort() $this->assertIsPort($port); $this->assertEquals('port1', $port->getName()); } + + public function testCreateSecurityGroup() + { + $this->assertIsSecurityGroup($this->service->createSecurityGroup()); + } + + public function testListSecurityGroups() + { + $this->addMockSubscriber($this->makeResponse('{"security_groups":[{"description":"default","id":"85cc3048-abc3-43cc-89b3-377341426ac5","name":"default","security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}],"tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}]}')); + + $securityGroups = $this->service->listSecurityGroups(); + $this->isCollection($securityGroups); + $this->assertIsSecurityGroup($securityGroups->getElement(0)); + } + + public function testGetSecurityGroup() + { + $this->addMockSubscriber($this->makeResponse('{"security_group":{"description":"default","id":"85cc3048-abc3-43cc-89b3-377341426ac5","name":"default","security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}],"tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}}')); + + $securityGroup = $this->service->getSecurityGroup('85cc3048-abc3-43cc-89b3-377341426ac5'); + $this->assertIsSecurityGroup($securityGroup); + $this->assertEquals('default', $securityGroup->getName()); + } + + public function testCreateSecurityGroupRule() + { + $this->assertIsSecurityGroupRule($this->service->createSecurityGroupRule()); + } + + public function testListSecurityGroupRules() + { + $this->addMockSubscriber($this->makeResponse('{"security_group_rules":[{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"egress","ethertype":"IPv4","id":"93aa42e5-80db-4581-9391-3a608bd0e448","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv6","id":"c0b09f00-1d49-4e64-a0a7-8a186d928138","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"},{"direction":"ingress","ethertype":"IPv4","id":"f7d45c89-008e-4bab-88ad-d6811724c51c","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}]}')); + + $securityGroupRules = $this->service->listSecurityGroupRules(); + $this->isCollection($securityGroupRules); + $this->assertIsSecurityGroupRule($securityGroupRules->getElement(0)); + } + + public function testGetSecurityGroupRule() + { + $this->addMockSubscriber($this->makeResponse('{"security_group_rule":{"direction":"egress","ethertype":"IPv6","id":"3c0e45ff-adaf-4124-b083-bf390e5482ff","port_range_max":null,"port_range_min":null,"protocol":null,"remote_group_id":null,"remote_ip_prefix":null,"security_group_id":"85cc3048-abc3-43cc-89b3-377341426ac5","tenant_id":"e4f50856753b4dc6afee5fa6b9b6c550"}}')); + + $securityGroupRule = $this->service->getSecurityGroupRule('3c0e45ff-adaf-4124-b083-bf390e5482ff'); + $this->assertIsSecurityGroupRule($securityGroupRule); + $this->assertEquals('egress', $securityGroupRule->getDirection()); + } } From 7b6a2f0df90b09b43769b281c34dea7c886c9e5f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:09:09 -0800 Subject: [PATCH 718/835] Adding userguide documentation for security groups and security group rules. --- docs/userguide/Networking/USERGUIDE.md | 147 +++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 74e713b8a..bfa1a217c 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -33,6 +33,16 @@ these entities. * [Get a port](#get-a-port) * [Update a port](#update-a-port) * [Delete a port](#delete-a-port) + * [Security Groups](#security-groups) + * [Create a security group](#create-a-security-group) + * [List security groups](#list-security-groups) + * [Get a security group](#get-a-security-group) + * [Delete a security group](#delete-a-security-group) + * [Security group rule Rules](#security-group-rules) + * [Create a security group rule](#create-a-security-group-rule) + * [List security group rules](#list-security-group-rules) + * [Get a security group rule](#get-a-security-group-rule) + * [Delete a security group rule](#delete-a-security-group-rule) ## Concepts @@ -55,6 +65,10 @@ be assigned to the interfaces plugged into them. When IP addresses are associated with a port, this also implies the port is associated with a subnet because the IP address is taken from the allocation pool for a specific subnet. +* **Security Group**: TODO + +* **Security Group Rule**: TODO + ## Prerequisites ### Client @@ -468,3 +482,136 @@ $port->delete(); ``` [ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] + +## Security Groups + +TODO: Add description + +### Create a security group + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `name` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | `new-webservers` | +| `description` | Description of the security group. | String | No | `null` | `security group for webservers` | + +You can create a security group as shown in the following example: + +```php +$securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' +)); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-security-group.php) ] + +### List security groups + +You can list all the security groups to which you have access as shown in the following +example: + +```php +$securityGroups = $networkingService->listSecurityGroups(); +foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-security-groups.php) ] + +### Get a security group + +You can retrieve a specific security group by using that security group's ID, as shown in the +following example: + +```php +$securityGroup = $networkingService->getSecurityGroup('2076db17-a522-4506-91de-c6dd8e837028'); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-security-group.php) ] + +### Delete a security group + +You can delete a security group as shown in the following example: + +```php +$securityGroup->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group.php) ] + +## Security Group Rules + +TODO: Add description + +### Create a security group rule + +This operation takes one parameter, an associative array, with the following keys: + +| Name | Description | Data type | Required? | Default value | Example value | +| ---- | ----------- | --------- | --------- | ------------- | ------------- | +| `securityGroupId` | The security group ID to associate with this security group rule. | String | Yes | - | `2076db17-a522-4506-91de-c6dd8e837028` | +| `direction` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (`ingress` or `egress`) | Yes | - | `ingress` | +| `ethertype` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (`IPv4` or `IPv6`) | No | `IPv4` | `IPv6` | +| `portRangeMin` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the `portRangeMax` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | +| `portRangeMax` | The maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | +| `protocol` | The protocol that is matched by the security group rule. | String (`tcp`, `udp`, `icmp`) | No | `null` | `tcp` | +| `remoteGroupId` | The remote group ID to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `85cc3048-abc3-43cc-89b3-377341426ac5` | +| `remoteIpPrefix` | The remote IP prefix to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `192.168.5.0` | + +You can create a security group rule as shown in the following example: + +```php +$securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' +)); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/create-security-group-rule.php) ] + +### List security group rules + +You can list all the security group rules to which you have access as shown in the following +example: + +```php +$securityGroupRules = $networkingService->listSecurityGroupRules(); +foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +} +``` + +[ [Get the executable PHP script for this example](/samples/Networking/list-security-group-rules.php) ] + +### Get a security group rule + +You can retrieve a specific security group rule by using that security group rule's ID, as shown in the +following example: + +```php +$securityGroupRule = $networkingService->getSecurityGroupRule(''); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +``` + +[ [Get the executable PHP script for this example](/samples/Networking/get-security-group-rule.php) ] + +### Delete a security group rule + +You can delete a security group rule as shown in the following example: + +```php +$securityGroupRule->delete(); +``` + +[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group-rule.php) ] From c5f8630cbe12309acd5058b745ba1a7b33d3df0d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:24:17 -0800 Subject: [PATCH 719/835] Adding downloadable code samples for Security Groups and Security Group Rules. --- .../Networking/create-security-group-rule.php | 42 +++++++++++++++++++ samples/Networking/create-security-group.php | 37 ++++++++++++++++ .../Networking/delete-security-group-rule.php | 36 ++++++++++++++++ samples/Networking/delete-security-group.php | 36 ++++++++++++++++ .../Networking/get-security-group-rule.php | 34 +++++++++++++++ samples/Networking/get-security-group.php | 34 +++++++++++++++ .../Networking/list-security-group-rules.php | 36 ++++++++++++++++ samples/Networking/list-security-groups.php | 36 ++++++++++++++++ 8 files changed, 291 insertions(+) create mode 100644 samples/Networking/create-security-group-rule.php create mode 100644 samples/Networking/create-security-group.php create mode 100644 samples/Networking/delete-security-group-rule.php create mode 100644 samples/Networking/delete-security-group.php create mode 100644 samples/Networking/get-security-group-rule.php create mode 100644 samples/Networking/get-security-group.php create mode 100644 samples/Networking/list-security-group-rules.php create mode 100644 samples/Networking/list-security-groups.php diff --git a/samples/Networking/create-security-group-rule.php b/samples/Networking/create-security-group-rule.php new file mode 100644 index 000000000..e60059774 --- /dev/null +++ b/samples/Networking/create-security-group-rule.php @@ -0,0 +1,42 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Create a security group rule. +$securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' +)); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ diff --git a/samples/Networking/create-security-group.php b/samples/Networking/create-security-group.php new file mode 100644 index 000000000..bf69ad044 --- /dev/null +++ b/samples/Networking/create-security-group.php @@ -0,0 +1,37 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Create a security group. +$securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' +)); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ diff --git a/samples/Networking/delete-security-group-rule.php b/samples/Networking/delete-security-group-rule.php new file mode 100644 index 000000000..d7cf988dd --- /dev/null +++ b/samples/Networking/delete-security-group-rule.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group rule. +$securityGroupRule = $networkingService->getSecurityGroupRule('{securityGroupRuleId}'); + +// 4. Delete security group rule. +$securityGroupRule->delete(); diff --git a/samples/Networking/delete-security-group.php b/samples/Networking/delete-security-group.php new file mode 100644 index 000000000..758da3ae8 --- /dev/null +++ b/samples/Networking/delete-security-group.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group. +$securityGroup = $networkingService->getSecurityGroup('{securityGroupId}'); + +// 4. Delete security group. +$securityGroup->delete(); diff --git a/samples/Networking/get-security-group-rule.php b/samples/Networking/get-security-group-rule.php new file mode 100644 index 000000000..f6ea2bc25 --- /dev/null +++ b/samples/Networking/get-security-group-rule.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group rule. +$securityGroupRule = $networkingService->getSecurityGroupRule('{securityGroupRuleId}'); +/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ diff --git a/samples/Networking/get-security-group.php b/samples/Networking/get-security-group.php new file mode 100644 index 000000000..7573aa1f9 --- /dev/null +++ b/samples/Networking/get-security-group.php @@ -0,0 +1,34 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. Get security group. +$securityGroup = $networkingService->getSecurityGroup('{securityGroupId}'); +/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ diff --git a/samples/Networking/list-security-group-rules.php b/samples/Networking/list-security-group-rules.php new file mode 100644 index 000000000..1adba50d7 --- /dev/null +++ b/samples/Networking/list-security-group-rules.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List security group rules +$securityGroupRules = $networkingService->listSecurityGroupRules(); +foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ +} diff --git a/samples/Networking/list-security-groups.php b/samples/Networking/list-security-groups.php new file mode 100644 index 000000000..82ffd0931 --- /dev/null +++ b/samples/Networking/list-security-groups.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List security groups +$securityGroups = $networkingService->listSecurityGroups(); +foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ +} From 271f8dbe11a418157aa5c82ed89e4c99d52454c8 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:40:40 -0800 Subject: [PATCH 720/835] Adding smoke tests for Security Groups and Security Group Rules. --- tests/OpenCloud/Smoke/Unit/Networking.php | 80 ++++++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 74f39fac7..b6ab4592e 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -26,9 +26,11 @@ */ class Networking extends AbstractUnit implements UnitInterface { - protected $cleanupNetworkIds = array(); - protected $cleanupSubnetIds = array(); - protected $cleanupPortIds = array(); + protected $cleanupNetworkIds = array(); + protected $cleanupSubnetIds = array(); + protected $cleanupPortIds = array(); + protected $cleanupSecurityGroupIds = array(); + protected $cleanupSecurityGroupRuleIds = array(); public function setupService() { @@ -40,6 +42,8 @@ public function main() $this->testNetworkOperations(); $this->testSubnetOperations(); $this->testPortOperations(); + $this->testSecurityGroupOperations(); + $this->testSecurityGroupRuleOperations(); } protected function testNetworkOperations() @@ -251,8 +255,78 @@ protected function testPortOperations() )); } + protected function testSecurityGroupOperations() + { + $this->step('Create security group'); + $security group = $this->getService()->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); + $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + $this->cleanupSecurityGroupIds[] = $securityGroup->getId(); + + $this->step('List security groups'); + $securityGroups = $this->getService()->listSecurityGroups(); + $this->stepInfo('%-40s | %s', 'Security Group ID', 'Security Group name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($securityGroups as $securityGroup) { + $this->stepInfo('%-40s | %s', $securityGroup->getId(), $securityGroup->getName()); + } + + $this->step('Get security group'); + $security group = $this->getService()->getSecurityGroup($securityGroup->getId()); + $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); + $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + } + + protected function testSecurityGroupRuleOperations() + { + $securityGroup1 = $this->getService()->createSecurityGroup(array( + 'name' => 'test_security_group_for_test_security_group_rule' + )); + $this->cleanupSecurityGroupIds[] = $securityGroup1->getId(); + + $this->step('Create security group rule'); + $security group rule = $this->getService()->createSecurityGroupRule(array( + 'securityGroupId' => $securityGroup1->getId(), + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); + $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); + $this->cleanupSecurityGroupRuleIds[] = $securityGroupRule->getId(); + + $this->step('List security group rules'); + $securityGroupRules = $this->getService()->listSecurityGroupRules(); + $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule name'); + $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); + foreach ($securityGroupRules as $securityGroupRule) { + $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getName()); + } + + $this->step('Get security group rule'); + $security group rule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); + $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); + $this->stepInfo('Security Group Rule Name: ' . $securityGroupRule->getName()); + } + public function teardown() { + foreach ($this->cleanupSecurityGroupRuleIds as $securityGroupRuleId) { + $securityGroupRule = $this->getService()->getSecurityGroupRule($securityGroupRuleId); + $securityGroupRule->delete(); + } + + foreach ($this->cleanupSecurityGroupIds as $securityGroupId) { + $securityGroup = $this->getService()->getSecurityGroup($securityGroupId); + $securityGroup->delete(); + } + foreach ($this->cleanupPortIds as $portId) { $port = $this->getService()->getPort($portId); $port->delete(); From 0b0b54eca816b729a68d6f7988936acd44dbd449 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 4 Mar 2015 23:41:48 -0800 Subject: [PATCH 721/835] Forgot to check-in resource classes. --- .../Networking/Resource/SecurityGroup.php | 67 +++++++++++++++ .../Networking/Resource/SecurityGroupRule.php | 82 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 lib/OpenCloud/Networking/Resource/SecurityGroup.php create mode 100644 lib/OpenCloud/Networking/Resource/SecurityGroupRule.php diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php new file mode 100644 index 000000000..105fc22a1 --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -0,0 +1,67 @@ + 'securityGroupRules', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'name', + 'description' + ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as {@see OpenCloud\Networking\Service}. + */ + public function createJson() + { + return parent::createJson(); + } + + /** + * {@inheritDoc} + */ + public function update($params = array()) + { + return $this->noUpdate(); + } +} diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php new file mode 100644 index 000000000..3659d08ac --- /dev/null +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -0,0 +1,82 @@ + 'portRangeMin', + 'port_range_max' => 'portRangeMax', + 'remote_group_id' => 'remoteGroupId', + 'remote_ip_prefix' => 'remoteIpPrefix', + 'security_group_id' => 'securityGroupId', + 'tenant_id' => 'tenantId' + ); + + protected $createKeys = array( + 'direction', + 'ethertype', + 'securityGroupId', + 'portRangeMin', + 'portRangeMax', + 'protocol', + 'remoteGroupId', + 'remoteIpPrefix' + ); + + /** + * This method is inherited. The inherited method has protected scope + * but we are widening the scope to public so this method may be called + * from other classes such as {@see OpenCloud\Networking\Service}. + */ + public function createJson() + { + return parent::createJson(); + } + + /** + * {@inheritDoc} + */ + public function update($params = array()) + { + return $this->noUpdate(); + } +} From 309c01d51998ba26d550c98e8caeeac9bbbd1215 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:11 +0100 Subject: [PATCH 722/835] Add docs for Images --- doc/services/image/Images.md.rst | 107 ------------------ doc/services/image/Tags.md.rst | 28 ----- doc/services/image/images.rst | 84 ++++++++++++++ doc/services/image/index.rst | 41 +++++++ .../image/{Schemas.md.rst => schemas.rst} | 29 ++--- .../image/{Sharing.md.rst => sharing.rst} | 50 ++++---- doc/services/image/tags.rst | 29 +++++ 7 files changed, 189 insertions(+), 179 deletions(-) delete mode 100644 doc/services/image/Images.md.rst delete mode 100644 doc/services/image/Tags.md.rst create mode 100644 doc/services/image/images.rst rename doc/services/image/{Schemas.md.rst => schemas.rst} (87%) rename doc/services/image/{Sharing.md.rst => sharing.rst} (75%) create mode 100644 doc/services/image/tags.rst diff --git a/doc/services/image/Images.md.rst b/doc/services/image/Images.md.rst deleted file mode 100644 index ef187f083..000000000 --- a/doc/services/image/Images.md.rst +++ /dev/null @@ -1,107 +0,0 @@ -Images -====== - -A virtual machine image is a single file which contains a virtual disk -that has an installed bootable operating system. In the Cloud Images -API, an image is represented by a JSON-encoded data structure (the image -schema) and its raw binary data (the image file). - -An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. - -Setup ------ - -You instantiate an Image object from its ``OpenCloud\Image\Service`` -class, which is available from the OpenStack/Rackspace client: - -.. code:: php - - $service = $client->imageService('cloudImages', 'IAD'); - -View the guides for more information about `clients <../Clients.md>`__ -or `services <../Services.md>`__. - -List images ------------ - -.. code:: php - - $images = $service->listImages(); - - foreach ($images as $image) { - /** @param $image OpenCloud\Image\Resource\Image */ - } - -For more information about working with iterators, please see the -`iterators documentation <../Iterators.md>`__. - -Get image details ------------------ - -.. code:: php - - /** @param $image OpenCloud\Image\Resource\Image */ - $image = $service->getImage(''); - -A note on schema classes -~~~~~~~~~~~~~~~~~~~~~~~~ - -Both ``OpenCloud\Image\Resource\Image`` and -``OpenCloud\Image\Resource\Member`` extend the -``AbstractSchemaResource`` abstract class, which offers some unique -functionality. - -Because these resources are inherently dynamic - i.e. they are modelled -on dynamic JSON schema - you need to access their state in a way -different than conventional getter/setter methods, and even class -properties. For this reason, they implement SPL's native -```ArrayAccess`` `__ -interface which allows you to access their state as a conventional -array: - -.. code:: php - - $image = $service->getImage(''); - - $id = $image['id']; - $tags = $image['tags']; - -Update image ------------- - -You can only update your own custom images - you cannot update or delete -base images. The way in which you may update your image is dictated by -its `schema `__. - -Although you should be able to add new and replace existing properties, -always prepare yourself for a situation where it might be forbidden: - -.. code:: php - - use OpenCloud\Common\Exceptions\ForbiddenOperationException; - - try { - $image->update(array( - 'name' => 'foo', - 'newProperty' => 'bar' - )); - } catch (ForbiddenOperationException $e) { - // A 403 Forbidden was returned - } - -There are three operations that can take place for each Image property: - -- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation - will occur, removing the property from the JSON document -- If a non-false value is provided and the property does not exist, an - ``ADD`` operation will add it to the document -- If a non-false value is provided and the property does exist, a - ``REPLACE`` operation will modify the property in the document - -Delete image ------------- - -.. code:: php - - $image->delete(); - diff --git a/doc/services/image/Tags.md.rst b/doc/services/image/Tags.md.rst deleted file mode 100644 index af5baf157..000000000 --- a/doc/services/image/Tags.md.rst +++ /dev/null @@ -1,28 +0,0 @@ -Image tags -========== - -An image tag is a string of characters used to identify a specific image -or images. - -Setup ------ - -All member operations are executed against an `Image `__, so -you will need to set this up first. - -Add image tag -------------- - -.. code:: php - - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->addTag('jamie_dev'); - -Delete image tag ----------------- - -.. code:: php - - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->deleteTag('jamie_dev'); - diff --git a/doc/services/image/images.rst b/doc/services/image/images.rst new file mode 100644 index 000000000..ec1a17c34 --- /dev/null +++ b/doc/services/image/images.rst @@ -0,0 +1,84 @@ +Images +====== + +List images +----------- + +.. code-block:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + + +Get image details +----------------- + +.. code-block:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage('{imageId}'); + + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and ``OpenCloud\Image\Resource\Member`` +extend the ``AbstractSchemaResource`` class, which offers some unique functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a different way +than conventional getter/setter methods, and even class properties. For this +reason, they implement SPL's native +`ArrayAccess `_ +interface which allows you to access their state as a conventional +array: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + $id = $image['id']; + $tags = $image['tags']; + + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +* If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +* If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +* If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code-block:: php + + $image->delete(); diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index e69de29bb..d54b09ad0 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -0,0 +1,41 @@ +Images v1 +========= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +.. code-block:: php + + $service = $client->imageService(null, '{region}'); + + +Operations +---------- + +.. toctree:: + + images + schemas + sharing + tags + + +Glossary +-------- + + image + A virtual machine image is a single file which contains a virtual disk + that has an installed bootable operating system. In the Cloud Images + API, an image is represented by a JSON-encoded data structure (the image + schema) and its raw binary data (the image file). + + schema + The Cloud Images API supplies JSON documents describing the JSON-encoded + data structures that represent domain objects, so that a client knows + exactly what to expect in an API response. + + tag + An image tag is a string of characters used to identify a specific image + or images. diff --git a/doc/services/image/Schemas.md.rst b/doc/services/image/schemas.rst similarity index 87% rename from doc/services/image/Schemas.md.rst rename to doc/services/image/schemas.rst index 8f792c4c0..c1b85b8de 100644 --- a/doc/services/image/Schemas.md.rst +++ b/doc/services/image/schemas.rst @@ -1,13 +1,6 @@ JSON schemas ============ -The Cloud Images API supplies json documents describing the JSON-encoded -data structures that represent domain objects, so that a client knows -exactly what to expect in an API response. - -A JSON Schema is represented by the -``OpenCloud\Image\Resource\Schema\Schema`` class. - Schema types ------------ @@ -19,7 +12,7 @@ Example response from the API A sample response from the API, for an Images schema might be: -.. code:: json +.. code-block:: json { "name": "images", @@ -70,7 +63,7 @@ links and a properties object. Inside this properties object we see the structure of this top-level ``images`` object. So we know that it will take this form: -.. code:: json +.. code-block:: json { "images": [something...] @@ -80,7 +73,7 @@ Within this object, we can see that it contains an array of anonymous objects, each of which is called ``image`` and has its own set of nested properties: -.. code:: json +.. code-block:: json { "images": [ @@ -101,7 +94,7 @@ i.e. a *subschema*. We know that each object has an ID property (string), a name property (string), a visibility property (can either be ``private`` or ``public``), etc. -.. code:: json +.. code-block:: json { "images": [ @@ -147,21 +140,21 @@ Requests need to use the In order for the operation to occur, the request entity body needs to contain a very particular structure: -:: +.. code-block:: json [ {"op": "replace", "path": "/name", "value": "Fedora 17"}, {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} ] -The ``op`` key refers to the type of Operation (see -``OpenCloud\Image\Enum\OperationType`` for a full list). +* The ``op`` key refers to the type of Operation (see + ``OpenCloud\Image\Enum\OperationType`` for a full list). -The ``path`` key is a JSON pointer to the document property you want to -modify or insert. JSON pointers are defined in `RFC -6901 `__. +* The ``path`` key is a JSON pointer to the document property you want to + modify or insert. JSON pointers are defined in `RFC + 6901 `__. -The ``value`` key is the value. +* The ``value`` key is the value. Because this is all handled for you behind the scenes, we will not go into exhaustive depth about how this operation is handled. You can diff --git a/doc/services/image/Sharing.md.rst b/doc/services/image/sharing.rst similarity index 75% rename from doc/services/image/Sharing.md.rst rename to doc/services/image/sharing.rst index b1a1ea382..5edf63ab4 100644 --- a/doc/services/image/Sharing.md.rst +++ b/doc/services/image/sharing.rst @@ -63,66 +63,64 @@ Additional notes Setup ----- -All member operations are executed against an `Image `__, so -you will need to set this up first. +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + List image members ------------------ This operation is available for both producers and consumers. -.. code:: php +.. code-block:: php - $members = $image->listMembers(); + $members = $image->listMembers(); - foreach ($members as $member) { - /** @param $member OpenCloud\Image\Resource\Member */ - } + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } -For more information about working with iterators, please see the -`iterators documentation <../Iterators.md>`__. Create image member ------------------- This operation is only available for producers. -.. code:: php +.. code-block:: php - $tenantId = 12345; + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember('{tenantId}'); - /** @param $response Guzzle\Http\Message\Response */ - $response = $image->createMember($tenantId); Delete image member ------------------- This operation is only available for producers. -.. code:: php +.. code-block:: php - $tenantId = 12345; + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); + $member->delete(); - /** @param $member OpenCloud\Image\Resource\Member */ - $member = $image->getMember($tenantId); - - $member->delete(); Update image member status -------------------------- This operation is only available for consumers. -.. code:: php - - use OpenCloud\Images\Enum\MemberStatus; +.. code-block:: php - $tenantId = 12345; + use OpenCloud\Images\Enum\MemberStatus; - /** @param $member OpenCloud\Image\Resource\Member */ - $member = $image->getMember($tenantId); + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); - $member->updateStatus(MemberStatus::ACCEPTED); + $member->updateStatus(MemberStatus::ACCEPTED); The acceptable states you may pass in are made available to you through the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` diff --git a/doc/services/image/tags.rst b/doc/services/image/tags.rst new file mode 100644 index 000000000..0a376e6de --- /dev/null +++ b/doc/services/image/tags.rst @@ -0,0 +1,29 @@ +Image tags +========== + +Setup +----- + +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + +Add image tag +------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); From fa7820646d98c9385039b458d6443cf9ad465e5b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:21 +0100 Subject: [PATCH 723/835] Add LB docs --- doc/services/load-balancer/README.md.rst | 92 -- doc/services/load-balancer/USERGUIDE.md.rst | 840 ------------------ doc/services/load-balancer/access.rst | 79 ++ doc/services/load-balancer/caching.rst | 35 + doc/services/load-balancer/errors.rst | 44 + doc/services/load-balancer/index.rst | 93 ++ .../load-balancer/lb-setup.sample.rst | 9 + doc/services/load-balancer/load-balancer.rst | 165 ++++ doc/services/load-balancer/logging.rst | 33 + doc/services/load-balancer/metadata.rst | 46 + doc/services/load-balancer/monitors.rst | 43 + doc/services/load-balancer/nodes.rst | 124 +++ doc/services/load-balancer/sessions.rst | 53 ++ doc/services/load-balancer/ssl.rst | 52 ++ doc/services/load-balancer/stats.rst | 59 ++ doc/services/load-balancer/virtual-ips.rst | 74 ++ 16 files changed, 909 insertions(+), 932 deletions(-) delete mode 100644 doc/services/load-balancer/README.md.rst delete mode 100644 doc/services/load-balancer/USERGUIDE.md.rst create mode 100644 doc/services/load-balancer/access.rst create mode 100644 doc/services/load-balancer/caching.rst create mode 100644 doc/services/load-balancer/errors.rst create mode 100644 doc/services/load-balancer/lb-setup.sample.rst create mode 100644 doc/services/load-balancer/load-balancer.rst create mode 100644 doc/services/load-balancer/logging.rst create mode 100644 doc/services/load-balancer/metadata.rst create mode 100644 doc/services/load-balancer/monitors.rst create mode 100644 doc/services/load-balancer/nodes.rst create mode 100644 doc/services/load-balancer/sessions.rst create mode 100644 doc/services/load-balancer/ssl.rst create mode 100644 doc/services/load-balancer/stats.rst create mode 100644 doc/services/load-balancer/virtual-ips.rst diff --git a/doc/services/load-balancer/README.md.rst b/doc/services/load-balancer/README.md.rst deleted file mode 100644 index 862cf20d2..000000000 --- a/doc/services/load-balancer/README.md.rst +++ /dev/null @@ -1,92 +0,0 @@ -Load Balancers -============== - -A **load balancer** is a device that distributes incoming network -traffic amongst multiple back-end systems. These back-end systems are -called the **nodes** of the load balancer. - -Getting started ---------------- - -1. Instantiate a Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Retrieve the server instances you want to add as nodes of the load balancer. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); - - $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); - $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); - -The example above uses two server instances that have already been -created. It retrieves the server instances using their IDs. See also: -`creating server instances <>`__. - -3. Obtain a Load Balancer service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This object will be used to first define the load balancer nodes and -later create the load balancer itself. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -4. Define a load balancer node for each server. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $serverOneNode = $loadBalancer->node(); - $serverOneNode->address = $serverOne->addresses->private[0]->addr; - $serverOneNode->port = 8080; - $serverOneNode->condition = 'ENABLED'; - - $serverTwoNode = $loadBalancer->node(); - $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; - $serverTwoNode->port = 8080; - $serverTwoNode->condition = 'ENABLED'; - -In the example above, each node runs a service that listens on port -8080. Further, each node will start out as ``ENABLED``, which means it -will be ready to receive network traffic from the load balancer as soon -as it is created. - -5. Create the load balancer with the two nodes. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->addVirtualIp('PUBLIC'); - $loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) - )); - -In the example above, the load balancer will have a virtual IP address -accessible from the public Internet. Also notice that the port the load -balancer listens on (80) does not need to match the ports of its nodes -(8080). - -Next steps ----------- - -Once you have created a load balancer, there is a lot you can do with -it. See the `complete user guide for load balancers `__. diff --git a/doc/services/load-balancer/USERGUIDE.md.rst b/doc/services/load-balancer/USERGUIDE.md.rst deleted file mode 100644 index 788651f2b..000000000 --- a/doc/services/load-balancer/USERGUIDE.md.rst +++ /dev/null @@ -1,840 +0,0 @@ -The Complete User Guide to Load Balancers -========================================= - -Prerequisites -------------- - -Client -~~~~~~ - -To use the load balancers service, you must first instantiate a -``Rackspace`` client object. - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Load Balancer Service -~~~~~~~~~~~~~~~~~~~~~ - -All operations on load balancers are done via a load balancer service -object. - -.. code:: php - - $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -Cloud Servers -~~~~~~~~~~~~~ - -Many of the examples in this document use two cloud servers as nodes for -the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer -to these two cloud servers. - -Load Balancers --------------- - -A **load balancer** is a device that distributes incoming network -traffic amongst multiple back-end systems. These back-end systems are -called the **nodes** of the load balancer. - -Create Load Balancer -~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer(); - - $serverOneNode = $loadBalancer->node(); - $serverOneNode->address = $serverOne->addresses->private[0]->addr; - $serverOneNode->port = 8080; - $serverOneNode->condition = 'ENABLED'; - - $serverTwoNode = $loadBalancer->node(); - $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; - $serverTwoNode->port = 8080; - $serverTwoNode->condition = 'ENABLED'; - - $loadBalancer->addVirtualIp('PUBLIC'); - $loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) - )); - -List Load Balancer Details -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a single load balancer's details by using its ID. - -.. code:: php - - $loadBalancer = $loadBalancerService->loadBalancer('254889'); - - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - -List Load Balancers -~~~~~~~~~~~~~~~~~~~ - -You can retrieve a list of all your load balancers. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $loadBalancers = $loadBalancerService->loadBalancerList(); - foreach ($loadBalancers as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - } - -Update Load Balancer Attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can update one or more of the following load balancer attributes: - -- ``name``: The name of the load balancer -- ``algorithm``: The algorithm used by the load balancer to distribute - traffic amongst its nodes. See also: `Load balancing - algorithms <#algorithms>`__. -- ``protocol``: The network protocol used by traffic coming in to the - load balancer. See also: `Protocols <#protocols>`__. -- ``port``: The network port on which the load balancer listens for - incoming traffic. -- ``halfClosed``: Enable or Disable Half-Closed support for the load - balancer. -- ``timeout``: The timeout value for the load balancer to communicate - with its nodes. -- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for - the load balancer. When enabled, any HTTP request will return status - code 301 (Moved Permanently), and the requestor will be redirected to - the requested URL via the HTTPS protocol on port 443. For example, - http://example.com/page.html would be redirected to https:// - example.com/page.html. Only available for HTTPS protocol (``port`` = - 443), or HTTP Protocol with a properly configured SSL Termination - (\`secureTrafficOnly=true, securePort=443). See also: `SSL - Termination <#ssl-termination>`__. - -Updating a single attribute of a load balancer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->update(array( - 'name' => 'New name' - )); - -Updating multiple attributes of a load balancer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->update(array( - 'name' => 'New name', - 'algorithm' => 'ROUND_ROBIN' - )); - -Remove Load Balancer -~~~~~~~~~~~~~~~~~~~~ - -When you no longer have a need for the load balancer, you can remove it. - -.. code:: php - - $loadBalancer->delete(); - -Nodes ------ - -A **node** is a backend device that provides a service on specified IP -and port. An example of a load balancer node might be a web server -serving HTTP traffic on port 8080. - -A load balancer typically has multiple nodes attached to it so it can -distribute incoming network traffic amongst them. - -List Nodes -~~~~~~~~~~ - -You can list the nodes attached to a load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $nodes = $loadBalancer->nodeList(); - foreach ($nodes as $node) { - /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ - } - -Add Nodes -~~~~~~~~~ - -You can attach additional nodes to a load balancer. Assume -``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` -and ``$serverTwo`` - and you want to attach a third node to it, say -``$serverThree``, which provides a service on port 8080. - -**Important:** Remember to call ``$loadBalancer->addNodes()`` after all -the calls to ``$loadBalancer->addNode()`` as shown below. - -.. code:: php - - $address = $serverThree->addresses->private[0]->addr; - $loadBalancer->addNode($address, 8080); - $loadBalancer->addNodes(); - -The ``addNode`` method accepts three more optional parameters, in -addition to the two shown above: - -| Position \| Description \| Data type \| Required? \| Default value \| -| ----------- \| --------------- \| --------------\| -------------- \| ------------------ \| -|  1 \| IP address of node \| String \| Yes \| - \| -|  2 \| Port used by node's service \| Integer \| Yes \| - \| -|  3 \| Starting condition of node: -|  4 \| Type of node to add: -|  5 \| Weight, between 1 and 100, given to node when distributing -traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the -``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| -No \| 1 \| - -Modify Nodes -~~~~~~~~~~~~ - -You can modify one or more of the following node attributes: - -- ``condition``: The condition of the load balancer: - - - ``ENABLED`` – Node is ready to receive traffic from the load - balancer. - - ``DISABLED`` – Node should not receive traffic from the load - balancer. - - ``DRAINING`` – Node should process any traffic it is already - receiving but should not receive any further traffic from the load - balancer. - -- ``type``: The type of the node: - - - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation - to receive traffic from the load balancer. - - ``SECONDARY`` – Nodes defined as SECONDARY are only in the - rotation to receive traffic from the load balancer when all the - primary nodes fail. - -- ``weight``: The weight, between 1 and 100, given to node when - distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the - ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. - -Modifying a single attribute of a node -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\NodeCondition; - - $node->update(array( - 'condition' => NodeCondition::DISABLED - )); - -Modifying multiple attributes of a node -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\NodeCondition; - use OpenCloud\LoadBalancer\Enum\NodeType; - - $node->update(array( - 'condition' => NodeCondition::DISABLED, - 'type' => NodeType::SECONDARY - )); - -Remove Nodes -~~~~~~~~~~~~ - -There are two ways to remove a node. - -Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $node->delete(); - -Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: php - - $loadBalancer->removeNode(490639); - -The ``removeNode`` method, as shown above, accepts the following -arguments: - -+------------+---------------+-------------+-------------+-----------------+ -| Position | Description | Data type | Required? | Default value | -+============+===============+=============+=============+=================+ -| 1 | ID of node | Integer | Yes | - | -+------------+---------------+-------------+-------------+-----------------+ - -View Node Service Events -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can view events associated with the activity between a node and a -load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $nodeEvents = $loadBalancer->nodeEventList(); - foreach ($nodeEvents as $nodeEvent) { - /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ - } - -Virtual IPs ------------ - -A **virtual IP (VIP)** makes a load balancer accessible by clients. The -load balancing service supports either a public VIP address -(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP -address (``SERVICENET``), routable only within the region in which the -load balancer resides. - -List Virtual IPs -~~~~~~~~~~~~~~~~ - -You can list the VIPs associated with a load balancer. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $vips = $loadBalancer->virtualIpList(); - foreach ($vips as $vip) { - /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ - } - -Add Virtual IPv6 -~~~~~~~~~~~~~~~~ - -You can add additional IPv6 VIPs to a load balancer. - -.. code:: php - - use OpenCloud\LoadBalancer\Enum\IpType; - - $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); - -The ``addVirtualIp`` method, as shown above, accepts the following -arguments: - -| Position \| Description \| Data type \| Required? \| Default value \| -| ----------- \| --------------- \| -------------- \|-------------- \| ------------------ \| -|  1 \| Type of VIP: -|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| - -Remove Virtual IPs -~~~~~~~~~~~~~~~~~~ - -You can remove a VIP from a load balancer. - -.. code:: php - - $vip->remove(); - -Please note that a load balancer must have at least one VIP associated -with it. If you try to remove a load balancer's last VIP, a -``ClientErrorResponseException`` will be thrown. - -Algorithms ----------- - -Load balancers use an **algorithm** to determine how incoming traffic is -distributed amongst the back-end nodes. - -List Load Balancing Algorithms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all supported load balancing algorithms using a load -balancer service object. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $algorithms = $loadBalancerService->algorithmList(); - foreach ($algorithms as $algorithm) { - /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ - } - -Protocols ---------- - -When a load balancer is created a network protocol must be specified. -This network protocol should be based on the network protocol of the -back-end service being load balanced. - -List Load Balancing Protocols -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all supported network protocols using a load balancer -service object. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $protocols = $loadBalancerService->protocolList(); - foreach ($protocols as $protocol) { - /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ - } - -Session Persistence -------------------- - -**Session persistence** is a feature of the load balancing service that -forces multiple requests, of the same protocol, from clients to be -directed to the same node. This is common with many web applications -that do not inherently share application state between back-end servers. - -There are two types (or modes) of session persistence: - -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| Name | Description | -+===================+===================================================================================================================================================================================================================================+ -| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | -+-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - -List Session Persistence Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistenceType = $sessionPersistence->persistenceType; - - /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ - -In the example above: - -- If session persistence is enabled, the value of - ``$sessionPersistenceType`` is the type of session persistence: - either ``HTTP_COOKIE`` or ``SOURCE_IP``. -- If session persistence is disabled, the value of - ``$sessionPersistenceType`` is ``null``. - -Enable Session Persistence -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistence->update(array( - 'persistenceType' => 'HTTP_COOKIE' - )); - -Disable Session Persistence -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sessionPersistence = $loadBalancer->sessionPersistence(); - $sessionPersistence->delete(); - -Connection Logging ------------------- - -The **connection logging** feature allows logs to be delivered to a -Cloud Files account every hour. For HTTP-based protocol traffic, these -are Apache-style access logs. For all other traffic, this is connection -and transfer logging. - -Check Logging Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $connectionLogging bool **/ - - $connectionLogging = $loadBalancer->hasConnectionLogging(); - -In the example above: - -- If connection logging is enabled, the value of ``$connectionLogging`` - is ``true``. -- If connection logging is disabled, the value of - ``$connectionLogging`` is ``false``. - -Enable Connection Logging -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableConnectionLogging(true); - -Disable Connection Logging -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableConnectionLogging(false); - -Error Page ----------- - -An **error page** is the html file that is shown to the end user when an -error in the service has been thrown. By default every virtual server is -provided with the default error file. It is also possible to set a -custom error page for a load balancer. - -View Error Page Content -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPageContent = $errorPage->content; - - /** @var $errorPageContent string **/ - -In the example above the value of ``$errorPageContent`` is the HTML for -that page. This could either be the HTML of the default error page or of -your custom error page. - -Set Custom Error Page -~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPage->update(array( - 'content' => '' - )); - -Delete Custom Error Page -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $errorPage = $loadBalancer->errorPage(); - $errorPage->delete(); - -Allowed Domains ---------------- - -**Allowed domains** are a restricted set of domain names that are -allowed to add load balancer nodes. - -List Allowed Domains -~~~~~~~~~~~~~~~~~~~~ - -You can list all allowed domains using a load balancer service object. -An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is -returned. - -.. code:: php - - $allowedDomains = $loadBalancerService->allowedDomainList(); - foreach ($allowedDomains as $allowedDomain) { - /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ - } - -Access Lists ------------- - -**Access Lists** allow fine-grained network access to a load balancer's -VIP. Using access lists, network traffic to a load balancer's VIP can be -allowed or denied from a single IP address, multiple IP addresses or -entire network subnets. - -Note that ``ALLOW`` network items will take precedence over ``DENY`` -network items in an access list. - -To reject traffic from all network items except those with the ``ALLOW`` -type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. - -View Access List -~~~~~~~~~~~~~~~~ - -You can view a load balancer's access list. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $accessList = $loadBalancer->accessList(); - foreach ($accessList as $networkItem) { - /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ - } - -Add Network Items To Access List -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can add network items to a load balancer's access list very easily: - -.. code:: php - - $loadBalancer->createAccessList(array( - (object) array( - 'type' => 'ALLOW', - 'address' => '206.160.165.1/24' - ), - (object) array( - 'type' => 'DENY', - 'address' => '0.0.0.0/0' - ) - )); - -In the above example, we allowed access for 1 IP address, and used the -"0.0.0.0" wildcard to blacklist all other traffic. - -Remove Network Item From Access List -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You an remove a network item from a load balancer's access list. - -.. code:: php - - $networkItem->delete(); - -Content Caching ---------------- - -When **content caching** is enabled on a load balancer, -recently-accessed files are stored on the load balancer for easy -retrieval by web clients. Requests to the load balancer for these files -are serviced by the load balancer itself, which reduces load off its -back-end nodes and improves response times as well. - -Check Content Caching Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $contentCaching bool **/ - - $contentCaching = $loadBalancer->hasContentCaching(); - -In the example above: - -- If content caching is enabled, the value of ``$contentCaching`` is - ``true``. -- If content caching is disabled, the value of ``$contentCaching`` is - ``false``. - -Enable Content Caching -~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableContentCaching(true); - -Disable Content Caching -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $loadBalancer->enableContentCaching(false); - -SSL Termination ---------------- - -The SSL Termination feature allows a load balancer user to terminate SSL -traffic at the load balancer layer versus at the web server layer. A -user may choose to configure SSL Termination using a key and an SSL -certificate or an (Intermediate) SSL certificate. - -When SSL Termination is configured on a load balancer, a secure shadow -server is created that listens only for secure traffic on a -user-specified port. This shadow server is only visible to and -manageable by the system. Existing or updated attributes on a load -balancer with SSL Termination will also apply to its shadow server. For -example, if Connection Logging is enabled on an SSL load balancer, it -will also be enabled on the shadow server and Cloud Files logs will -contain log files for both. - -View current SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ - - $sslConfig = $loadBalancer->SSLTermination(); - -Update SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sslConfig->update(array( - 'enabled' => true, - 'securePort' => 443, - 'privateKey' => $key, - 'certificate' => $cert - )); - -For a full list, with explanations, of required and optional attributes, -please consult the `official -documentation `__ - -Delete SSL termination config -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $sslConfig->delete(); - -Metadata --------- - -Metadata can be associated with each load balancer and each node for the -client's personal use. It is defined using key-value pairs where the key -and value consist of alphanumeric characters. A key is unique per load -balancer. - -List metadata -~~~~~~~~~~~~~ - -.. code:: php - - $metadataList = $loadBalancer->metadataList(); - - foreach ($metadataList as $metadataItem) { - printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); - } - -Please consult the `iterator -documentation `__ for more information -about iterators. - -Add metadata -~~~~~~~~~~~~ - -.. code:: php - - $metadataItem = $loadBalancer->metadata(); - $metadataItem->create(array( - 'key' => 'foo', - 'value' => 'bar' - )); - -Modify metadata -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadataItem = $loadBalancer->metadata('foo'); - $metadataItem->update(array( - 'value' => 'baz' - )); - -Remove metadata -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadataItem->delete(); - -Monitors --------- - -The load balancing service includes a health monitoring operation which -periodically checks your back-end nodes to ensure they are responding -correctly. If a node is not responding, it is removed from rotation -until the health monitor determines that the node is functional. In -addition to being performed periodically, the health check also is -performed against every node that is added to ensure that the node is -operating properly before allowing it to service traffic. Only one -health monitor is allowed to be enabled on a load balancer at a time. - -.. code:: php - - /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ - - $healthMonitor = $loadBalancer->healthMonitor(); - - printf( - "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", - $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout - ); - -For a full list, with explanations, of required and optional attributes, -please consult the `official -documentation `__ - -Update or delete -~~~~~~~~~~~~~~~~ - -.. code:: php - - // Update - $healthMonitor->update(array( - 'delay' => 120, - 'timeout' => 60, - 'type' => 'CONNECT' - 'attemptsBeforeDeactivation' => 3 - )); - - // Delete - $healthMonitor->delete(); - -Statistics ----------- - -You can retrieve detailed stats about your load balancer, including the -following information: - -- ``connectTimeOut`` – Connections closed by this load balancer because - the 'connect\_timeout' interval was exceeded. -- ``connectError`` – Number of transaction or protocol errors in this - load balancer. -- ``connectFailure`` – Number of connection failures in this load - balancer. -- ``dataTimedOut`` – Connections closed by this load balancer because - the 'timeout' interval was exceeded. -- ``keepAliveTimedOut`` – Connections closed by this load balancer - because the 'keepalive\_timeout' interval was exceeded. -- ``maxConn`` – Maximum number of simultaneous TCP connections this - load balancer has processed at any one time. - -.. code:: php - - /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ - - $stats = $loadBalancer->stats(); - -Usage Reports -------------- - -The load balancer usage reports provide a view of all transfer activity, -average number of connections, and number of virtual IPs associated with -the load balancing service. Current usage represents all usage recorded -within the preceding 24 hours. Values for both incomingTransfer and -outgoingTransfer are expressed in bytes transferred. - -The optional startTime and endTime parameters can be used to filter all -usage. If the startTime parameter is supplied but the endTime parameter -is not, then all usage beginning with the startTime will be provided. -Likewise, if the endTime parameter is supplied but the startTime -parameter is not, then all usage will be returned up to the endTime -specified. - -.. code:: php - - # View billable LBs - $billable = $service->billableLoadBalancerList(); - - foreach ($billable as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - - # View usage - /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ - $usage = $loadBalancer->usage(); - - echo $usage->averageNumConnections, PHP_EOL; - } - diff --git a/doc/services/load-balancer/access.rst b/doc/services/load-balancer/access.rst new file mode 100644 index 000000000..4387dd8de --- /dev/null +++ b/doc/services/load-balancer/access.rst @@ -0,0 +1,79 @@ +Allowed Domains +=============== + +List Allowed Domains +-------------------- + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code-block:: php + + $allowedDomains = $service->allowedDomainList(); + + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + + +Access Lists +============ + +Access Lists allow fine-grained network access to a load balancer's VIP. Using +access lists, network traffic to a load balancer's VIP can be allowed or denied +from a single IP address, multiple IP addresses or entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` network +items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +.. include:: lb-setup.sample.rst + + +View Access List +---------------- + +You can view a load balancer's access list: + +.. code-block:: php + + $accessList = $loadBalancer->accessList(); + + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + + +Add Network Items To Access List +-------------------------------- + +You can add network items to a load balancer's access list very easily: + +.. code-block:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + + +Remove Network Item From Access List +------------------------------------ + +You an remove a network item from a load balancer's access list: + +.. code-block:: php + + $networkItem->delete(); diff --git a/doc/services/load-balancer/caching.rst b/doc/services/load-balancer/caching.rst new file mode 100644 index 000000000..6678e048a --- /dev/null +++ b/doc/services/load-balancer/caching.rst @@ -0,0 +1,35 @@ +Content Caching +=============== + +When content caching is enabled on a load balancer, recently-accessed files are +stored on the load balancer for easy retrieval by web clients. Requests to the +load balancer for these files are serviced by the load balancer itself, which +reduces load off its back-end nodes and improves response times as well. + + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $contentCaching = $loadBalancer->hasContentCaching(); + + +Enable Content Caching +---------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(true); + + +Disable Content Caching +----------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(false); diff --git a/doc/services/load-balancer/errors.rst b/doc/services/load-balancer/errors.rst new file mode 100644 index 000000000..041c19d45 --- /dev/null +++ b/doc/services/load-balancer/errors.rst @@ -0,0 +1,44 @@ +Error Pages +=========== + +.. include:: lb-setup.sample.rst + +An error page is the html file that is shown to the end user when an error in +the service has been thrown. By default every virtual server is provided with +the default error file. It is also possible to set a custom error page for a +load balancer. + + +View Error Page Content +----------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + + +Set Custom Error Page +--------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + + +Delete Custom Error Page +------------------------ + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index e69de29bb..bf2b6dd0c 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -0,0 +1,93 @@ +Load Balancer v1 +================ + +.. note:: + + This service is only available for Rackspace users. + + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Load Balancer service: + +.. code-block:: php + + $service = $client->loadBalancerService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + load-balancers + nodes + virtual-ips + access + caching + errors + logging + monitors + metadata + sessions + ssl + stats + + +Glossary +-------- + + allowed domain + Allowed domains are a restricted set of domain names that are allowed to add + load balancer nodes. + + content caching + When content caching is enabled on a load balancer, recently-accessed files + are stored on the load balancer for easy retrieval by web clients. Requests to + the load balancer for these files are serviced by the load balancer itself, + which reduces load off its back-end nodes and improves response times as well. + + health monitor + The load balancing service includes a health monitoring operation which + periodically checks your back-end nodes to ensure they are responding + correctly. If a node is not responding, it is removed from rotation until the + health monitor determines that the node is functional. In addition to being + performed periodically, the health check also is performed against every node + that is added to ensure that the node is operating properly before allowing it + to service traffic. Only one health monitor is allowed to be enabled on a load + balancer at a time. + + load balancer + A load balancer is a device that distributes incoming network + traffic amongst multiple back-end systems. These back-end systems are + called the nodes of the load balancer. + + metadata + Metadata can be associated with each load balancer and each node for the + client's personal use. It is defined using key-value pairs where the key + and value consist of alphanumeric characters. A key is unique per load + balancer. + + node + A node is a backend device that provides a service on specified IP and port. + An example of a load balancer node might be a web server serving HTTP + traffic on port 8080. A load balancer typically has multiple nodes attached + to it so it can distribute incoming network traffic amongst them. + + session persistence + Session persistence is a feature of the load balancing service that forces + multiple requests, of the same protocol, from clients to be directed to the + same node. This is common with many web applications that do not inherently + share application state between back-end servers. + + virtual IP + A virtual IP (VIP) makes a load balancer accessible by clients. The + load balancing service supports either a public VIP address + (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP + address (``SERVICENET``), routable only within the region in which the + load balancer resides. diff --git a/doc/services/load-balancer/lb-setup.sample.rst b/doc/services/load-balancer/lb-setup.sample.rst new file mode 100644 index 000000000..96e228b8d --- /dev/null +++ b/doc/services/load-balancer/lb-setup.sample.rst @@ -0,0 +1,9 @@ +Setup +----- + +In order to interact with this feature you must first retrieve a particular +load balancer, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer('{id}'); diff --git a/doc/services/load-balancer/load-balancer.rst b/doc/services/load-balancer/load-balancer.rst new file mode 100644 index 000000000..7071794df --- /dev/null +++ b/doc/services/load-balancer/load-balancer.rst @@ -0,0 +1,165 @@ +Load Balancer +============= + +.. note:: + + Many of the examples in this document use two cloud servers as nodes for + the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer + to these two cloud servers. + + +Create Load Balancer +-------------------- + +The first step is to instantiate an empty object, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer(); + +In essence, all a load balancer does is evenly distribute traffic between +various back-end nodes - which can be Compute or Database instances. So we will +need to add a few when creating our load balancer: + +.. code-block:: php + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + +All that remains is apply final configuration touches, such as name and the +port number, before submitting to the API: + +.. code-block:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode), + 'algorithm' => 'ROUND_ROBIN', + )); + +For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ +please see the sections below. + + +List Load Balancer Details +-------------------------- + +You can retrieve a single load balancer's details by using its ID: + +.. code-block:: php + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + $loadBalancer = $service->loadBalancer('{loadBalancerId}'); + + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers: + +.. code-block:: php + + $loadBalancers = $service->loadBalancerList(); + + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + + +Update a Load Balancer +---------------------- + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +.. code-block:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it: + +.. code-block:: php + + $loadBalancer->delete(); + + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. Common protocols are ``HTTP``, ``HTTPS`` +and ``MYSQL``. A full list is available `here `_. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols like so: + +.. code-block:: php + + $protocols = $service->protocolList(); + + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. A full list is available `here +`_. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can programmatically list all supported load balancing algorithms: + +.. code-block:: php + + $algorithms = $service->algorithmList(); + + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } diff --git a/doc/services/load-balancer/logging.rst b/doc/services/load-balancer/logging.rst new file mode 100644 index 000000000..29c37aee2 --- /dev/null +++ b/doc/services/load-balancer/logging.rst @@ -0,0 +1,33 @@ +Connection Logging +================== + +The connection logging feature allows logs to be delivered to a Cloud Files +account every hour. For HTTP-based protocol traffic, these are Apache-style +access logs. For all other traffic, this is connection and transfer logging. + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $connectionLogging = $loadBalancer->hasConnectionLogging(); + + +Enable Connection Logging +------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(true); + + +Disable Connection Logging +-------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(false); diff --git a/doc/services/load-balancer/metadata.rst b/doc/services/load-balancer/metadata.rst new file mode 100644 index 000000000..2b38de129 --- /dev/null +++ b/doc/services/load-balancer/metadata.rst @@ -0,0 +1,46 @@ +Metadata +======== + +.. include:: lb-setup.sample.rst + +List metadata +------------- + +.. code-block:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + + +Add metadata +------------ + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + + +Modify metadata +--------------- + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + + +Remove metadata +--------------- + +.. code-block:: php + + $metadataItem->delete(); diff --git a/doc/services/load-balancer/monitors.rst b/doc/services/load-balancer/monitors.rst new file mode 100644 index 000000000..9c463f270 --- /dev/null +++ b/doc/services/load-balancer/monitors.rst @@ -0,0 +1,43 @@ +Health Monitors +=============== + +.. include:: lb-setup.sample.rst + +Retrieve monitor details +------------------------ + +.. code-block:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Update monitor +-------------- + +.. code-block:: php + + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + +Delete monitor +-------------- + +.. code-block:: php + + $healthMonitor->delete(); diff --git a/doc/services/load-balancer/nodes.rst b/doc/services/load-balancer/nodes.rst new file mode 100644 index 000000000..dfaadc404 --- /dev/null +++ b/doc/services/load-balancer/nodes.rst @@ -0,0 +1,124 @@ +Nodes +===== + +.. include:: lb-setup.sample.rst + +List Nodes +---------- + +You can list the nodes attached to a load balancer: + +.. code-block:: php + + $nodes = $loadBalancer->nodeList(); + + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + + +Add Nodes +--------- + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code-block:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + + +The signature for ``addNodes`` is as follows: + +.. function:: addNodes($address, $port[, $condition = 'ENABLED'[, $type = null[, $weight = null]]]) + + Add a node to a load balancer + + :param string $address: the IP address of the node + :param integer $port: the port number of the node + :param string $condition: the initial condition of the code. Defaults to ``ENABLED`` + :param string $type: either ``PRIMARY`` or ``SECONDARY`` + :param integer $weight: the node weight (for round-robin algorithm) + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +Modify Nodes +------------ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + + +Remove Nodes +------------ + +There are two ways to remove a node. The first way is on an +``OpenCloud\LoadBalancer\Resource\Node`` instance, like so: + + +.. code-block:: php + + $node->delete(); + +The second is with an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` +instance and the node's ID, like so: + +.. code-block:: php + + $loadBalancer->removeNode('{nodeId}'); + +where '{nodeId}' is the integer ID of the node itself - this is a required value. + + +View Node Service Events +------------------------ + +You can view events associated with the activity between a node and a +load balancer: + +.. code-block:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } diff --git a/doc/services/load-balancer/sessions.rst b/doc/services/load-balancer/sessions.rst new file mode 100644 index 000000000..3d7f71cfe --- /dev/null +++ b/doc/services/load-balancer/sessions.rst @@ -0,0 +1,53 @@ +Session Persistence +=================== + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. include:: lb-setup.sample.rst + + +List Session Persistence Configuration +-------------------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + $sessionPersistenceType = $sessionPersistence->persistenceType; + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + + +Enable Session Persistence +-------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + + +Disable Session Persistence +--------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); diff --git a/doc/services/load-balancer/ssl.rst b/doc/services/load-balancer/ssl.rst new file mode 100644 index 000000000..12b28a6af --- /dev/null +++ b/doc/services/load-balancer/ssl.rst @@ -0,0 +1,52 @@ +SSL Termination +=============== + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +.. include:: lb-setup.sample.rst + + +View configuration +------------------ + +.. code-block:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + $sslConfig = $loadBalancer->SSLTermination(); + + +Update configuration +-------------------- + +.. code-block:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Delete configuration +-------------------- + +.. code-block:: php + + $sslConfig->delete(); diff --git a/doc/services/load-balancer/stats.rst b/doc/services/load-balancer/stats.rst new file mode 100644 index 000000000..65077643f --- /dev/null +++ b/doc/services/load-balancer/stats.rst @@ -0,0 +1,59 @@ +Statistics and Usage Reports +============================ + +.. include:: lb-setup.sample.rst + +Retrieve LB stats +----------------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code-block:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + $stats = $loadBalancer->stats(); + + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code-block:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } diff --git a/doc/services/load-balancer/virtual-ips.rst b/doc/services/load-balancer/virtual-ips.rst new file mode 100644 index 000000000..fa0add613 --- /dev/null +++ b/doc/services/load-balancer/virtual-ips.rst @@ -0,0 +1,74 @@ +Virtual IPs +=========== + +.. include:: lb-setup.sample.rst + + +List Virtual IPs +---------------- + +You can list the VIPs associated with a load balancer like so: + +.. code-block:: php + + $vips = $loadBalancer->virtualIpList(); + + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + + +Get existing VIP +---------------- + +To retrieve the details of an existing VIP on a load balancer, you will need +its ID: + +.. code-block:: + + $vip = $loadBalancer->virtualIp('{virtualIpId}'); + + +Add Virtual IPv6 +---------------- + +You can add additional IPv6 VIPs to a load balancer using the following method: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +the first argument is the type of network your IP address will server traffic in +- and can either be ``PUBLIC`` or ``PRIVATE``. The second argument is the version +of IP address, either ``4`` or ``6``. + + +Add Virtual IPv4 +---------------- + +Similar to above: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 4); + + +Remove Virtual IP +----------------- + +You can remove a VIP from a load balancer. + +.. code-block:: php + + $vip->remove(); + + +.. note:: + + A load balancer must have at least one VIP associated with it. If you try to + remove a load balancer's last VIP, a ``ClientErrorResponseException`` will be + thrown. From 19d081e22e35fe48ba4bd3543196dba4b9132a78 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:32 +0100 Subject: [PATCH 724/835] Add monitoring docs --- doc/services/monitoring/Agents.md.rst | 202 -------------- doc/services/monitoring/Entities.md.rst | 77 ------ doc/services/monitoring/Service.md.rst | 23 -- doc/services/monitoring/agents.rst | 188 +++++++++++++ .../monitoring/{Alarms.md.rst => alarms.rst} | 84 +++--- .../{Changelogs.md.rst => changelogs.rst} | 17 +- .../monitoring/{Checks.md.rst => checks.rst} | 189 +++++++------ doc/services/monitoring/entities.rst | 77 ++++++ doc/services/monitoring/index.rst | 34 +++ .../{Metrics.md.rst => metrics.rst} | 54 ++-- ...Notifications.md.rst => notifications.rst} | 252 ++++++++++-------- .../monitoring/{Views.md.rst => views.rst} | 20 +- .../monitoring/{Zones.md.rst => zones.rst} | 42 ++- 13 files changed, 661 insertions(+), 598 deletions(-) delete mode 100644 doc/services/monitoring/Agents.md.rst delete mode 100644 doc/services/monitoring/Entities.md.rst delete mode 100644 doc/services/monitoring/Service.md.rst create mode 100644 doc/services/monitoring/agents.rst rename doc/services/monitoring/{Alarms.md.rst => alarms.rst} (66%) rename doc/services/monitoring/{Changelogs.md.rst => changelogs.rst} (52%) rename doc/services/monitoring/{Checks.md.rst => checks.rst} (80%) create mode 100644 doc/services/monitoring/entities.rst rename doc/services/monitoring/{Metrics.md.rst => metrics.rst} (83%) rename doc/services/monitoring/{Notifications.md.rst => notifications.rst} (64%) rename doc/services/monitoring/{Views.md.rst => views.rst} (58%) rename doc/services/monitoring/{Zones.md.rst => zones.rst} (71%) diff --git a/doc/services/monitoring/Agents.md.rst b/doc/services/monitoring/Agents.md.rst deleted file mode 100644 index 943983f7e..000000000 --- a/doc/services/monitoring/Agents.md.rst +++ /dev/null @@ -1,202 +0,0 @@ -Agents -====== - -Intro ------ - -The Monitoring Agent resides on the host server being monitored. The -agent allows you to gather on-host metrics based on agent checks and -push them to Cloud Monitoring where you can analyze them, use them with -the Cloud Monitoring infrastructure (such as alarms), and archive them. - -For more information about this feature, including a brief overview of -its core design principles and security layers, see the `official API -documentation `__. - -Setup ------ - -.. code:: php - - $agentId = '00-agent.example.com'; - $agent = $service->getAgent($agentId); - -You can view the `service page `__ for more information -about setting up the Cloud Monitoring service. - -List agents ------------ - -.. code:: php - - $agents = $service->getAgents(); - - foreach ($agents as $agent) { - echo $agent->getLastConnected(); - } - -Please consult the `iterator doc `__ for -more information about iterators. - -List connections ----------------- - -.. code:: php - - $connections = $agent->getConnections(); - -Please consult the `iterator doc `__ for -more information about iterators. - -Get connection --------------- - -.. code:: php - - /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ - $connection = $agent->getConnection('cntl4qsIbA'); - -Agent Connection properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -+--------------------+---------------+--------+---------------------------+ -| Name | Description | Type | Method | -+====================+===============+========+===========================+ -| id | - | - | ``getId()`` | -+--------------------+---------------+--------+---------------------------+ -| guid | - | - | ``getGuid()`` | -+--------------------+---------------+--------+---------------------------+ -| agent\_id | - | - | ``getAgentId()`` | -+--------------------+---------------+--------+---------------------------+ -| endpoint | - | - | ``getEndpoint()`` | -+--------------------+---------------+--------+---------------------------+ -| process\_version | - | - | ``getProcessVersion()`` | -+--------------------+---------------+--------+---------------------------+ -| bundle\_version | - | - | ``getBundleVersion()`` | -+--------------------+---------------+--------+---------------------------+ -| agent\_ip | - | - | ``getAgentIp()`` | -+--------------------+---------------+--------+---------------------------+ - -Agent tokens -============ - -Intro ------ - -Agent tokens are used to authenticate Monitoring agents to the -Monitoring Service. Multiple agents can share a single token. - -Setup ------ - -.. code:: php - - $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; - $agentToken = $service->getAgentToken($tokenId); - -Create agent token ------------------- - -.. code:: php - - $newToken = $service->getAgentToken(); - $newToken->create(array('label' => 'Foobar')); - -List agent tokens ------------------ - -.. code:: php - - $agentTokens = $service->getAgentTokens(); - - foreach ($agentTokens as $token) { - echo $token->getLabel(); - } - -Please consult the `iterator doc `__ for -more information about iterators. - -Update and delete Agent Token ------------------------------ - -.. code:: php - - // Update - $token->update(array( - 'label' => 'New label' - )); - - // Delete - $token->delete(); - -Agent Host Information -====================== - -Info ----- - -An agent can gather host information, such as process lists, network -configuration, and memory usage, on demand. You can use the -host-information API requests to gather this information for use in -dashboards or other utilities. - -Setup ------ - -.. code:: php - - $host = $monitoringService->getAgentHost(); - -Get some metrics ----------------- - -.. code:: php - - $cpuInfo = $host->info('cpus'); - $diskInfo = $host->info('disks'); - $filesystemInfo = $host->info('filesystems'); - $memoryInfo = $host->info('memory'); - $networkIntInfo = $host->info('network_interfaces'); - $processesInfo = $host->info('processes'); - $systemInfo = $host->info('system'); - $userInfo = $host->info('who'); - - // What CPU models do we have? - foreach ($cpuInfo as $cpuMetric) { - echo $cpuMetric->model, PHP_EOL; - } - - // How many disks do we have? - echo $diskInfo->count(); - - // What's the available space on our ext4 filesystem? - foreach ($filesystemInfo as $filesystemMetric) { - if ($filesystemMetric->sys_type_name == 'ext4') { - echo $filesystemMetric->avail; - } - } - -Agent targets -============= - -Info ----- - -Each agent check type gathers data for a related set of target devices -on the server where the agent is installed. For example, -``agent.network`` gathers data for network devices. The actual list of -target devices is specific to the configuration of the host server. By -focusing on specific targets, you can efficiently narrow the metric data -that the agent gathers. - -List agent targets -~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $targets = $service->getAgentTargets(); - - foreach ($targets as $target) { - echo $target->getType(); - } - diff --git a/doc/services/monitoring/Entities.md.rst b/doc/services/monitoring/Entities.md.rst deleted file mode 100644 index 068aec122..000000000 --- a/doc/services/monitoring/Entities.md.rst +++ /dev/null @@ -1,77 +0,0 @@ - Entities -========= - -Info ----- - -An entity is the target of what you are monitoring. For example, you can -create an entity to monitor your website, a particular web service, or -your Rackspace server. Note that an entity represents only one item in -the monitoring system -- if you wanted to monitor each server in a -cluster, you would create an entity for each of the servers. You would -not create a single entity to represent the entire cluster. - -An entity can have multiple checks associated with it. This allows you -to check multiple services on the same host by creating multiple checks -on the same entity, instead of multiple entities each with a single -check. - -Setup ------ - -.. code:: php - - $entity = $service->getEntity(); - -For more information about setting up the ``$service`` object, please -see the userguide tutorial for `services `__. - -Attributes ----------- - -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| Name | Description | Required? | Data type | Method | -+=================+=========================================================================+=============+=====================================================+========================+ -| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ -| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | -+-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ - -Create Entity -------------- - -.. code:: php - - $service->createEntity(array( - 'label' => 'Brand New Entity', - 'ip_addresses' => array( - 'default' => '127.0.0.4', - 'b' => '127.0.0.5', - 'c' => '127.0.0.6', - 'test' => '127.0.0.7' - ), - 'metadata' => array( - 'all' => 'kinds', - 'of' => 'stuff', - 'can' => 'go', - 'here' => 'null is not a valid value' - ) - )); - -Update and delete Entity ------------------------- - -.. code:: php - - // Update - $entity->update(array( - 'label' => 'New label for my entity' - )); - - // Delete - $entity->delete(); - diff --git a/doc/services/monitoring/Service.md.rst b/doc/services/monitoring/Service.md.rst deleted file mode 100644 index 09c25c21d..000000000 --- a/doc/services/monitoring/Service.md.rst +++ /dev/null @@ -1,23 +0,0 @@ -Cloud Monitoring Service -======================== - -Initializing the Cloud Monitoring is easy - and can be done in a similar -way to all other Rackspace services: - -1. Create client and pass in auth details. For more information about - creating clients, please consult the `Client - documentation <../Clients.md>`__. -2. Use the factory method, specifying additional parameters where - necessary: - -.. code:: php - - $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); - -All three parameters are optional - if not specified, it will revert to -the service's default values which are: - -- Name = ``cloudMonitoring`` -- Region = ``DFW`` -- URL type = ``publicURL`` - diff --git a/doc/services/monitoring/agents.rst b/doc/services/monitoring/agents.rst new file mode 100644 index 000000000..f4c673608 --- /dev/null +++ b/doc/services/monitoring/agents.rst @@ -0,0 +1,188 @@ +Agents +====== + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Retrieve details about an agent +------------------------------- + +.. code-block:: php + + $agent = $service->getAgent('{agentId}'); + + +List agents +----------- + +.. code-block:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + + +List connections +---------------- + +.. code-block:: php + + $connections = $agent->getConnections(); + + +Get connection +-------------- + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('{connectionId}'); + + +Once you have access to an agent's ``OpenCloud\CloudMonitoring\Resource\AgentConnection`` +object, these are the attributes you can access: + ++--------------------+---------------------------+ +| Name | Method | ++====================+===========================+ +| id | ``getId()`` | ++--------------------+---------------------------+ +| guid | ``getGuid()`` | ++--------------------+---------------------------+ +| agent_id | ``getAgentId()`` | ++--------------------+---------------------------+ +| endpoint | ``getEndpoint()`` | ++--------------------+---------------------------+ +| process_version | ``getProcessVersion()`` | ++--------------------+---------------------------+ +| bundle_version | ``getBundleVersion()`` | ++--------------------+---------------------------+ +| agent_ip | ``getAgentIp()`` | ++--------------------+---------------------------+ + +Agent tokens +============ + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Retrieve an agent token +----------------------- + +.. code-block:: php + + $agentToken = $service->getAgentToken('{tokenId}'); + + +Create agent token +------------------ + +.. code-block:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + + +List agent tokens +----------------- + +.. code-block:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + + +Update agent token +------------------ + +.. code-block:: php + + $token->update(array( + 'label' => 'New label' + )); + + +Update agent token +------------------ + +.. code-block:: php + + $token->delete(); + + +Agent Host Information +====================== + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code-block:: php + + $host = $service->getAgentHost(); + + +Get some metrics +---------------- + +.. code-block:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +------------------ + +.. code-block:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } diff --git a/doc/services/monitoring/Alarms.md.rst b/doc/services/monitoring/alarms.rst similarity index 66% rename from doc/services/monitoring/Alarms.md.rst rename to doc/services/monitoring/alarms.rst index 2918637e9..297a57407 100644 --- a/doc/services/monitoring/Alarms.md.rst +++ b/doc/services/monitoring/alarms.rst @@ -1,9 +1,6 @@ Alarms ====== -Info ----- - Alarms bind alerting rules, entities, and notification plans into a logical unit. Alarms are responsible for determining a state (``OK``, ``WARNING`` or ``CRITICAL``) based on the result of a Check, and @@ -15,25 +12,39 @@ documentation getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + +Retrieve alarm +-------------- -.. code:: php +.. code-block:: php - $alarmId = 'alAAAA'; - $alarm = $check->getAlarm(); + $alarm = $check->getAlarm('{alarmId}'); -For more information about working with Checks, please see the -`appropriate documentation `__. -Attributes ----------- +Once you have access to a ``OpenCloud\Monitoring\Resource\Alarm`` object, these +are the attributes you can access: +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ | Name | Description | Required? | Method | +==========================+=============================================================================+=============+=================================+ -| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | +| check_id | The ID of the check to alert on. | Required | ``getCheckId()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ -| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | +| notification_plan_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ | criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ @@ -44,38 +55,45 @@ Attributes | metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | +--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + Create Alarm ------------ -.. code:: php +.. code-block:: php + + $alarm = $check->getAlarm(); + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); - $alarm->create(array( - 'check_id' => 'chAAAA', - 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', - 'notification_plan_id' => 'npAAAAA' - )); List Alarms ----------- -.. code:: php +.. code-block:: php - $alarms = $entity->getAlarms(); + $alarms = $entity->getAlarms(); - foreach ($alarms as $alarm) { - echo $alarm->getId(); - } + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } -Update and delete Alarm ------------------------ -.. code:: php +Update Alarm +------------ + +.. code-block:: php - // Update - $alarm->update(array( - 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' - )); + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + +Delete alarm +------------ - // Delete - $alarm->delete(); +.. code-block:: php + $alarm->delete(); diff --git a/doc/services/monitoring/Changelogs.md.rst b/doc/services/monitoring/changelogs.rst similarity index 52% rename from doc/services/monitoring/Changelogs.md.rst rename to doc/services/monitoring/changelogs.rst index 2e9059e26..a6b26af25 100644 --- a/doc/services/monitoring/Changelogs.md.rst +++ b/doc/services/monitoring/changelogs.rst @@ -1,21 +1,18 @@ Changelogs ========== -Info ----- - The monitoring service records changelogs for alarm statuses. Changelogs are accessible as a Time Series Collection. By default the API queries the last 7 days of changelog information. -Working with Changelogs ------------------------ -.. code:: php +View Changelog +-------------- - $changelog = $service->getChangelog(); +.. code-block:: php - foreach ($changelog as $item) { - $entity = $item->getEntityId(); - } + $changelog = $service->getChangelog(); + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } diff --git a/doc/services/monitoring/Checks.md.rst b/doc/services/monitoring/checks.rst similarity index 80% rename from doc/services/monitoring/Checks.md.rst rename to doc/services/monitoring/checks.rst index 0e8e902a5..73474c170 100644 --- a/doc/services/monitoring/Checks.md.rst +++ b/doc/services/monitoring/checks.rst @@ -1,8 +1,6 @@ Checks ====== -Info ----- A check is one of the foundational building blocks of the monitoring system. The check determines the parts or pieces of the entity that you @@ -23,18 +21,31 @@ servers stops responding) or multiple alarms off of a single check. (e.g. ensure both that a HTTPS server is responding and that it has a valid certificate). -Setup ------ +Create a check +-------------- + +There are various attributes available to you when creating a new monitoring +check: -Checks are sub-resources of Entities: +.. code-block:: php -.. code:: php + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); - $checkId = 'chAAAA'; - $check = $entity->getCheck($checkId); +For a full list of available attributes, consult the list below. Attributes ----------- +~~~~~~~~~~ +------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ | Name | Description | Required? | Data type | @@ -54,92 +65,90 @@ Attributes | timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | +------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ -Attributes used for remote Checks -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Optional attributes to be used with remote checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ | Name | Description | Required? | Data type | +===========================+========================================================================================================================================================+=============+============================================================+ -| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | +| monitoring_zones_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | +| target_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | +| target_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | +| target_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | +---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ -Test parameters (before create) -------------------------------- - -.. code:: php - - $params = array( - 'type' => 'remote.http', - 'details' => array( - 'url' => 'http://example.com', - 'method' => 'GET' - ), - 'monitoring_zones_poll' => array('mzlon'), - 'period' => '100', - 'timeout' => '30', - 'target_alias' => 'default', - 'label' => 'Website check 1' - ); - - // You can do a test to see what would happen - // if a Check is launched with these params - $response = $entity->testNewCheckParams($params); - - echo $response->timestamp; // When was it executed? - echo $response->available; // Was it available? - echo $response->status; // Status code - -Create a Check --------------- -.. code:: php +Test parameters +--------------- + +Sometimes it can be useful to test out the parameters before sending them as a +create call. To do this, pass in the ``$params`` like so: + +.. code-block:: php + + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + + +Send parameters +~~~~~~~~~~~~~~~ + +Once you are satisfied with your configuration parameters, you can complete the +operation and send it to the API like so: + +.. code-block:: php + + $entity->createCheck($params); - $entity->createCheck($params); Test existing Check ------------------- -.. code:: php +.. code-block:: php - // Set arg to TRUE for debug information - $response = $check->test(true); + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; - echo $response->debug_info; List Checks ----------- -.. code:: php +.. code-block:: php + + $checks = $entity->getChecks(); - $checks = $entity->getChecks(); + foreach ($checks as $check) { + echo $check->getId(); + } - foreach ($checks as $check) { - echo $check->getId(); - } -Update and delete Check -~~~~~~~~~~~~~~~~~~~~~~~ +Update Check +------------ -.. code:: php +.. code-block:: php - // Update - $check->update(array('period' => 500)); + $check->update(array('period' => 500)); + + +Delete check +------------ + +.. code-block:: php + + $check->delete(); - // Delete - $check->delete(); Check types =========== -Info ----- - Each check within the Rackspace Cloud Monitoring has a designated check type. The check type instructs the monitoring system how to check the monitored resource. **Note:** Users cannot create, update or delete @@ -168,26 +177,46 @@ using the 'extract' attribute on the remote.http check, however the default metrics will always be present. To determine the exact metrics available, the Test Check API is provided. -Setup ------ +Find an existing check's type +----------------------------- + +If you want to see the type for an existing Check resource: + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + + +List all possible check types +----------------------------- + +.. code-block:: php + + $checkTypes = $service->getCheckTypes(); -If you want to see the type for an existing Check: + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } -.. code:: php - /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ - $checkType = $check->getCheckType(); +Retrieve details about a Type by its ID +--------------------------------------- Alternatively, you can retrieve a specific type based on its ID: -.. code:: php +.. code-block:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); - $checkTypeId = 'remote.dns'; - $checkType = $service->getCheckType($checkTypeId); Attributes ---------- +Once you have access to a ``OpenCloud\CloudMonitoring\Resource\CheckType`` object, +you can query these attributes: + +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ | Name | Description | Data type | Method | +========================+==========================================================================================================================================================================================+=============+===============================+ @@ -195,17 +224,5 @@ Attributes +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ | fields | Check type fields. | Array | ``getFields()`` | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ -| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | +| supported_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | +------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ - -List all possible check types ------------------------------ - -.. code:: php - - $checkTypes = $service->getCheckTypes(); - - foreach ($checkTypes as $checkType) { - echo $checkType->getId(); - } - diff --git a/doc/services/monitoring/entities.rst b/doc/services/monitoring/entities.rst new file mode 100644 index 000000000..7e4132af1 --- /dev/null +++ b/doc/services/monitoring/entities.rst @@ -0,0 +1,77 @@ + Entities +========= + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Create Entity +------------- + +.. code-block:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + + +Retrive an entity +----------------- + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + + +Attributes +~~~~~~~~~~ + ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+=========================================================================+=====================================================+========================+ +| label | Defines a name for the entity. | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| agent_id | Agent to which this entity is bound to. | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| ip_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ + + +Update an entity +---------------- + +.. code-block:: php + + $entity->update(array( + 'label' => 'New label for my entity' + )); + + +Delete entity +------------- + +.. code-block:: php + + $entity->delete(); diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index e69de29bb..df0597e8c 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -0,0 +1,34 @@ +Monitoring v1 +============= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Cloud Monitoring service: + +.. code-block:: php + + $service = $client->monitoringService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + +Operations +---------- + +.. toctree:: + + entities + checks + alarms + agents + changelogs + metrics + notifications + views + zones + + +Glossary +-------- diff --git a/doc/services/monitoring/Metrics.md.rst b/doc/services/monitoring/metrics.rst similarity index 83% rename from doc/services/monitoring/Metrics.md.rst rename to doc/services/monitoring/metrics.rst index 4231cf914..0b412a6a3 100644 --- a/doc/services/monitoring/Metrics.md.rst +++ b/doc/services/monitoring/metrics.rst @@ -1,9 +1,6 @@  Metrics ======== -Info ----- - When Monitoring checks run, they generate metrics. These metrics are stored as full resolution data points in the Cloud Monitoring system. Full resolution data points are periodically rolled up (condensed) into @@ -13,6 +10,7 @@ Depending on your needs, you can use the metrics API to fetch individual data points (fine-grained) or rolled up data points (coarse-grained) over a period of time. + Data Granularity ---------------- @@ -42,7 +40,7 @@ elapsed since January 1, 1970** ). The following table shows the number of points that the API returns for a given resolution. Specifying resolution to retrieve data in 48 hour period -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-----------------------------+-------------------------+ | You specify resolution... | API returns points... | @@ -61,7 +59,7 @@ Specifying resolution to retrieve data in 48 hour period +-----------------------------+-------------------------+ Specifying number of points to retrieve data in 48 hour period -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------------------+-----------------------------+ | You specify points in the range... | API calculates resolution | @@ -80,7 +78,7 @@ Specifying number of points to retrieve data in 48 hour period +--------------------------------------+-----------------------------+ Data Point Expiration -^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~ Cloud Monitoring expires data points according to the following schedule: @@ -101,31 +99,45 @@ schedule: | MIN1440 | 365 days | +--------------+--------------+ - Setup +Setup ------ -Metrics are sub-resources of Checks. For more information about working -with Checks, please see the `relevant documentation `__. +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + List all metrics ---------------- -.. code:: php +.. code-block:: php - $metrics = $check->getMetrics(); + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } - foreach ($metrics as $metric) { - echo $metric->getName(); - } Fetch data points ----------------- -.. code:: php - - $data = $check->fetchDataPoints('mzdfw.available', array( - 'resolution' => 'FULL', - 'from' => 1369756378450, - 'to' => 1369760279018 - )); +.. code-block:: php + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); diff --git a/doc/services/monitoring/Notifications.md.rst b/doc/services/monitoring/notifications.rst similarity index 64% rename from doc/services/monitoring/Notifications.md.rst rename to doc/services/monitoring/notifications.rst index f361e3c9c..d7e289ad5 100644 --- a/doc/services/monitoring/Notifications.md.rst +++ b/doc/services/monitoring/notifications.rst @@ -1,9 +1,6 @@ Notifications ============= -Info ----- - A notification is a destination to send an alarm; it can be a variety of different types, and will evolve over time. @@ -11,16 +8,15 @@ For instance, with a webhook type notification, Cloud Monitoring posts JSON formatted data to a user-specified URL on an alert condition (Check goes from ``OK`` -> ``CRITICAL`` and so on). -Setup ------ +Get notification +---------------- -.. code:: php +.. code-block:: php - $id = 'ntAAAA'; - $notification = $service->getNotification($id); + $notification = $service->getNotification('{id}'); -Attributes ----------- +Once you have access to a ``OpenCloud\Monitoring\Resource\Notification`` object, +these are the attributes available for use: +-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ | Name | Description | Data type | Method | @@ -32,76 +28,96 @@ Attributes | type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | +-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +Creating notifications +---------------------- + +The first thing to do when creating a new notification is configure the +parameters which will define the behaviour of your resource: + +.. code-block:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + Test parameters ---------------- +~~~~~~~~~~~~~~~ + +Once this is done, it is often useful to test them out to check whether they +will result in a successful creation: -.. code:: php +.. code-block:: php - $params = array( - 'label' => 'My webhook #1', - 'type' => 'webhook', - 'details' => array( - 'url' => 'http://example.com' - ) - ); + // Test it + $response = $notification->testParams($params); - // Test it - $response = $notification->testParams($params); + if ($response->status == 'Success') { + echo $response->message; + } - if ($response->status == 'Success') { - echo $response->message; - } -Create Notification -------------------- +Send parameters +~~~~~~~~~~~~~~~ -.. code:: php +When you're happy with the parameters you've defined, you can complete the +operation by sending them to the API like so: + +.. code-block:: php + + $notification->create($params); - $notification->create($params); Test existing notification -------------------------- -.. code:: php +.. code-block:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; - $response = $notification->testExisting(true); - echo $response->debug_info; List Notifications ------------------ -.. code:: php +.. code-block:: php - $notifications = $service->getNotifications(); + $notifications = $service->getNotifications(); - foreach ($notifications as $notification) { - echo $notification->getId(); - } + foreach ($notifications as $notification) { + echo $notification->getId(); + } -Update and delete Notifications -------------------------------- -.. code:: php +Update a Notification +--------------------- - // Update - $notification->update(array( - 'label' => 'New notification label' - )); +.. code-block:: php + + $notification->update(array( + 'label' => 'New notification label' + )); + + +Delete a Notification +--------------------- + +.. code-block:: php + + $notification->delete(); - // Delete - $notification->delete(); Notification types ================== -Info ----- - -Pretty self-explanatory. Rackspace Cloud Monitoring currently supports -the following notification types: +Rackspace Cloud Monitoring currently supports the following notification types: Webhook -^^^^^^^ +~~~~~~~ Industry-standard web hooks, where JSON is posted to a configurable URL. It has these attributes: @@ -113,7 +129,7 @@ It has these attributes: +-----------+------------------------------------------+---------------+ Email -^^^^^ +~~~~~ Email alerts where the message is delivered to a specified address. It has these attributes: @@ -124,6 +140,7 @@ has these attributes: | url | An HTTP or HTTPS URL to POST to | Valid URL | +--------+-----------------------------------+-------------+ + Setup ----- @@ -131,34 +148,33 @@ If you've already set up a main Notification object, and want to access functionality for this Notification's particular Notification Type, you can access its property: -.. code:: php +.. code-block:: php - $type = $notification->getNotificationType(); + $type = $notification->getNotificationType(); Alternatively, you can retrieve an independent resource using the ID: -.. code:: php +.. code-block:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); - $typeId = 'pagerduty'; - $type = $service->getNotificationType($typeId); List all possible notification types ------------------------------------ -.. code:: php +.. code-block:: php + + $types = $service->getNotificationTypes(); - $types = $service->getNotificationTypes(); + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } - foreach ($types as $type) { - echo sprintf('%s %s', $type->getName(), $type->getDescription()); - } Notification plans ================== -Info ----- - A notification plan contains a set of notification actions that Rackspace Cloud Monitoring executes when triggered by an alarm. Rackspace Cloud Monitoring currently supports webhook and email @@ -180,60 +196,62 @@ A notification plan, ``npTechnicalContactsEmail``, is provided by default which will email all of the technical contacts on file for an account whenever there is a state change. -Setup ------ +Get a notification plan +----------------------- -.. code:: php +.. code-block:: php - $planId = 'npAAAA'; - $plan = $service->getNotificationPlan(); + $plan = $service->getNotificationPlan('{planId}'); -Attributes ----------- +Once you have access to a ``OpenCloud\\Monitoring\\Resource\\NotificationPlan`` +object, you can access these resources: +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ | Name | Description | Required? | Data type | Method | +===================+====================================================================+=============+=========================+==========================+ | label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | +| critical_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | +| ok_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ -| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | +| warning_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | +-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ Create Notification Plan ------------------------ -.. code:: php +.. code-block:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + + +Update notification plan +------------------------ + +.. code-block:: php - $plan->create(array( - 'label' => 'New Notification Plan', - 'critical_state' => array('ntAAAA'), - 'ok_state' => array('ntBBBB'), - 'warning_state' => array('ntCCCC') - )); + $plan->update(array( + 'label' => 'New label for my plan' + )); -Update and delete Notification Plan ------------------------------------ -.. code:: php +Delete notification plan +------------------------ + +.. code-block:: php - // Update - $plan->update(array( - 'label' => 'New label for my plan' - )); + $plan->delete(); - // Delete - $plan->delete(); Alarm Notification History ========================== -Info ----- - The monitoring service keeps a record of notifications sent for each alarm. This history is further subdivided by the check on which the notification occurred. Every attempt to send a notification is recorded, @@ -245,12 +263,31 @@ Alarm notification history is accessible as a Time Series Collection. By default alarm notification history is stored for 30 days and the API queries the last 7 days of information. - Setup +Setup ------ -Notification History is a sub-resource of an Alarm. For more information -about working with Alarms, please consult the relevant -`documentation `__. +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +and finally, retrieve the alarm: + +.. code-block:: php + + $alarm = $check->getAlarm('{alarmId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + Discover which Checks have a Notification History ------------------------------------------------- @@ -258,24 +295,25 @@ Discover which Checks have a Notification History This operation list checks for which alarm notification history is available: -.. code:: php +.. code-block:: php + + $checks = $alarm->getRecordedChecks(); - $checks = $alarm->getRecordedChecks(); List Alarm Notification History for a particular Check ------------------------------------------------------ -.. code:: php +.. code-block:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); - $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); Get a particular Notification History item ------------------------------------------ -.. code:: php - - $checkId = 'chAAAA'; - $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; +.. code-block:: php - $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); diff --git a/doc/services/monitoring/Views.md.rst b/doc/services/monitoring/views.rst similarity index 58% rename from doc/services/monitoring/Views.md.rst rename to doc/services/monitoring/views.rst index a5b7245fa..e83b97154 100644 --- a/doc/services/monitoring/Views.md.rst +++ b/doc/services/monitoring/views.rst @@ -1,27 +1,21 @@ Views ===== -Info ----- - Views contain a combination of data that usually includes multiple, different objects. The primary purpose of a view is to save API calls and make data retrieval more efficient. Instead of doing multiple API calls and then combining the result yourself, you can perform a single API call against the view endpoint. + List all Views -------------- -\`\`\`php $views = $service->getViews(); - -foreach ($views as $view) { $entity = $view->getEntity(); - -:: - - echo $view->getTimestamp(); +.. code-block:: php -} \`\`\` + $views = $service->getViews(); -Please consult the `iterator doc `__ for -more information about iterators. + foreach ($views as $view) { + $entity = $view->getEntity(); + echo $view->getTimestamp(); + } diff --git a/doc/services/monitoring/Zones.md.rst b/doc/services/monitoring/zones.rst similarity index 71% rename from doc/services/monitoring/Zones.md.rst rename to doc/services/monitoring/zones.rst index ffe4bba1e..bb588a303 100644 --- a/doc/services/monitoring/Zones.md.rst +++ b/doc/services/monitoring/zones.rst @@ -1,9 +1,6 @@ Zones ===== -Info ----- - A monitoring zone is a location that Rackspace Cloud Monitoring collects data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". It is an abstraction for a general location from which data is @@ -18,50 +15,43 @@ addresses or unrelated machines within that IP address range. A check references a list of monitoring zones it should be run from. - Setup ------- - -.. code:: php +Get details about a zone +------------------------ - $zoneId = 'mzAAAAA'; - $zone = $monitoringService->getMonitoringZone($zoneId); +.. code-block:: php -Attributes ----------- + $zone = $monitoringService->getMonitoringZone('{zoneId}'); +-----------------+------------------+-----------------------------------+------------------------+ | Name | Description | Data type | Method | +=================+==================+===================================+========================+ -| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | +| country_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | +-----------------+------------------+-----------------------------------+------------------------+ | label | Label | String | ``getLabel()`` | +-----------------+------------------+-----------------------------------+------------------------+ -| source\_ips | Source IP list | Array | ``getSourceIps()`` | +| source_ips | Source IP list | Array | ``getSourceIps()`` | +-----------------+------------------+-----------------------------------+------------------------+  List all zones --------------- -.. code:: php +.. code-block:: php $zones = $service->getMonitoringZones(); -Please consult the `iterator doc `__ for -more information about iterators. Perform a traceroute -------------------- -.. code:: php - - $traceroute = $zone->traceroute(array( - 'target' => 'http://test.com', - 'target_resolver' => 'IPv4' - )); +.. code-block:: php - // How many hops? - echo count($traceroute); + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); - // What was the first hop's IP? - echo $traceroute[0]->ip; + // How many hops? + echo count($traceroute); + // What was the first hop's IP? + echo $traceroute[0]->ip; From 7481ea1989c15b56b75263acf395300b8fe7b39d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:41 +0100 Subject: [PATCH 725/835] Networking docs --- doc/services/networking/README.md.rst | 108 ----- doc/services/networking/USERGUIDE.md.rst | 582 ----------------------- doc/services/networking/index.rst | 50 ++ doc/services/networking/networks.rst | 128 +++++ doc/services/networking/ports.rst | 150 ++++++ doc/services/networking/subnets.rst | 152 ++++++ 6 files changed, 480 insertions(+), 690 deletions(-) delete mode 100644 doc/services/networking/README.md.rst delete mode 100644 doc/services/networking/USERGUIDE.md.rst create mode 100644 doc/services/networking/networks.rst create mode 100644 doc/services/networking/ports.rst create mode 100644 doc/services/networking/subnets.rst diff --git a/doc/services/networking/README.md.rst b/doc/services/networking/README.md.rst deleted file mode 100644 index 605805fe1..000000000 --- a/doc/services/networking/README.md.rst +++ /dev/null @@ -1,108 +0,0 @@ -Networking -========== - -**Networking** is a service that you can use to create virtual networks -and attach cloud devices such as servers to these networks. - -Concepts --------- - -Concepts --------- - -To use the Networking service effectively, you should understand the -following key concepts: - -- **Network**: A network is an isolated virtual layer-2 broadcast - domain that is typically reserved for the tenant who created it - unless you configure the network to be shared. The network is the - main entity in the Networking service. Ports and subnets are always - associated with a network. - -- **Subnet**: A subnet represents an IP address block that can be used - to assign IP addresses to virtual instances (such as servers created - using the Compute service). Each subnet must have a CIDR and must be - associated with a network. - -- **Port**: A port represents a virtual switch port on a logical - network switch. Virtual instances (such as servers created using the - Compute service) attach their interfaces into ports. The port also - defines the MAC address and the IP address(es) to be assigned to the - interfaces plugged into them. When IP addresses are associated to a - port, this also implies the port is associated with a subet, as the - IP address is taken from the allocation pool for a specific subnet. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the Networking service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Networking service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All Networking operations are done via an *networking service object*. -To instantiate this object, call the ``networkingService`` method on the -``$client`` object. This method takes two arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+=====================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ - -.. code:: php - - $region = ''; - $networkingService = $client->networkingService(null, $region); - -Any networks, subnets, and ports created with this -``$networkingService`` instance will be stored in the cloud region -specified by ``$region``. - -3. Create a network. -~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -Once you have created a network, there is more you can do with it. See -`complete user guide for networking `__. diff --git a/doc/services/networking/USERGUIDE.md.rst b/doc/services/networking/USERGUIDE.md.rst deleted file mode 100644 index bfeb27774..000000000 --- a/doc/services/networking/USERGUIDE.md.rst +++ /dev/null @@ -1,582 +0,0 @@ -Complete User Guide for the Networking Service -============================================== - -Networking is a service that you can use to create virtual networks and -attach cloud devices such as servers to these networks. - -This user guide introduces you the entities in the Networking service — -networks, subnets, and ports — and shows you how to create and manage -these entities. - -Table of contents ------------------ - -- `Concepts <#concepts>`__ -- `Prerequisites <#prerequisites>`__ - - - `Client <#client>`__ - - `Networking service <#networking-service>`__ - -- `Networks <#networks>`__ - - - `Create a network <#create-a-network>`__ - - `Create multiple networks <#create-multiple-networks>`__ - - `List networks <#list-networks>`__ - - `Get a network <#get-a-network>`__ - - `Update a network <#update-a-network>`__ - - `Delete a network <#delete-a-network>`__ - -- `Subnets <#subnets>`__ - - - `Create a subnet <#create-a-subnet>`__ - - `Create multiple subnets <#create-multiple-subnets>`__ - - `List subnets <#list-subnets>`__ - - `Get a subnet <#get-a-subnet>`__ - - `Update a subnet <#update-a-subnet>`__ - - `Delete a subnet <#delete-a-subnet>`__ - -- `Ports <#ports>`__ - - - `Create a port <#create-a-port>`__ - - `Create multiple ports <#create-multiple-ports>`__ - - `List ports <#list-ports>`__ - - `Get a port <#get-a-port>`__ - - `Update a port <#update-a-port>`__ - - `Delete a port <#delete-a-port>`__ - -Concepts --------- - -To use the Networking service effectively, you should understand the -following key concepts: - -- **Network**: An isolated virtual layer-2 broadcast domain that is - typically reserved for the tenant who created it unless it is - configured to be shared. The network is the main entity in the - Networking service. Ports and subnets are always associated with a - network. - -- **Subnet**: An IP address block that can be used to assign IP - addresses to virtual instances (such as servers created using the - Compute service). Each subnet must have a CIDR and must be associated - with a network. - -- **Port**: A virtual switch port on a logical network switch. Virtual - instances (such as servers created using the Compute service) attach - their interfaces into ports. The port also defines the MAC address - and the IP address or addresses to be assigned to the interfaces - plugged into them. When IP addresses are associated with a port, this - also implies the port is associated with a subnet because the IP - address is taken from the allocation pool for a specific subnet. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the Networking service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate an - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Networking service -~~~~~~~~~~~~~~~~~~ - -All Networking operations are done via a *networking service object*. To -instantiate this object, call the ``networkingService`` method on the -``$client`` object. This method takes the following arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+=====================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ - -.. code:: php - - $region = ''; - $networkingService = $client->networkingService(null, $region); - -Any networks, subnets, and ports created with this -``$networkingService`` instance are stored in the cloud region specified -by ``$region``. - -Networks --------- - -A network is an isolated virtual layer-2 broadcast domain that is -typically reserved for the tenant who created it unless it is configured -to be shared. The network is the main entity in the Networking service. -Ports and subnets are always associated with a network. - -Create a network -~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+====================+===================================================================================================+=============+=============+=======================================+==================================+ -| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ -| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ - -You can create a network as shown in the following example: - -.. code:: php - - $network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' - )); - /** @var $network OpenCloud\Networking\Resource\Network **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple networks -~~~~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-network>`__. - -You can create multiple networks as shown in the following example: - -.. code:: php - - $networks = $networkingService->createNetworks(array( - array( - 'name' => 'My private backend network #1' - ), - array( - 'name' => 'My private backend network #2' - ) - )); - - foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List networks -~~~~~~~~~~~~~ - -You can list all the networks to which you have access as shown in the -following example: - -.. code:: php - - $networks = $networkingService->listNetworks(); - foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a network -~~~~~~~~~~~~~ - -You can retrieve a specific network by using that network's ID, as shown -in the following example: - -.. code:: php - - $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); - /** @var $network OpenCloud\Networking\Resource\Network **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a network -~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+====================+===================================================================================================+=============+=============+=================+==========================================+ -| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ -| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | -+--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ - -You can update a network as shown in the following example: - -.. code:: php - - $network->update(array( - 'name' => 'My updated private backend network' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a network -~~~~~~~~~~~~~~~~ - -You can delete a network as shown in the following example: - -.. code:: php - - $network->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Subnets -------- - -A subnet represents an IP address block that can be used to assign IP -addresses to virtual instances (such as servers created using the -Compute service). Each subnet must have a CIDR and must be associated -with a network. - -Create a subnet -~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ -| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ -| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | -+-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ - -You can create a subnet as shown in the following example: - -.. code:: php - - $subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25' - )); - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple subnets -~~~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-subnet>`__. - -You can create multiple subnets as shown in the following example: - -.. code:: php - - $subnets = $networkingService->createSubnets(array( - array( - 'name' => 'My subnet #1' - ), - array( - 'name' => 'My subnet #2' - ) - )); - - foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List subnets -~~~~~~~~~~~~ - -You can list all the subnets to which you have access as shown in the -following example: - -.. code:: php - - $subnets = $networkingService->listSubnets(); - foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a subnet -~~~~~~~~~~~~ - -You can retrieve a specific subnet by using that subnet's ID, as shown -in the following example: - -.. code:: php - - $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a subnet -~~~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ -| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ -| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | -+----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ - -You can update a subnet as shown in the following example: - -.. code:: php - - $subnet->update(array( - 'name' => 'My updated subnet', - 'hostRoutes' => array( - array( - 'destination' => '1.1.1.0/24', - 'nexthop' => '192.168.17.19' - ) - ), - 'gatewayIp' => '192.168.62.155' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a subnet -~~~~~~~~~~~~~~~ - -You can delete a subnet as shown in the following example: - -.. code:: php - - $subnet->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Ports ------ - -A port represents a virtual switch port on a logical network switch. -Virtual instances (such as servers created using the Compute service) -attach their interfaces into ports. The port also defines the MAC -address and the IP address or addresses to be assigned to the interfaces -plugged into them. When IP addresses are associated with a port, this -also implies the port is associated with a subnet because the IP address -is taken from the allocation pool for a specific subnet. - -Create a port -~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ -| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | -+----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ - -You can create a port as shown in the following example: - -.. code:: php - - $port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - )); - /** @var $port OpenCloud\Networking\Resource\Port **/ - -[ `Get the executable PHP script for this -example `__ ] - -Create multiple ports -~~~~~~~~~~~~~~~~~~~~~ - -This operation takes one parameter, an indexed array. Each element of -this array must be an associative array with the keys shown in `the -preceding table <#create-a-port>`__. - -You can create multiple ports as shown in the following example: - -.. code:: php - - $ports = $networkingService->createPorts(array( - array( - 'name' => 'My port #1', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ), - array( - 'name' => 'My port #2', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ) - )); - - foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List ports -~~~~~~~~~~ - -You can list all the ports to which you have access as shown in the -following example: - -.. code:: php - - $ports = $networkingService->listPorts(); - foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get a port -~~~~~~~~~~ - -You can retrieve a specific port by using that port's ID, as shown in -the following example: - -.. code:: php - - $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); - /** @var $port OpenCloud\Networking\Resource\Port **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update a port -~~~~~~~~~~~~~ - -This operation takes one parameter, an associative array, with the -following keys: - -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ -| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ -| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | -+----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ - -You can update a port as shown in the following example: - -.. code:: php - - $port->update(array( - 'fixedIps' => array( - array( - 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', - 'ipAddress' => '192.168.199.59' - ) - ) - )); - -[ `Get the executable PHP script for this -example `__ ] - -Delete a port -~~~~~~~~~~~~~ - -You can delete a port as shown in the following example: - -.. code:: php - - $port->delete(); - -[ `Get the executable PHP script for this -example `__ ] diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index e69de29bb..92270fe86 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -0,0 +1,50 @@ +Networking v2 +============= + +Setup +----- + +.. include:: rs-client.sample.rst + +Now, set up the Cloud Monitoring service: + +.. code-block:: php + + $service = $client->networkingService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + networks + subnets + ports + +Glossary +-------- + +.. glossary:: + + network + A network is an isolated virtual layer-2 broadcast domain that is typically + reserved for the tenant who created it unless you configure the network to + be shared. The network is the main entity in the Networking service. Ports + and subnets are always associated with a network. + + subnet + A subnet represents an IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the Compute + service). Each subnet must have a CIDR and must be associated with a network. + + port + A port represents a virtual switch port on a logical network switch. + Virtual instances (such as servers created using the Compute service) + attach their interfaces into ports. The port also defines the MAC address + and the IP address(es) to be assigned to the interfaces plugged into them. + When IP addresses are associated to a port, this also implies the port is + associated with a subet, as the IP address is taken from the allocation + pool for a specific subnet. diff --git a/doc/services/networking/networks.rst b/doc/services/networking/networks.rst new file mode 100644 index 000000000..a290a34e7 --- /dev/null +++ b/doc/services/networking/networks.rst @@ -0,0 +1,128 @@ +Networks +======== + +Create a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +`Get the executable PHP script for this example `__ + + +Create multiple networks +------------------------ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code-block:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +`Get the executable PHP script for this example `_ + +List networks +------------- + +You can list all the networks to which you have access as shown in the +following example: + +.. code-block:: php + + $networks = $networkingService->listNetworks(); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + + +`Get the executable PHP script for this example `_ + + +Get a network +------------- + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->getNetwork('{networkId}'); + +`Get the executable PHP script for this example `_ + + +Update a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code-block:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +`Get the executable PHP script for this example `_ + + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code-block:: php + + $network->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst new file mode 100644 index 000000000..06d2ec2cf --- /dev/null +++ b/doc/services/networking/ports.rst @@ -0,0 +1,150 @@ +Ports +===== + +Create a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + + +`Get the executable PHP script for this example `_ + + +Create multiple ports +--------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +List ports +---------- + +You can list all the ports to which you have access as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->listPorts(); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +Get a port +---------- + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->getPort('{portId}'); + +`Get the executable PHP script for this example `_ + + +Update a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code-block:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +`Get the executable PHP script for this example `_ + + +Delete a port +------------- + +You can delete a port as shown in the following example: + +.. code-block:: php + + $port->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/subnets.rst b/doc/services/networking/subnets.rst new file mode 100644 index 000000000..e8d6951a0 --- /dev/null +++ b/doc/services/networking/subnets.rst @@ -0,0 +1,152 @@ +Subnets +======= + +Create a subnet +--------------- + +This operation takes one parameter, an associative array, with the following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + +`Get the executable PHP script for this example `_ + + +Create multiple subnets +----------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code-block:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +List subnets +------------ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code-block:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +Get a subnet +------------ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->getSubnet('{subnetId}'); + +`Get the executable PHP script for this example `_ + + +Update a subnet +--------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code-block:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +`Get the executable PHP script for this example `_ + + +Delete a subnet +--------------- + +You can delete a subnet as shown in the following example: + +.. code-block:: php + + $subnet->delete(); + +`Get the executable PHP script for this example `_ From 1bd1c2a21c1261b472eabfc9fd304f2917db2d40 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:50 +0100 Subject: [PATCH 726/835] Object Store docs --- doc/services/object-store/Account.md.rst | 33 - .../object-store/Container.md.cdn.rst | 90 -- doc/services/object-store/Object.md.cdn.rst | 25 - .../object-store/Object.md.storage.rst | 330 ------- doc/services/object-store/README.md.rst | 86 -- doc/services/object-store/USERGUIDE.md.rst | 900 ------------------ .../{Access.md.rst => access.rst} | 69 +- doc/services/object-store/account.rst | 46 + doc/services/object-store/cdn.rst | 124 +++ ...ontainer.md.storage.rst => containers.rst} | 193 ++-- doc/services/object-store/index.rst | 57 ++ ...d.storage.rst => migrating-containers.rst} | 64 +- doc/services/object-store/objects.rst | 427 +++++++++ doc/services/object-store/rs-only.rst | 3 + 14 files changed, 834 insertions(+), 1613 deletions(-) delete mode 100644 doc/services/object-store/Account.md.rst delete mode 100644 doc/services/object-store/Container.md.cdn.rst delete mode 100644 doc/services/object-store/Object.md.cdn.rst delete mode 100644 doc/services/object-store/Object.md.storage.rst delete mode 100644 doc/services/object-store/README.md.rst delete mode 100644 doc/services/object-store/USERGUIDE.md.rst rename doc/services/object-store/{Access.md.rst => access.rst} (52%) create mode 100644 doc/services/object-store/account.rst create mode 100644 doc/services/object-store/cdn.rst rename doc/services/object-store/{Container.md.storage.rst => containers.rst} (53%) rename doc/services/object-store/{Migrating.md.storage.rst => migrating-containers.rst} (82%) create mode 100644 doc/services/object-store/objects.rst create mode 100644 doc/services/object-store/rs-only.rst diff --git a/doc/services/object-store/Account.md.rst b/doc/services/object-store/Account.md.rst deleted file mode 100644 index a2d380b2a..000000000 --- a/doc/services/object-store/Account.md.rst +++ /dev/null @@ -1,33 +0,0 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles'); - -View Account Details --------------------- - -To see how many containers you have in your account -(X-Account-Container-Count), how many objects are in your account -(X-Account-Object-Count), and how many total bytes your account uses -(X-Account-Bytes-Used): - -.. code:: php - - $account = $service->getAccount(); - - // Either return the full Metadata object - $details = $account->getDetails(); - - // or individual values - $account->getContainerCount(); - $account->getObjectCount(); - $account->getBytesUsed(); - diff --git a/doc/services/object-store/Container.md.cdn.rst b/doc/services/object-store/Container.md.cdn.rst deleted file mode 100644 index 2f1c8ea68..000000000 --- a/doc/services/object-store/Container.md.cdn.rst +++ /dev/null @@ -1,90 +0,0 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles'); - -To access the CDN functionality of a particular container: - -.. code:: php - - $container = $service->getContainer('foo_bar'); - - $cdn = $container->getCdn(); - -List CDN-enabled container --------------------------- - -To list CDN-only containers, follow the same operation for Storage which -lists all containers. The only difference is which service object you -execute the method on: - -.. code:: php - - $cdnService = $service->getCdnService(); - $cdnContainers = $cdnService->listContainers(); - - foreach ($cdnContainers as $cdnContainer) { - - } - -CDN-enable and -disable a container ------------------------------------ - -Before a container can be CDN-enabled, it must exist in the storage -system. When a container is CDN-enabled, any objects stored in it are -publicly accessible over the Content Delivery Network by combining the -container's CDN URL with the object name. - -Any CDN-accessed objects are cached in the CDN for the specified amount -of time called the TTL. The default TTL value is 259200 seconds, or 72 -hours. Each time the object is accessed after the TTL expires, the CDN -refetches and caches the object for the TTL period. - -.. code:: php - - $container->enableCdn(); - $container->disableCdn(); - -Serving containers through SSL ------------------------------- - -.. code:: php - - $cdn->getCdnSslUri(); - -Streaming CDN-enabled containers --------------------------------- - -.. code:: php - - $cdn->getCdnStreamingUri(); - -iOS streaming -------------- - -The Cloud Files CDN allows you to stream video to iOS devices without -needing to convert your video. Once you CDN-enable your container, you -have the tools necessary for streaming media to multiple devices. - -.. code:: php - - $cdn->getIosStreamingUri(); - -CDN logging ------------ - -To enable and disable logging for your CDN: - -.. code:: php - - $cdn->enableCdnLogging(); - $cdn->disableCdnLogging(); - diff --git a/doc/services/object-store/Object.md.cdn.rst b/doc/services/object-store/Object.md.cdn.rst deleted file mode 100644 index 64ac143b3..000000000 --- a/doc/services/object-store/Object.md.cdn.rst +++ /dev/null @@ -1,25 +0,0 @@ -Setup ------ - -You will need to instantiate the container object and access its CDN -functionality as `documented -here `__. - -Purge CDN-enabled objects -------------------------- - -To remove a CDN object from public access: - -.. code:: php - - $object->purge(); - -You can also provide an optional e-mail address (or comma-delimeted list -of e-mails), which the API will send a confirmation message to once the -object has been completely purged: - -.. code:: php - - $object->purge('jamie.hannaford@rackspace.com'); - $object->purge('hello@example.com,hallo@example.com'); - diff --git a/doc/services/object-store/Object.md.storage.rst b/doc/services/object-store/Object.md.storage.rst deleted file mode 100644 index 16dab18f4..000000000 --- a/doc/services/object-store/Object.md.storage.rst +++ /dev/null @@ -1,330 +0,0 @@ -Setup ------ - -Conceptually, a container contains objects (also known as files). In -order to work with objects, you will need to instantiate a container -object first as `documented -here `__. - -Note on object properties -------------------------- - -Please be aware that you cannot directly access the properties of -DataObject anymore, you **must** use appropriate getter/ setter methods: - -+----------------------+------------------------+ -| Property | Method | -+======================+========================+ -| Parent container | ``getContainer`` | -+----------------------+------------------------+ -| Name | ``getName`` | -+----------------------+------------------------+ -| Body of file | ``getContent`` | -+----------------------+------------------------+ -| Size of file | ``getContentLength`` | -+----------------------+------------------------+ -| Type of file | ``getContentType`` | -+----------------------+------------------------+ -| ETag checksum | ``getEtag`` | -+----------------------+------------------------+ -| Last modified date | ``getLastModified`` | -+----------------------+------------------------+ - -Create an object ----------------- - -There are three ways to upload a new file, each of which has different -business needs. - - **Note:** Unlike previous versions, you do not need to manually - specify your object's content type. The API will do this for you. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -To upload a single/basic file: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - use OpenCloud\ObjectStore\Resource\DataObject; - - $data = fopen('/path/to/sample.mp3', 'r+'); - - // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) - - $meta = array( - 'Author' => 'Camera Obscura', - 'Origin' => 'Glasgow' - ); - - $metaHeaders = DataObject::stockHeaders($meta); - $customHeaders = array(); - $allHeaders = $metaHeaders + $customHeaders; - - $container->uploadObject('sample.mp3', $data, $allHeaders); - -To upload multiple small-to-mid sized files: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $files = array( - array( - 'name' => 'apache.log', - 'path' => '/etc/httpd/logs/error_log' - ), - array( - 'name' => 'mysql.log', - 'body' => fopen('/tmp/mysql.log', 'r+') - ), - array( - 'name' => 'to_do_list.txt', - 'body' => 'PHONE HOME' - ) - ); - - $container->uploadObjects($files); - -As you can see, the ``name`` key is required for every file. You must -also specify *either* a path key (to an existing file), or a ``body``. -The ``body`` can either be a PHP resource or a string representation of -the content you want to upload. - -To upload large files -~~~~~~~~~~~~~~~~~~~~~ - -For files over 5GB, you will need to use the -``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your -transfer, upon which you can execute your upload functionality. For your -convenience, the Container resource object contains a simple method to -do this heavy lifting for you: - -.. code:: php - - $transfer = $container->setupObjectTransfer(array( - 'name' => 'video.mov', - 'path' => '/home/jamie/video.mov', - 'metadata' => array( - 'Author' => 'Jamie' - ), - 'concurrency' => 4, - 'partSize' => 1.5 * Size::GB - )); - - $transfer->upload(); - -You can specify how many concurrent cURL connections are used to upload -parts of your file. The file is fragmented into chunks, each of which is -uploaded individually as a separate file (the filename of each part will -indicate that it's a segment rather than the full file). After all parts -are uploaded, a manifest is uploaded. When the end-user accesses the 5GB -by its true filename, it actually references the manifest file which -concatenates each segment into a streaming download. - -List objects in a container ---------------------------- - -To return a list of objects: - -.. code:: php - - $files = $container->objectList(); - - foreach ($files as $file) { - // ... do something - } - -By default, 10,000 objects are returned as a maximum. To get around -this, you can construct a query which refines your result set. For a -full specification of query parameters relating to collection filtering, -see the `official -docs `__. - -.. code:: php - - $container->objectList(array('prefix' => 'logFile_')); - -Get object ----------- - -To retrieve a specific file from Cloud Files: - -.. code:: php - - $file = $container->getObject('summer_vacation.mp4'); - -Conditional requests -~~~~~~~~~~~~~~~~~~~~ - -You can also perform conditional requests according to `RFC 2616 -specification `__ (§§ 14.24-26). -Supported headers are ``If-Match``, ``If-None-Match``, -``If-Modified-Since`` and ``If-Unmodified-Since``. - -So, to retrieve a file's contents only if it's been recently changed - -.. code:: php - - $file = $container->getObject('error_log.txt', array( - 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' - )); - - if ($file->getContentLength()) { - echo 'Has been changed since the above date'; - } else { - echo 'Has not been changed'; - } - -Retrieve a file only if it has NOT been modified (and expect a 412 on -failure): - -:: - - use Guzzle\Http\Exception\ClientErrorResponseException; - - try { - $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( - 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' - )); - } catch (ClientErrorResponseException $e) { - echo 'This file has been modified...'; - } - -Finally, you can specify a range - which will return a subset of bytes -from the file specified. To return the last 20B of a file: - -.. code:: php - - $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); - -Update an existing object -------------------------- - -Updating content is easy: - -.. code:: php - - $file->setContent(fopen('/path/to/new/content', 'r+')); - $file->update(); - -Bear in mind that updating a file name will result in a new file being -generated (under the new name). You will need to delete the old file. - -Copy object ------------ - -To copy a file to another location, you need to specify a string-based -destination path: - -.. code:: php - - $object->copy('/container_2/new_object_name'); - -Delete object -------------- - -.. code:: php - - $object->delete(); - -Get object metadata -------------------- - -You can fetch just the object metadata without fetching the full -content: - -.. code:: php - - $container->getPartialObject('summer_vacation.mp4'); - -In order to access the metadata on a partial or complete object, use: - -.. code:: php - - $object->getMetadata(); - -You can turn a partial object into a full object to get the content -after looking at the metadata: - -.. code:: php - - $object->refresh(); - -You can also update to get the latest metadata: - -.. code:: php - - $object->retrieveMetadata(); - -Update object metadata ----------------------- - -Similarly, with setting metadata there are two options: you can update -the metadata values of the local object (i.e. no HTTP request) if you -anticipate you'll be executing one soon (an update operation for -example): - -.. code:: php - - // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation - $object->setMetadata(array( - 'Author' => 'Hemingway' - )); - - // ... code here - - $object->update(); - -Alternatively, you can update the API straight away - so that everything -is retained: - -.. code:: php - - $object->saveMetadata(array( - 'Author' => 'Hemingway' - )); - -Please be aware that these methods override and wipe existing values. If -you want to append values to your metadata, use the correct method: - -.. code:: php - - $metadata = $object->appendToMetadata(array( - 'Author' => 'Hemingway' - )); - - $object->saveMetadata($metadata); - -Extract archive ---------------- - -CloudFiles provides you the ability to extract uploaded archives to -particular destinations. The archive will be extracted and its contents -will populate the particular area specified. To upload file (which might -represent a directory structure) into a particular container: - -.. code:: php - - use OpenCloud\ObjectStore\Constants\UrlType; - - $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); - -You can also omit the container name (i.e. provide an empty string as -the first argument). If you do this, the API will create the containers -necessary to house the extracted files - this is done based on the -filenames inside the archive. - -Bulk delete ------------ - -Bulk delete a set of paths: - -.. code:: php - - $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - - $service->bulkDelete($pathsToBeDeleted); - diff --git a/doc/services/object-store/README.md.rst b/doc/services/object-store/README.md.rst deleted file mode 100644 index 01cf26733..000000000 --- a/doc/services/object-store/README.md.rst +++ /dev/null @@ -1,86 +0,0 @@ -Object Store -============ - -**Object Store** is an object-based storage system that stores content -and metadata as objects in a cloud. - -Specifically, a cloud is made up of one or more regions. Each region can -have several **containers**, created by a user. Each container can -container several **objects** (sometimes referred to as files), uploaded -by the user. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Choose one of the following two options: - -- If you are working with a vanilla OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as shown below. - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as shown below. - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Object Store service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $region = 'DFW'; - $objectStoreService = $client->objectStoreService(null, $region); - -In the example above, you are connecting to the ``DFW`` region of the -cloud. Any containers and objects created with this -``$objectStoreService`` instance will be stored in that cloud region. - -3. Create a container for your objects (also referred to as files). -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $container = $objectStoreService->createContainer('logos'); - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -4. Upload an object to the container. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -There is a lot more you can do with containers and objects. See the -`complete user guide to the Object Store service `__. diff --git a/doc/services/object-store/USERGUIDE.md.rst b/doc/services/object-store/USERGUIDE.md.rst deleted file mode 100644 index d9732ea36..000000000 --- a/doc/services/object-store/USERGUIDE.md.rst +++ /dev/null @@ -1,900 +0,0 @@ -The Complete User Guide to the Object Store Service -=================================================== - -**Object Store** is an object-based storage system that stores content -and metadata as objects in a cloud. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the object store service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with a vanilla OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as shown below. - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as shown below. - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Object Store Service -~~~~~~~~~~~~~~~~~~~~ - -All operations on the object store are done via an object store service -object. - -.. code:: php - - $region = 'DFW'; - $objectStoreService = $client->objectStoreService(null, $region); - -In the example above, you are connecting to the ``DFW`` region of the -cloud. Any containers and objects created with this -``$objectStoreService`` instance will be stored in that cloud region. - -Containers ----------- - -A **container** defines a namespace for **objects**. An object with the -same name in two different containers represents two different objects. - -For example, you may create a container called ``logos`` to hold all the -image files for your blog. - -A container may contain zero or more objects in it. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -Create Container -~~~~~~~~~~~~~~~~ - -.. code:: php - - $container = $objectStoreService->createContainer('logos'); - -[ `Get the executable PHP script for this -example `__ ] - -Get Container Details -~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a single container's details by using its name. An -instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. - -.. code:: php - - $container = $objectStoreService->getContainer('logos'); - - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - -[ `Get the executable PHP script for this -example `__ ] - -List Containers -~~~~~~~~~~~~~~~ - -You can retrieve a list of all your containers. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $containers = $objectStoreService->listContainers(); - foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Set or Update Container Metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can set metadata on a container. - -.. code:: php - - $container->saveMetadata(array( - 'author' => 'John Doe' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Get Container Metadata -~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve the metadata for a container. - -.. code:: php - - $containerMetadata = $container->getMetadata(); - -[ `Get the executable PHP script for this -example `__ ] - -Delete Container -~~~~~~~~~~~~~~~~ - -When you no longer have a need for the container, you can remove it. - -If the container is empty (that is, it has no objects in it), you can -remove it as shown below: - -.. code:: php - - $container->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -If the container is not empty (that is, it has objects in it), you have -two choices in how to remove it: - -- `Individually remove each object <#delete-object>`__ in the - container, then remove the container itself as shown above, or - -- Remove the container and all the objects within it as shown below: - - .. code:: php - - $container->delete(true); - - [ `Get the executable PHP script for this - example `__ ] - -Get Object Count -~~~~~~~~~~~~~~~~ - -You can quickly find out how many objects are in a container. - -.. code:: php - - $containerObjectCount = $container->getObjectCount(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$containerObjectCount`` will contain the number -of objects in the container represented by ``$container``. - -Get Bytes Used -~~~~~~~~~~~~~~ - -You can quickly find out the space used by a container, in bytes. - -.. code:: php - - $containerSizeInBytes = $container->getBytesUsed(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$containerSizeInBytes`` will contain the space -used, in bytes, by the container represented by ``$container``. - -Container Quotas -~~~~~~~~~~~~~~~~ - -Set Quota for Number of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can set a quota for the maximum number of objects that may be stored -in a container. - -.. code:: php - - $maximumNumberOfObjectsAllowedInContainer = 25; - $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); - -[ `Get the executable PHP script for this -example `__ ] - -Set Quota for Total Size of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can set a quota for the maximum total space (in bytes) used by -objects in a container. - -.. code:: php - - use OpenCloud\Common\Constants\Size; - - $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; - $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); - -[ `Get the executable PHP script for this -example `__ ] - -Get Quota for Number of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can retrieve the quota for the maximum number of objects that may be -stored in a container. - -.. code:: php - - $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); - -[ `Get the executable PHP script for this -example `__ ] - -Get Quota for Total Size of Objects -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can retrieve the quota for the maximum total space (in bytes) used -by objects in a container. - -.. code:: php - - $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); - -[ `Get the executable PHP script for this -example `__ ] - -Objects -------- - -An **object** (sometimes referred to as a file) is the unit of storage -in an Object Store. An object is a combination of content (data) and -metadata. - -For example, you may upload an object named ``php-elephant.jpg``, a JPEG -image file, to the ``logos`` container. Further, you may assign metadata -to this object to indicate that the author of this object was someone -named Jane Doe. - - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in - -Upload Object -~~~~~~~~~~~~~ - -Once you have created a container, you can upload objects to it. - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, an image file from the local filesystem -(``path/to/local/php-elephant.jpg``) is uploaded to a container in the -Object Store. - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -It is also possible to upload an object and associate metadata with it. - -.. code:: php - - use OpenCloud\ObjectStore\Resource\DataObject; - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'php-elephant.jpg'; - $metadata = array('author' => 'Jane Doe'); - - $customHeaders = array(); - $metadataHeaders = DataObject::stockHeaders($metadata); - $allHeaders = $customHeaders + $metadataHeaders; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData, $allHeaders); - -[ `Get the executable PHP script for this -example `__ ] - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -Pseudo-hierarchical Folders -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Although you cannot nest directories in an Object Store, you can -simulate a hierarchical structure within a single container by adding -forward slash characters (``/``) in the object name. - -.. code:: php - - $localFileName = '/path/to/local/php-elephant.jpg'; - $remoteFileName = 'languages/php/elephant.jpg'; - - $fileData = fopen($localFileName, 'r'); - $container->uploadObject($remoteFileName, $fileData); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, an image file from the local filesystem -(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the -Object Store. Within that container, the filename is -``languages/php/elephant.jpg``, where ``languages/php/`` is a -pseudo-hierarchical folder hierarchy. - -Note that while we call ``fopen`` to open the file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``uploadObject`` call. - -Upload Multiple Objects -^^^^^^^^^^^^^^^^^^^^^^^ - -You can upload more than one object at a time to a container. - -.. code:: php - - $objects = array( - array( - 'name' => 'php-elephant.jpg', - 'path' => '/path/to/local/php-elephant.jpg' - ), - array( - 'name' => 'python-snake.jpg', - 'path' => '/path/to/local/python-snake.jpg' - ), - a - ); - - $container->uploadObjects($objects); - -[ `Get the executable PHP script for this -example `__ ] - -In the above example, the contents of two files present on the local -filesystem are uploaded as objects to the container referenced by -``$container``. - -Instead of specifying the ``path`` key in an element of the ``$objects`` -array, you can specify a ``body`` key whose value is a string or a -stream representation. - -Finally, you can pass headers as the second parameter to the -``uploadObjects`` method. These headers will be applied to every object -that is uploaded. - -:: - - $metadata = array('author' => 'Jane Doe'); - - $customHeaders = array(); - $metadataHeaders = DataObject::stockHeaders($metadata); - $allHeaders = $customHeaders + $metadataHeaders; - - $container->uploadObjects($objects, $allHeaders); - -[ `Get the executable PHP script for this -example `__ -] - -In the example above, every object referenced within the ``$objects`` -array will be uploaded with the same metadata. - -Large Objects -~~~~~~~~~~~~~ - -If you want to upload objects larger than 5GB in size, you must use a -different upload process. - -.. code:: php - - $options = array( - 'name' => 'san_diego_vacation_video.mp4', - 'path' => '/path/to/local/videos/san_diego_vacation.mp4' - ); - $objectTransfer = $container->setupObjectTransfer($options); - $objectTransfer->upload(); - -[ `Get the executable PHP script for this -example `__ ] - -The process shown above will automatically partition your large object -into small chunks and upload them concurrently to the container -represented by ``$container``. - -You can tune the parameters of this process by specifying additional -options in the ``$options`` array. Here is a complete listing of keys -that can be specified in the ``$options`` array: - -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| Key name | Description | Data Type | Required? | Default Value | Example | -+===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ -| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ -| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | -+-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ - -Auto-extract Archive Files -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can upload a tar archive file and have the Object Store service -automatically extract it into a container. - -.. code:: php - - use OpenCloud\ObjectStore\Constants\UrlType; - - $localArchiveFileName = '/path/to/local/image_files.tar.gz'; - $remotePath = 'images/'; - - $fileData = fopen($localArchiveFileName, 'r'); - $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); - -[ `Get the executable PHP script for this -example `__ ] - -In the above example, a local archive file named ``image_files.tar.gz`` -is uploaded to an Object Store container named ``images`` (defined by -the ``$remotePath`` variable). - -Note that while we call ``fopen`` to open a file resource, we do not -call ``fclose`` at the end. The file resource is automatically closed -inside the ``bulkExtract`` call. - -The third parameter to ``bulkExtract`` is the type of the archive file -being uploaded. The acceptable values for this are: - -- ``UrlType::TAR`` for tar archive files, *or*, -- ``UrlType:TAR_GZ`` for tar archive files that are compressed with - gzip, *or* -- ``UrlType::TAR_BZ`` for tar archive file that are compressed with - bzip - -Note that the value of ``$remotePath`` could have been a -(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as -``images/blog`` as well. - -List Objects in a Container -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all the objects stored in a container. An instance of -``OpenCloud\Common\Collection\PaginatedIterator`` is returned. - -.. code:: php - - $objects = $container->objectList(); - foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -You can list only those objects in the container whose names start with -a certain prefix. - -.. code:: php - - $options = array( - 'prefix' => 'php' - ); - - $objects = $container->objectList($options); - -[ `Get the executable PHP script for this -example `__ ] - -In general, the ``objectList()`` method described above takes an -optional parameter (``$options`` in the example above). This parameter -is an associative array of various options. Here is a complete listing -of keys that can be specified in the ``$options`` array: - -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| Key name | Description | Data Type | Required? | Default Value | Example | -+==================+=============================================================================+=============+=============+=================+=========================+ -| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ -| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | -+------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ - -Retrieve Object -~~~~~~~~~~~~~~~ - -You can retrieve an object and its metadata, given the object's -container and name. - -.. code:: php - - $objectName = 'php-elephant.jpg'; - $object = $container->getObject($objectName); - - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - - $objectContent = $object->getContent(); - - /** @var $objectContent Guzzle\Http\EntityBody **/ - - // Write object content to file on local filesystem. - $objectContent->rewind(); - $stream = $objectContent->getStream(); - $localFilename = tempnam("/tmp", 'php-opencloud-'); - file_put_contents($localFilename, $stream); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$object`` is the object named -``php-elephant.jpg`` in the container represented by ``$container``. -Further, ``$objectContent`` represents the contents of the object. It is -of type -```Guzzle\Http\EntityBody`` `__. - -Retrieve Object Metadata -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve just an object's metadata without retrieving its -contents. - -.. code:: php - - $objectName = 'php-elephant.jpg'; - $object = $container->getPartialObject($objectName); - $objectMetadata = $object->getMetadata(); - - /** @var $objectMetadata \OpenCloud\Common\Metadata **/ - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, while ``$object`` is an instance of -``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only -partially populated. Specifically, only properties of the instance -relating to object metadata are populated. - -Temporary URLs -~~~~~~~~~~~~~~ - -The Temporary URL feature allows you to create limited-time Internet -addresses that allow you to grant limited access to your Object Store -account. Using this feature, you can allow others to retrieve or place -objects in your Object Store account for a specified amount of time. -Access to the temporary URL is independent of whether or not your -account is `CDN-enabled <#cdn-containers>`__. Even if you do not -CDN-enable a container, you can still grant temporary public access -through a temporary URL. - -First, you must set the temporary URL secret on your account. This is a -one-time operation; you only need to perform it the very first time you -wish to use the temporary URLs feature. - -.. code:: php - - $account->setTempUrlSecret(); - -[ `Get the executable PHP script for this -example `__ ] - -Note that this operation is carried out on ``$account``, which is an -instance of ``OpenCloud\ObjectStore\Resource\Account``, a class -representing `your object store account <#accounts>`__. - -The above operation will generate a random secret and set it on your -account. Instead of a random secret, if you wish to provide a secret, -you can supply it as a parameter to the ``setTempUrlSecret`` method. - -.. code:: php - - $account->setTempUrlSecret(''); - -[ `Get the executable PHP script for this -example `__ -] - -Once a temporary URL secret has been set on your account, you can -generate a temporary URL for any object in your Object Store. - -.. code:: php - - $expirationTimeInSeconds = 3600; // one hour from now - $httpMethodAllowed = 'GET'; - $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, a temporary URL for the object is generated. This -temporary URL will provide public access to the object for an hour (3600 -seconds), as specified by the ``$expirationTimeInSeconds`` variable. -Further, only GET HTTP methods will be allowed on this URL, as specified -by the ``$httpMethodAllowed`` variable. The other value allowed for the -``$httpMethodAllowed`` variable would be ``PUT``. - -You can also retrieve the temporary URL secret that has been set on your -account. - -.. code:: php - - $tempUrlSecret = $account->getTempUrlSecret(); - -[ `Get the executable PHP script for this -example `__ ] - -Update Object -~~~~~~~~~~~~~ - -You can update an object's contents (as opposed to `updating its -metadata <#update-object-metadata>`__) by simply re-\ `uploading the -object <#upload-object>`__ to its container using the same object name -as before. - -Update Object Metadata -~~~~~~~~~~~~~~~~~~~~~~ - -You can update an object's metadata after it has been uploaded to a -container. - -.. code:: php - - $object->saveMetadata(array( - 'author' => 'John Doe' - )); - -[ `Get the executable PHP script for this -example `__ ] - -Copy Object -~~~~~~~~~~~ - -You can copy an object from one container to another, provided the -destination container already exists. - -.. code:: php - - $object->copy('logos_copy/php.jpg'); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, both the name of the destination container -(``logos_copy``)and the name of the destination object (``php.jpg``) -have to be specified, separated by a ``/``. - -Delete Object -~~~~~~~~~~~~~ - -When you no longer need an object, you can delete it. - -.. code:: php - - $object->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Bulk Delete -~~~~~~~~~~~ - -While you can delete individual objects as shown above, you can also -delete objects and empty containers in bulk. - -.. code:: php - - $objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' - )); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, two objects (``some_container/object_a.png``, -``some_other_container/object_z.png``) and one empty container -(``some_empty_container``) are all being deleted in bulk via a single -command. - -CDN Containers --------------- - -Note: The functionality described in this section is available only on -the Rackspace cloud. It will not work as described when working with a -vanilla OpenStack cloud. - -Any container can be converted to a CDN-enabled container. When this is -done, the objects within the container can be accessed from anywhere on -the Internet via a URL. - -Enable CDN Container -~~~~~~~~~~~~~~~~~~~~ - -To take advantage of CDN capabilities for a container and its objects, -you must CDN-enable that container. - -.. code:: php - - $container->enableCdn(); - -[ `Get the executable PHP script for this -example `__ ] - -Public URLs -~~~~~~~~~~~ - -Once you have CDN-enabled a container, you can retrieve a -publicly-accessible URL for any of its objects. There are four types of -publicly-accessible URLs for each object. Each type of URL is meant for -a different purpose. The sections below describe each of these URL types -and how to retrieve them. - -HTTP URL -^^^^^^^^ - -You can use this type of URL to access the object over HTTP. - -:: - - $httpUrl = $object->getPublicUrl(); - -[ `Get the executable PHP script for this -example `__ ] - -Secure HTTP URL -^^^^^^^^^^^^^^^ - -You can use this type of URL to access the object over HTTP + TLS/SSL. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $httpsUrl = $object->getPublicUrl(UrlType::SSL); - -[ `Get the executable PHP script for this -example `__ ] - -Streaming URL -^^^^^^^^^^^^^ - -You can use this type of URL to stream a video or audio object using -Adobe's HTTP Dynamic Streaming. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); - -[ `Get the executable PHP script for this -example `__ ] - -IOS Streaming URL -^^^^^^^^^^^^^^^^^ - -You can use this type of URL to stream an audio or video object to an -iOS device. - -:: - - use OpenCloud\ObjectStore\Constants\UrlType; - - $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); - -[ `Get the executable PHP script for this -example `__ ] - -Update CDN Container TTL -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can update the TTL of a CDN-enabled container. - -.. code:: php - - $cdnContainer = $container->getCdn(); - $cdnContainer->setTtl(); - -[ `Get the executable PHP script for this -example `__ ] - -Disable CDN Container -~~~~~~~~~~~~~~~~~~~~~ - -If you no longer need CDN capabilities for a container, you can disable -them. - -.. code:: php - - $container->disableCdn(); - -[ `Get the executable PHP script for this -example `__ ] - -Accounts --------- - -An **account** defines a namespace for **containers**. An account can -have zero or more containers in it. - -Retrieve Account -~~~~~~~~~~~~~~~~ - -You must retrieve the account before performing any operations on it. - -.. code:: php - - $account = $objectStoreService->getAccount(); - -Get Container Count -~~~~~~~~~~~~~~~~~~~ - -You can quickly find out how many containers are in your account. - -.. code:: php - - $accountContainerCount = $account->getContainerCount(); - -[ `Get the executable PHP script for this -example `__ ] - -Get Object Count -~~~~~~~~~~~~~~~~ - -You can quickly find out how many objects are in your account. - -.. code:: php - - $accountObjectCount = $account->getObjectCount(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$accountObjectCount`` will contain the number of -objects in the account represented by ``$account``. - -Get Bytes Used -~~~~~~~~~~~~~~ - -You can quickly find out the space used by your account, in bytes. - -.. code:: php - - $accountSizeInBytes = $account->getBytesUsed(); - -[ `Get the executable PHP script for this -example `__ ] - -In the example above, ``$accountSizeInBytes`` will contain the space -used, in bytes, by the account represented by ``$account``. diff --git a/doc/services/object-store/Access.md.rst b/doc/services/object-store/access.rst similarity index 52% rename from doc/services/object-store/Access.md.rst rename to doc/services/object-store/access.rst index ee85a938e..62cb541ca 100644 --- a/doc/services/object-store/Access.md.rst +++ b/doc/services/object-store/access.rst @@ -1,31 +1,19 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(RACKSPACE_US, array( - - )); - - $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want - Temporary URLs --------------- +============== Temporary URLs allow you to create time-limited Internet addresses that allow you to grant access to your Cloud Files account. Using Temporary URL, you may allow others to retrieve or place objects in your containers - regardless of whether they're CDN-enabled. + Set "temporary URL" metadata key -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------- You must set this "secret" value on your account, where it can be used in a global state: -.. code:: php +.. code-block:: php $account = $service->getAccount(); $account->setTempUrlSecret('my_secret'); @@ -35,41 +23,64 @@ in a global state: The string argument of ``setTempUrlSecret()`` is optional - if left out, the SDK will generate a random hashed secret for you. + Create a temporary URL -~~~~~~~~~~~~~~~~~~~~~~ +---------------------- Once you've set an account secret, you can create a temporary URL for your object. To allow GET access to your object for 1 minute: -.. code:: php +.. code-block:: php $object->getTemporaryUrl(60, 'GET'); + To allow PUT access for 1 hour: -.. code:: php +.. code-block:: php $object->getTemporaryUrl(360, 'PUT'); -Hosting websites on CloudFiles ------------------------------- -To host a static (i.e. HTML) website on CloudFiles, you must follow +Hosting HTML sites on CDN +========================= + +.. include:: rs-only.rst + +To host a static (i.e. HTML) website on Cloud Files, you must follow these steps: -1. CDN-enable a container +1. CDN-enable a container: + +.. code-block:: php + + $container = $service->getContainer('html_site'); + $container->enableCdn(); + 2. Upload all HTML content. You can use nested directory structures. -3. Tell CloudFiles what to use for your default index page like this: -.. code:: php +.. code-block:: php + + $container->uploadObjects(array( + array('name' => 'index.html', 'path' => 'index.html'), + array('name' => 'contact.html', 'path' => 'contact.html'), + array('name' => 'error.html', 'path' => 'error.html'), + array('name' => 'styles.css', 'path' => 'styles.css'), + array('name' => 'main.js', 'path' => 'main.js'), + )); + +3. Tell Cloud Files what to use for your default index page like this: + +.. code-block:: php + + $container->setStaticIndexPage('index.html'); - $container->setStaticIndexPage('index.html'); +4. (Optional) Tell Cloud Files which error page to use by default: -4. (Optional) Tell CloudFiles which error page to use by default: +.. code-block:: php -.. code:: php + $container->setStaticErrorPage('error.html'); - $container->setStaticErrorPage('error.html'); Bear in mind that steps 3 & 4 do not upload content, but rather specify a reference to an existing page/CloudFiles object. diff --git a/doc/services/object-store/account.rst b/doc/services/object-store/account.rst new file mode 100644 index 000000000..fb51c8855 --- /dev/null +++ b/doc/services/object-store/account.rst @@ -0,0 +1,46 @@ +Account Details +=============== + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +Setup +----- + +.. code-block:: php + + $account = $service->getAccount(); + + +View all details +---------------- + +.. code-block:: php + + $details = $account->getDetails(); + + +Retrieve total container count +------------------------ + +.. code-block:: php + + $account->getContainerCount(); + + +Retrieve total object count +--------------------- + +.. code-block:: php + + $account->getObjectCount(); + + +Retrieve total bytes used +------------------------- + +.. code-block:: php + + $account->getBytesUsed(); diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst new file mode 100644 index 000000000..5ad72b6cd --- /dev/null +++ b/doc/services/object-store/cdn.rst @@ -0,0 +1,124 @@ +CDN Containers +============== + +.. include:: rs-only.rst + +Setup +----- + +In order to interact with CDN containers, you first need to instantiate a +CDN service object: + +.. code-block:: php + + $cdnService = $service->getCdnService(); + + +List CDN-enabled containers +--------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code-block:: php + + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ + } + + +CDN-enable a container +---------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code-block:: php + + $container->enableCdn(); + + +CDN-disable a container +----------------------- + +.. code-block:: php + + $container->disableCdn(); + + +Operations on CDN-enabled containers +------------------------------------ + +Once a container has been CDN-enabled, you can retrieve it like so: + +.. code-block:: php + + $cdnContainer = $cdnService->cdnContainer('{containerName}'); + + +Retrieve the SSL URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnSslUri(); + + +Retrieve the streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnStreamingUri(); + + +Retrieve the iOS streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code-block:: php + + $cdnContainer->getIosStreamingUri(); + + +CDN logging +~~~~~~~~~~~ + +To enable and disable logging for your CDN-enabled container: + +.. code-block:: php + + $cdnContainer->enableCdnLogging(); + $cdnContainer->disableCdnLogging(); + + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code-block:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code-block:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); diff --git a/doc/services/object-store/Container.md.storage.rst b/doc/services/object-store/containers.rst similarity index 53% rename from doc/services/object-store/Container.md.storage.rst rename to doc/services/object-store/containers.rst index 89798fdd0..4b9c66ac3 100644 --- a/doc/services/object-store/Container.md.storage.rst +++ b/doc/services/object-store/containers.rst @@ -1,26 +1,12 @@ -Setup ------ - -.. code:: php - - use OpenCloud\Rackspace; - - // Create a client object to communicate with various Rackspace Cloud services. - $client = new Rackspace(RACKSPACE_US, array( - 'username' => 'Replace this with your Rackspace Cloud user name', - 'apiKey' => 'Replace this with your Rackspace Cloud API key' - )); - - // Create a service object to use the object store service. The sample code - // creates the object store in the 'DFW' region. - $service = $client->objectStoreService('cloudFiles', 'DFW'); +Containers +========== Create container ---------------- To create a new container, you just need to define its name: -.. code:: php +.. code-block:: php $container = $service->createContainer('my_amazing_container'); @@ -30,133 +16,153 @@ likely due to the fact you have a naming collision. Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. - **Note:** when working with names that contain non-standard - alphanumerical characters (such as spaces or non-English - characters), you must ensure they are encoded with - ```urlencode`` `__ before passing them in +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in List containers --------------- -Return a list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $containerList = $service->listContainers(); + $containers = $service->listContainers(); - while ($container = $containerList->next()) { - // Do stuff; some examples below - printf("Container name: %s\n", $container->name); - printf("Number of objects within container: %d\n", $container->getObjectCount()); - } + foreach ($containers as $container) { + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } Container names are sorted based on a binary comparison, a single built-in collating sequence that compares string data using SQLite's memcmp() function, regardless of text encoding. -The list is limited to 10,000 containers at a time. See 1.3 for ways to -limit and navigate this list. +The list is limited to 10,000 containers at a time. To work with larger +collections, please read the next section. -Return a formatted list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Currently, the SDK only supports JSON-formatted responses. - -Controlling a large list of containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Filtering large collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You may limit and control this list of results by using the ``marker`` +When you need more control over collections of containers, you can filter the +results and return back a subset of the total collection by using the ``marker`` and ``end_marker`` parameters. The former parameter (``marker``) tells the API where to begin the list, and the latter (``end_marker``) tells it where to end the list. You may use either of them independently or -together. You may also use the ``limit`` parameter to fix the number of +together. + +You may also use the ``limit`` parameter to fix the number of containers returned. To list a set of containers between two fixed points: -.. code:: php +.. code-block:: php - $someContainers = $service->listContainers(array( - 'marker' => 'container_55', - 'end_marker' => 'container_2001' - )); + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); Or to return a limited set: -.. code:: php +.. code-block:: php + + $someContainers = $service->listContainers(array('limit' => 560)); - $someContainers = $service->listContainers(array('limit' => 560)); Get container ------------- -To retrieve a certain container, either to access its object or -metadata: +To retrieve a certain container: + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $service->getContainer('{containerName}'); + + +Retrieve a container's name +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php -.. code:: php + $name = $container->name; - $container = $service->getContainer('container_name'); - echo $container->getObjectCount(); - echo $container->getBytesUsed(); +Retrieve a container's object count +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $count = $container->getObjectCount(); + + +Retrieve a container's total bytes used +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $bytes = $container->getBytesUsed(); + Delete container ---------------- -Deleting a container is easy: +Deleting an empty container is easy: -.. code:: php +.. code-block:: php + + $container->delete(); - $container->delete(); Please bear mind that you must delete all objects inside a container before deleting it. This is done for you if you set the ``$deleteObjects`` parameter to ``TRUE`` like so: -.. code:: php +.. code-block:: php + + $container->delete(true); - $container->delete(TRUE); -You can also do it manually: +You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ +first, and then call ``delete``. -.. code:: php - $container->deleteAllObjects(); - $container->delete(); +Deleting all objects inside a container +--------------------------------------- + +.. code-block:: php + + $container->deleteAllObjects(); + Create or update container metadata ----------------------------------- -.. code:: php +.. code-block:: php - $container->saveMetadata(array( - 'Author' => 'Virginia Woolf', - 'Published' => '1931' - )); + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); Please bear in mind that this action will set metadata to this array - overriding existing values and wiping those left out. To *append* values to the current metadata: -.. code:: php - - $metadata = $container->appendToMetadata(array( - 'Publisher' => 'Hogarth' - )); +.. code-block:: php -If you only want to set the metadata to the local object, and not -immediately retain these values on the API, you can use a standard -setter method - which can contribute to eventual actions like an update: + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); -.. code:: php - - $container->setMetadata(array('Foo' => 'Bar')); Container quotas ---------------- -The container\_quotas middleware implements simple quotas that can be +The ``container_quotas`` middleware implements simple quotas that can be imposed on Cloud Files containers by a user. Setting container quotas can be useful for limiting the scope of containers that are delegated to non-admin users, exposed to formpost uploads, or just as a self-imposed @@ -164,19 +170,20 @@ sanity check. To set quotas for a container: -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Size; + use OpenCloud\Common\Constants\Size; - $container->setCountQuota(1000); - $container->setBytesQuota(2.5 * Size::GB); + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); And to retrieve them: -.. code:: php +.. code-block:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); - echo $container->getCountQuota(); - echo $container->getBytesQuota(); Access log delivery ------------------- @@ -186,10 +193,11 @@ access logs to analyze the number of people who access your objects, where they come from, how many requests for each object you receive, and time-based usage patterns (such as monthly or seasonal usage). -.. code:: php +.. code-block:: php + + $container->enableLogging(); + $container->disableLogging(); - $container->enableLogging(); - $container->disableLogging(); Syncing containers ------------------ @@ -198,9 +206,9 @@ You can synchronize local directories with your CloudFiles/Swift containers very easily. When you do this, the container will mirror exactly the nested file structure within your local directory: -.. code:: php +.. code-block:: php - $container->uploadDirectory('/home/Jamie/blog'); + $container->uploadDirectory('/home/user/my-blog'); There are four scenarios you should be aware of: @@ -215,4 +223,3 @@ There are four scenarios you should be aware of: +------------------------+-----------------------+----------------------+--------------------------------+ | Files does not exist | File exists | - | Remote file deleted | +------------------------+-----------------------+----------------------+--------------------------------+ - diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index e69de29bb..6b3a18b6f 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -0,0 +1,57 @@ +Object Store v1 +=============== + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now, set up the Object Store service: + +.. code-block:: php + + $service = $client->objectStoreService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + account + containers + objects + cdn + migrating-containers + access + +Glossary +-------- + +.. glossary:: + + account + The portion of the system designated for your use. An Object Store system is + typically designed to be used by many different customers, and your user + account is your portion of it. + + container + A storage compartment that provides a way for you to organize data. A + container is similar to a folder in Windows or a directory in UNIX. The + primary difference between a container and these other file system concepts + is that containers cannot be nested. + + cdn + A system of distributed servers (network) that delivers web pages and other + web content to a user based on the geographic locations of the user, the + origin of the web page, and a content delivery server. + + metadata + Optional information that you can assign to Cloud Files accounts, + containers, and objects through the use of a metadata header. + + object + An object (sometimes referred to as a file) is the unit of storage in an + Object Store. An object is a combination of content (data) and metadata. diff --git a/doc/services/object-store/Migrating.md.storage.rst b/doc/services/object-store/migrating-containers.rst similarity index 82% rename from doc/services/object-store/Migrating.md.storage.rst rename to doc/services/object-store/migrating-containers.rst index 99738554b..22adb839b 100644 --- a/doc/services/object-store/Migrating.md.storage.rst +++ b/doc/services/object-store/migrating-containers.rst @@ -1,8 +1,5 @@ -Migrating containers (across regions) -===================================== - -Introduction ------------- +Migrating containers across regions +=================================== Currently, there exists no single API operation to copy containers across geographic endpoints. Although the API offers a ``COPY`` @@ -12,6 +9,7 @@ copying. The SDK, however, does offer this functionality. You **will** be charged for bandwidth between regions, so it's advisable to use ServiceNet where possible (which is free). + Requirements ------------ @@ -20,71 +18,84 @@ Requirements requests to be batched for greater efficiency). You can do this by running: -.. code:: bash +.. code-block:: bash - php composer.phar install --dev + composer require guzzle/guzzle - Depending on the size and number of transfer items, you will need to raise PHP's memory limit: -.. code:: php +.. code-block:: php - ini_set('memory_limit', '512M'); + ini_set('memory_limit', '512M'); - You will need to enact some kind of backoff/retry strategy for rate limits. Guzzle comes with a convenient feature that just needs to be added as a normal subscriber: -.. code:: php +.. code-block:: php use Guzzle\Plugin\Backoff\BackoffPlugin; - $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + // set timeout in secs + $timeout = 10; + + // set HTTP error codes + $httpErrors = array(500, 503, 408); + + $backoffPlugin = BackoffPlugin::getExponentialBackoff($timeout, $httpErrors); + $client->addSubscriber($backoffPlugin); + This tells the client to retry up to ``10`` times for failed requests have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + Setup ----- You can access all this functionality by executing: -.. code:: php +.. code-block:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); - $ordService = $client->objectStoreService('cloudFiles', 'ORD'); - $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); - $oldContainer = $ordService->getContainer('old_container'); - $newContainer = $iadService->getContainer('new_container'); + $iadService->migrateContainer($oldContainer, $newContainer); - $iadService->migrateContainer($oldContainer, $newContainer); It's advisable to do this process in a Cloud Server in one of the two regions you're migrating to/from. This allows you to use ``privateURL`` as the third argument in the ``objectStoreService`` methods like this: -.. code:: php +.. code-block:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); - $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); This will ensure that traffic between your server and your new IAD container will be held over the internal Rackspace network which is free. + Options ------- You can pass in an array of arguments to the method: -.. code:: php +.. code-block:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); - $options = array( - 'read.batchLimit' => 100, - 'read.pageLimit' => 100, - 'write.batchLimit' => 50 - ); + $iadService->migrateContainer($oldContainer, $newContainer, $options); - $iadService->migrateContainer($oldContainer, $newContainer, $options); Options explained ~~~~~~~~~~~~~~~~~ @@ -98,4 +109,3 @@ Options explained +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ | ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | +------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ - diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst new file mode 100644 index 000000000..e38e0eb8b --- /dev/null +++ b/doc/services/object-store/objects.rst @@ -0,0 +1,427 @@ +Objects +======= + +Setup +----- + +In order to interact with this feature, you must first retrieve a particular +container using its unique name: + +.. code-block:: php + + $container = $service->getContainer('{containerName}'); + + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + +.. note:: + + Unlike previous versions, you do not need to manually specify your object's + content type. The API will do this for you. + +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in. + +Upload a single file (under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The simplest way to upload a local object, without additional metadata, is by +its path: + +.. code-block:: php + + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+')); + + +The resource handle will be automatically closed by Guzzle in its destructor, +so there is no need to execute ``fclose``. + + +Upload a single file (under 5GB) with metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although the previous section handles most use cases, there are times when you +want greater control over what is being uploaded. For example, you might want +to control the object's metadata, or supply additional HTTP headers to coerce +browsers to handle the download a certain way. To add metadata to a new object: + +.. code-block:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + // specify optional metadata + $metadata = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow', + ); + + // specify optional HTTP headers + $httpHeaders = array( + 'Content-Type' => 'application/json', + ); + + // merge the two + $allHeaders = array_merge(DataObject::stockHeaders($metadata), $httpHeaders); + + // upload as usual + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+'), $allHeaders); + + +As you will notice, the first argument to ``uploadObject`` is the remote object +name, i.e. the name it will be uploaded as. The second argument is either a +file handle resource, or a string representation of object content (a temporary +resource will be created in memory), and the third is an array of additional +headers. + + +Batch upload multiple files (each under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + + +Upload large files (over 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build and execute your +transfer. For your convenience, the Container resource object contains a simple +method to do this heavy lifting for you: + +.. code-block:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/user/video.mov', + 'metadata' => array('Author' => 'Jamie'), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifestfile is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. +To find out more details, please consult the `official documentation +`_. + + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code-block:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code-block:: php + + $container->objectList(array('prefix' => 'logFile_')); + + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code-block:: php + + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + $file = $container->getObject('summer_vacation.mp4'); + +Once you have access to this ``OpenCloud\ObjectStore\Resource\DataObject`` +object, you can access these attributes: + +Get object's parent container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file name +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file size +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $size int */ + $size = $object->getContentLength(); + + +Get content of file +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $content Guzzle\Http\EntityBody */ + $content = $object->getContainer(); + + +Get type of file +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $type string */ + $type = $object->getContentType(); + + +Get file checksum +~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $etag string */ + $etag = $object->getEtag(); + + +Get last modified date of file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $lastModified string */ + $lastModified = $object->getLastModified(); + + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code-block:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code-block:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + + +Update an existing object +------------------------- + +.. code-block:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + + +Copy object to new location +--------------------------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code-block:: php + + $object->copy('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container that does not exist yet. + + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code-block:: php + + $container->getPartialObject('summer_vacation.mp4'); + + +In order to access the metadata on a partial or complete object, use: + +.. code-block:: php + + $object->getMetadata(); + + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code-block:: php + + $object->refresh(); + + +You can also update to get the latest metadata: + +.. code-block:: php + + $object->retrieveMetadata(); + + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code-block:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code-block:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code-block:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code-block:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + + +Delete object +------------- + +.. code-block:: php + + $object->delete(); + + +Delete multiple objects +----------------------- + +Bulk delete a set of paths: + +.. code-block:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); diff --git a/doc/services/object-store/rs-only.rst b/doc/services/object-store/rs-only.rst new file mode 100644 index 000000000..fab4e423f --- /dev/null +++ b/doc/services/object-store/rs-only.rst @@ -0,0 +1,3 @@ +.. note:: + + This feature is only available to Rackspace users. From b869d8fbbd445d60d45763b70d541161d739307e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 15:29:57 +0100 Subject: [PATCH 727/835] Modify toctree --- doc/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/index.rst b/doc/index.rst index 45c453a40..9c06b86e8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,7 +10,7 @@ Contents: .. toctree:: :glob: - :maxdepth: 2 + :maxdepth: 1 services/**/index From f441dd6da5bdd7e461a6fd9f0a20708983ef42cb Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 17:58:16 +0100 Subject: [PATCH 728/835] Add orchestration docs --- doc/services/orchestration/README.md.rst | 98 --- doc/services/orchestration/USERGUIDE.md.rst | 672 ------------------ doc/services/orchestration/build-info.rst | 15 + doc/services/orchestration/events.rst | 53 ++ doc/services/orchestration/index.rst | 50 ++ doc/services/orchestration/resource-types.rst | 48 ++ doc/services/orchestration/resources.rst | 50 ++ doc/services/orchestration/stacks.rst | 299 ++++++++ doc/services/orchestration/templates.rst | 55 ++ 9 files changed, 570 insertions(+), 770 deletions(-) delete mode 100644 doc/services/orchestration/README.md.rst delete mode 100644 doc/services/orchestration/USERGUIDE.md.rst create mode 100644 doc/services/orchestration/build-info.rst create mode 100644 doc/services/orchestration/events.rst create mode 100644 doc/services/orchestration/resource-types.rst create mode 100644 doc/services/orchestration/resources.rst create mode 100644 doc/services/orchestration/stacks.rst create mode 100644 doc/services/orchestration/templates.rst diff --git a/doc/services/orchestration/README.md.rst b/doc/services/orchestration/README.md.rst deleted file mode 100644 index 1a983d187..000000000 --- a/doc/services/orchestration/README.md.rst +++ /dev/null @@ -1,98 +0,0 @@ -Orchestration -============= - -**Orchestration** is a service that can be used to create and manage -cloud resources. Examples of such resources are databases, load -balancers, servers and software installed on them. - -Concepts --------- - -To use the Orchestration service effectively, you should understand -several key concepts: - -- **Template**: An Orchestration template is a JSON or YAML document - that describes how a set of resources should be assembled to produce - a working deployment. The template specifies what resources should be - used, what attributes of these resources are parameterized and what - information is output to the user when a template is instantiated. - -- **Resource**: A resource is a template artifact that represents some - component of your desired architecture (a Cloud Server, a group of - scaled Cloud Servers, a load balancer, some configuration management - system, and so forth). - -- **Stack**: A stack is a running instance of a template. When a stack - is created, the resources specified in the template are created. - -Getting started ---------------- - -1. Instantiate an OpenStack or Rackspace client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use the Orchestration service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -2. Obtain an Orchestration service object from the client. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All Orchestration operations are done via an *orchestration service -object*. To instantiate this object, call the ``orchestrationService`` -method on the ``$client`` object as shown in the following example: - -.. code:: php - - $region = ''; - $orchestrationService = $client->orchestrationService(null, $region); - -Any stacks and resources created with this ``$orchestrationService`` -instance will be stored in the cloud region specified by ``$region``. - -3. Create a stack from a template. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: php - - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -[ `Get the executable PHP script for this -example `__ ] - -Next steps ----------- - -Once you have created a stack, there is more you can do with it. See -`complete user guide for orchestration `__. diff --git a/doc/services/orchestration/USERGUIDE.md.rst b/doc/services/orchestration/USERGUIDE.md.rst deleted file mode 100644 index 98bd51db7..000000000 --- a/doc/services/orchestration/USERGUIDE.md.rst +++ /dev/null @@ -1,672 +0,0 @@ -Complete User Guide for the Orchestration Service -================================================= - -Orchestration is a service that you can use to create and manage cloud -resources such as databases, load balancers, and servers, and the -software installed on servers. - -Table of Contents ------------------ - -- `Concepts <#concepts>`__ -- `Prerequisites <#prerequisites>`__ -- `Client <#client>`__ -- `Orchestration service <#orchestration-service>`__ -- `Templates <#templates>`__ -- `Validate template <#validate-template>`__ - - - `Validate a template from a - file <#validate-a-template-from-a-file>`__ - - `Validate Template from URL <#validate-template-from-url>`__ - -- `Stacks <#stacks>`__ -- `Preview stack <#preview-stack>`__ - - - `Preview a stack from a template - file <#preview-a-stack-from-a-template-file>`__ - - `Preview a stack from a template - URL <#preview-a-stack-from-a-template-url>`__ - -- `Create stack <#create-stack>`__ - - - `Create a stack from a template - file <#create-a-stack-from-a-template-file>`__ - - `Create a stack from a template - URL <#create-a-stack-from-a-template-url>`__ - -- `List stacks <#list-stacks>`__ -- `Get stack <#get-stack>`__ -- `Get stack template <#get-stack-template>`__ -- `Update stack <#update-stack>`__ - - - `Update a stack from a template - file <#update-a-stack-from-a-template-file>`__ - - `Update Stack from Template - URL <#update-stack-from-template-url>`__ - -- `Delete stack <#delete-stack>`__ -- `Abandon Stack <#abandon-stack>`__ -- `Adopt stack <#adopt-stack>`__ -- `Stack resources <#stack-resources>`__ -- `List stack resources <#list-stack-resources>`__ -- `Get stack resource <#get-stack-resource>`__ -- `Get stack resource metadata <#get-stack-resource-metadata>`__ -- `Stack resource events <#stack-resource-events>`__ -- `List stack events <#list-stack-events>`__ -- `List stack resource events <#list-stack-resource-events>`__ -- `Get stack resource event <#get-stack-resource-event>`__ -- `Resource types <#resource-types>`__ -- `List resource types <#list-resource-types>`__ -- `Get resource type <#get-resource-type>`__ -- `Get resource type template <#get-resource-type-template>`__ -- `Build info <#build-info>`__ -- `Get build info <#get-build-info>`__ - -Concepts --------- - -To use the Orchestration service effectively, you should understand the -following key concepts: - -- **Template**: A JSON or YAML document that describes how a set of - resources should be assembled to produce a working deployment. The - template specifies the resources to use, the attributes of these - resources that are parameterized and the information that is sent to - the user when a template is instantiated. - -- **Resource**: Some component of your architecture (a cloud server, a - group of scaled cloud servers, a load balancer, some configuration - management system, and so on) that is defined in a template. - -- **Stack**: A running instance of a template. When a stack is created, - the resources specified in the template are created. - -Prerequisites -------------- - -Client -~~~~~~ - -To use the Orchestration service, you must first instantiate a -``OpenStack`` or ``Rackspace`` client object. - -- If you are working with an OpenStack cloud, instantiate an - ``OpenCloud\OpenStack`` client as follows: - - .. code:: php - - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - -- If you are working with the Rackspace cloud, instantiate a - ``OpenCloud\Rackspace`` client as follows: - - .. code:: php - - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - -Orchestration service -~~~~~~~~~~~~~~~~~~~~~ - -All Orchestration operations are done via an *orchestration service -object*. To instantiate this object, call the ``orchestrationService`` -method on the ``$client`` object. This method takes two arguments: - -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ -| Position | Description | Data type | Required? | Default value | Example value | -+============+=============================================================+=============+=============+====================================================+==========================+ -| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ -| 2 | Cloud region | String | Yes | - | ``DFW`` | -+------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ - -.. code:: php - - $region = ''; - $orchestrationService = $client->orchestrationService(null, $region); - -Any stacks and resources created with this ``$orchestrationService`` -instance will be stored in the cloud region specified by ``$region``. - -Templates ---------- - -An Orchestration template is a JSON or YAML document that describes how -a set of resources should be assembled to produce a working deployment -(known as a `stack <#stacks>`__). The template specifies the resources -to use, the attributes of these resources that are parameterized and the -information that is sent to the user when a template is instantiated. - -Validate template -~~~~~~~~~~~~~~~~~ - -Before you use a template to create a stack, you might want to validate -it. - -Validate a template from a file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can validate it as shown in the following example: - -.. code:: php - - use OpenCloud\Common\Exceptions\InvalidTemplateError; - - try { - $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') - )); - } catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid - } - -[ `Get the executable PHP script for this -example `__ -] - -Validate Template from URL -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can validate it as shown in the -following example: - -.. code:: php - - use OpenCloud\Common\Exceptions\InvalidTemplateError; - - try { - $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' - )); - } catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid - } - -[ `Get the executable PHP script for this -example `__ -] - -Stacks ------- - -A stack is a running instance of a template. When a stack is created, -the `resources <#stack-resources>`__ specified in the template are -created. - -Preview stack -~~~~~~~~~~~~~ - -Before you create a stack from a template, you might want to see what -that stack will look like. This is called *previewing the stack*. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Preview a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to preview a stack as shown in the following -example: - -.. code:: php - - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Preview a stack from a template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to preview a stack as shown -in the following example: - -.. code:: php - - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Create stack -~~~~~~~~~~~~ - -You can create a stack from a template. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Create a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to create a stack as shown in the following -example: - -.. code:: php - - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Create a stack from a template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to create a stack as shown -in the following example: - -.. code:: php - - $stack = $orchestrationService->stack(); - $stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -[ `Get the executable PHP script for this -example `__ ] - -List stacks -~~~~~~~~~~~ - -You can list all the stacks that you have created as shown in the -following example: - -.. code:: php - - $stacks = $orchestrationService->listStacks(); - foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack -~~~~~~~~~ - -You can retrieve a specific stack using its name, as shown in the -following example: - -.. code:: php - - $stack = $orchestrationService->getStack('simple-lamp-setup'); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get stack template -~~~~~~~~~~~~~~~~~~ - -You can retrieve the template used to create a stack. Note that a JSON -string is returned, regardless of whether a JSON or YAML template was -used to create the stack. - -.. code:: php - - $stackTemplate = $stack->getTemplate(); - /** @var $stackTemplate string **/ - -[ `Get the executable PHP script for this -example `__ ] - -Update stack -~~~~~~~~~~~~ - -You can update a running stack. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ - -Update a stack from a template file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to update a stack as shown in the following -example: - -.. code:: php - - $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ -] - -Update Stack from Template URL -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to update a stack as shown -in the following example: - -.. code:: php - - $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Delete stack -~~~~~~~~~~~~ - -If you no longer need a stack and all its resources, you can delete the -stack *and* the resources as shown in the following example: - -.. code:: php - - $stack->delete(); - -[ `Get the executable PHP script for this -example `__ ] - -Abandon Stack -~~~~~~~~~~~~~ - -If you want to delete a stack but preserve all its resources, you can -abandon the stack as shown in the following example: - -.. code:: php - - $abandonStackData = $stack->abandon(); - /** @var $abandonStackData string **/ - - file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); - -[ `Get the executable PHP script for this -example `__ ] - -Note that this operation returns data about the abandoned stack as a -string. You can use this data to recreate the stack by using the `adopt -stack <#adopt-stack>`__ operation. - -Adopt stack -~~~~~~~~~~~ - -If you have data from an abandoned stack, you can re-create the stack as -shown in the following example: - -.. code:: php - - $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 - )); - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - -[ `Get the executable PHP script for this -example `__ ] - -Stack resources ---------------- - -A stack is made up of zero or more resources such as databases, load -balancers, and servers, and the software installed on servers. - -List stack resources -~~~~~~~~~~~~~~~~~~~~ - -You can list all the resources for a stack as shown in the following -example: - -.. code:: php - - $resources = $stack->listResources(); - foreach ($resources as $resource) { - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource -~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource in a stack bt using that resource's -name, as shown in the following example: - -.. code:: php - - $resource = $stack->getResource('load-balancer'); - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve the metadata for a specific resource in a stack as -shown in the following example: - -.. code:: php - - $resourceMetadata = $resource->getMetadata(); - /** @var $resourceMetadata \stdClass **/ - -[ `Get the executable PHP script for this -example `__ ] - -Stack resource events ---------------------- - -Operations on resources within a stack (such as the creation of a -resource) produce events. - -List stack events -~~~~~~~~~~~~~~~~~ - -You can list all of the events for all of the resources in a stack as -shown in the following example: - -.. code:: php - - $stackEvents = $stack->listEvents(); - foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -List stack resource events -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can list all of the events for a specific resource in a stack as -shown in the following example: - -.. code:: php - - $resourceEvents = $resource->listEvents(); - foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get stack resource event -~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific event for a specific resource in a stack, by -using the resource event's ID, as shown in the following example: - -.. code:: php - - $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ - -[ `Get the executable PHP script for this -example `__ ] - -Resource types --------------- - -When you define a template, you must use resource types supported by -your cloud. - -List resource types -~~~~~~~~~~~~~~~~~~~ - -You can list all supported resource types as shown in the following -example: - -.. code:: php - - $resourceTypes = $orchestrationService->listResourceTypes(); - foreach ($resourceTypes as $resourceType) { - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ - } - -[ `Get the executable PHP script for this -example `__ ] - -Get resource type -~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource type's schema as shown in the -following example: - -.. code:: php - - $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ - -[ `Get the executable PHP script for this -example `__ ] - -Get resource type template -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can retrieve a specific resource type's representation as it would -appear in a template, as shown in the following example: - -.. code:: php - - $resourceTypeTemplate = $resourceType->getTemplate(); - /** @var $resourceTypeTemplate string **/ - -[ `Get the executable PHP script for this -example `__ ] - -Build info ----------- - -Get build info -~~~~~~~~~~~~~~ - -You can retrieve information about the current Orchestration service -build as shown in the following example: - -.. code:: php - - $buildInfo = $orchestrationService->getBuildInfo(); - /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ - -[ `Get the executable PHP script for this -example `__ ] diff --git a/doc/services/orchestration/build-info.rst b/doc/services/orchestration/build-info.rst new file mode 100644 index 000000000..c30ce5cfa --- /dev/null +++ b/doc/services/orchestration/build-info.rst @@ -0,0 +1,15 @@ +Build info +========== + +Get build info +-------------- + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + $buildInfo = $orchestrationService->getBuildInfo(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/events.rst b/doc/services/orchestration/events.rst new file mode 100644 index 000000000..68dbf611b --- /dev/null +++ b/doc/services/orchestration/events.rst @@ -0,0 +1,53 @@ +Stack resource events +===================== + +Operations on resources within a stack (such as the creation of a +resource) produce events. + + +List stack events +----------------- + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code-block:: php + + $stackEvents = $stack->listEvents(); + + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +List stack resource events +-------------------------- + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + $resourceEvents = $resource->listEvents(); + + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource event +------------------------ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code-block:: php + + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index e69de29bb..0dce8ac43 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -0,0 +1,50 @@ +Orchestration v1 +================ + +Setup +----- + +.. include:: rs-client.sample.rst + +Now set up the Orchestration service: + +.. code-block:: php + + $service = $client->orchestrationService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + templates + stacks + resources + resource-types + build-info + + +Glossary +-------- + +.. glossary:: + + template + An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + + resource + A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + + stack + A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. diff --git a/doc/services/orchestration/resource-types.rst b/doc/services/orchestration/resource-types.rst new file mode 100644 index 000000000..cd229d660 --- /dev/null +++ b/doc/services/orchestration/resource-types.rst @@ -0,0 +1,48 @@ +Resource types +============== + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +------------------- + +You can list all supported resource types as shown in the following +example: + +.. code-block:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +`Get the executable PHP script for this example `_ + + +Get resource type +----------------- + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + +`Get the executable PHP script for this example `_ + + +Get resource type template +-------------------------- + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code-block:: php + + /** @var $resourceTypeTemplate string **/ + $resourceTypeTemplate = $resourceType->getTemplate(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resources.rst b/doc/services/orchestration/resources.rst new file mode 100644 index 000000000..049150f58 --- /dev/null +++ b/doc/services/orchestration/resources.rst @@ -0,0 +1,50 @@ +Stack resources +=============== + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + + +List stack resources +-------------------- + +You can list all the resources for a stack as shown in the following +example: + +.. code-block:: php + + $resources = $stack->listResources(); + + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource +------------------ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code-block:: php + + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + $resource = $stack->getResource('load-balancer'); + +`Get the executable PHP script for this example `_ + + +Get stack resource metadata +--------------------------- + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + /** @var $resourceMetadata \stdClass **/ + $resourceMetadata = $resource->getMetadata(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst b/doc/services/orchestration/stacks.rst new file mode 100644 index 000000000..75a14cd4c --- /dev/null +++ b/doc/services/orchestration/stacks.rst @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/templates.rst b/doc/services/orchestration/templates.rst new file mode 100644 index 000000000..5eca2519d --- /dev/null +++ b/doc/services/orchestration/templates.rst @@ -0,0 +1,55 @@ +Templates +========= + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validating templates +-------------------- + +Before you use a template to create a stack, you might want to validate it. + + +Validate a template from a file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ + +Validate Template from URL +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ From 4cd5f9424c4c72571479b7c484715d83acc751e1 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 5 Mar 2015 19:12:57 +0100 Subject: [PATCH 729/835] Add Queues docs --- doc/services/queues/Queue.md.rst | 197 ------------- .../queues/{Claim.md.rst => claims.rst} | 115 +++----- doc/services/queues/index.rst | 48 +++ .../queues/{Message.md.rst => messages.rst} | 274 +++++++----------- doc/services/queues/queues.rst | 124 ++++++++ 5 files changed, 316 insertions(+), 442 deletions(-) delete mode 100644 doc/services/queues/Queue.md.rst rename doc/services/queues/{Claim.md.rst => claims.rst} (68%) rename doc/services/queues/{Message.md.rst => messages.rst} (52%) create mode 100644 doc/services/queues/queues.rst diff --git a/doc/services/queues/Queue.md.rst b/doc/services/queues/Queue.md.rst deleted file mode 100644 index 1fe9f7036..000000000 --- a/doc/services/queues/Queue.md.rst +++ /dev/null @@ -1,197 +0,0 @@ -1. Introduction ---------------- - -A Queue is an entity that holds messages. Ideally, a queue is created -per work type. For example, if you want to compress files, you would -create a queue dedicated to this job. Any application that reads from -this queue would only compress files. - -2. Setup --------- - -.. code:: php - - $service = $client->queuesService('cloudQueues', 'ORD'); - -3. Client IDs -------------- - -With most of Marconi's operation, you must specify a **Client ID** which -will be used as a unique identifier for the process accessing this -Queue. This is basically a UUID that must be unique to each client -accessing the API - it can be an arbitrary string. - -.. code:: php - - $service->setClientId(); - - echo $service->getClientId(); - -If you call ``setClientId`` without any parameters, a UUID is -automatically generated for you. - -4. List queues --------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation lists queues for the project. The queues are sorted -alphabetically by name. - -4.2 Parameters -~~~~~~~~~~~~~~ - -\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| -\|marker\|Query\|​String\|Specifies the name of the last queue received -in a previous request, or none to get the first page of results. -Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to -return. The default value for the number of queues returned is 10. If -you do not specify this parameter, the default number of queues is -returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether -queue metadata is included in the response. The default value for this -parameter is false, which excludes the metadata. Optional.\| -\|----\|-----\|----\|-----------\| - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queues = $service->listQueues(); - - while ($queue = $queues->next()) { - echo $queue->getName() . PHP_EOL; - } - -5. Create queue ---------------- - -5.1 Description -~~~~~~~~~~~~~~~ - -This operation creates a new queue. - -5.2 Parameters -~~~~~~~~~~~~~~ - -A string representation of the name for your new Queue. The name must -not exceed 64 bytes in length, and it is limited to US-ASCII letters, -digits, underscores, and hyphens. - -5.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue = $service->createQueue('new_queue'); - -6. Retrieve queue ------------------ - -6.1 Description -~~~~~~~~~~~~~~~ - -Returns a ``Queue`` object for use. - -6.2 Parameters -~~~~~~~~~~~~~~ - -Queue name. - -6.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue = $service->getQueue('new_queue'); - -7. Check queue existence ------------------------- - -7.1 Description -~~~~~~~~~~~~~~~ - -This operation verifies whether the specified queue exists by returning -``TRUE`` or ``FALSE``. - -7.2 Parameters -~~~~~~~~~~~~~~ - -7.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - if ($service->hasQueue('new_queue')) { - // do something - } - -8. Update queue metadata (permanently to the API) -------------------------------------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation replaces any existing metadata document in its entirety. -Ensure that you do not accidentally overwrite existing metadata that you -want to retain. If you want to *append* metadata, ensure you merge a new -array to the existing values. - -4.2 Parameters -~~~~~~~~~~~~~~ - -Hash of key pairs. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue->saveMetadata(array( - 'foo' => 'bar' - )); - -9. Retrieve the queue metadata (fresh from the API) ---------------------------------------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation returns metadata, such as message TTL, for the queue. - -4.2 Parameters -~~~~~~~~~~~~~~ - -None. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $metadata = $queue->retrieveMetadata(); - - print_r($metadata->toArray()); - -10. Get queue stats -------------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation returns queue statistics, including how many messages are -in the queue, categorized by status. - -4.2 Parameters -~~~~~~~~~~~~~~ - -None. - -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $queue->getStats(); - diff --git a/doc/services/queues/Claim.md.rst b/doc/services/queues/claims.rst similarity index 68% rename from doc/services/queues/Claim.md.rst rename to doc/services/queues/claims.rst index 42161536d..7e375c8d3 100644 --- a/doc/services/queues/Claim.md.rst +++ b/doc/services/queues/claims.rst @@ -1,28 +1,18 @@ -1. Introduction ---------------- +Claims +====== -A **Claim** is the process of a worker checking out a message to perform -a task. Claiming a message prevents other workers from attempting to -process the same messages. +Setup +----- -2. Setup --------- +In order to work with messages, you must first retrieve a queue by its name: -A Claim is initialized on its parent object, a Queue: +.. code-block:: php -.. code:: php + $queue = $service->getQueue('{queueName}'); - // To initialize an empty object: - $claim = $queue->getClaim(); - // or retrieve a specific claim: - $claim = $queue->getClaim('51db7067821e727dc24df754'); - -3. Claim messages ------------------ - -3.1 Description -~~~~~~~~~~~~~~~ +Claim messages +-------------- This operation claims a set of messages (up to the value of the limit parameter) from oldest to newest and skips any messages that are already @@ -46,8 +36,8 @@ and whether a given message's claim is about to expire. When a claim expires, it is released. If the original worker failed to process the message, another client worker can then claim the message. -3.2 Attributes -~~~~~~~~~~~~~~ +Parameters +~~~~~~~~~~ The ``ttl`` attribute specifies how long the server waits before releasing the claim. The ttl value must be between 60 and 43200 seconds @@ -67,46 +57,30 @@ The ``limit`` attribute specifies the number of messages to return, up to 20 messages. If limit is not specified, limit defaults to 10. The limit parameter is optional. -3.3 Code -~~~~~~~~ +.. code-block:: php -.. code:: php + use OpenCloud\Common\Constants\Datetime; - use OpenCloud\Common\Constants\Datetime; + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); - $queue->claimMessages(array( - 'limit' => 15, - 'grace' => 5 * Datetime::MINUTE, - 'ttl' => 5 * Datetime::MINUTE - )); - -4. Query claim --------------- - -4.1 Description -~~~~~~~~~~~~~~~ - -This operation queries the specified claim for the specified queue. -Claims with malformed IDs or claims that are not found by ID are -ignored. -4.2 Attributes -~~~~~~~~~~~~~~ +Query claim +----------- -Claim ID. +This operation queries the specified claim for the specified queue. Claims with +malformed IDs or claims that are not found by ID are ignored. -4.3 Code -~~~~~~~~ +.. code-block:: php -.. code:: php + $claim = $queue->getClaim('{claimId}'); - $claim = $queue->getClaim('51db7067821e727dc24df754'); -5. Update claim ---------------- - -5.1 Description -~~~~~~~~~~~~~~~ +Update claim +------------ This operation updates the specified claim for the specified queue. Claims with malformed IDs or claims that are not found by ID are @@ -118,27 +92,17 @@ renew a claim by executing this method on a specific **Claim** and including a new TTL. The API will then reset the age of the claim and apply the new TTL. -5.2 Attributes -~~~~~~~~~~~~~~ - -See section 4.2. - -5.3 Code -~~~~~~~~ - -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $claim->update(array( - 'ttl' => 10 * Datetime::MINUTE - )); + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); -6. Release claim ----------------- -6.1 Description -~~~~~~~~~~~~~~~ +Release claim +------------- This operation immediately releases a claim, making any remaining undeleted messages that are associated with the claim available to other @@ -150,15 +114,6 @@ shutdown, fails to process one or more messages, or is taking longer than expected to process messages, and wants to make the remainder of the messages available to other workers. -6.2 Attributes -~~~~~~~~~~~~~~ - -See section 4.2. - -6.3 Code -~~~~~~~~ - -.. code:: php - - $message->delete(); +.. code-block:: php + $message->delete(); diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index e69de29bb..e37845d76 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -0,0 +1,48 @@ +Queues v1 +========= + +Setup +----- + +.. include:: ../common/rs-client.sample.rst + +Now to instantiate the Queues service: + +.. code-block:: php + + $service = $client->queuesService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + queues + messages + claims + + +Glossary +-------- + +.. glossary:: + + claim + A Claim is the process of a worker checking out a message to perform + a task. Claiming a message prevents other workers from attempting to + process the same messages. + + queue + A Queue is an entity that holds messages. Ideally, a queue is created + per work type. For example, if you want to compress files, you would + create a queue dedicated to this job. Any application that reads from + this queue would only compress files. + + message + A Message is a task, a notification, or any meaningful data that a + producer or publisher sends to the queue. A message exists until it is + deleted by a recipient or automatically by the system based on a TTL + (time-to-live) value. diff --git a/doc/services/queues/Message.md.rst b/doc/services/queues/messages.rst similarity index 52% rename from doc/services/queues/Message.md.rst rename to doc/services/queues/messages.rst index ca67e22bb..ea23caea3 100644 --- a/doc/services/queues/Message.md.rst +++ b/doc/services/queues/messages.rst @@ -1,29 +1,18 @@ -1. Introduction ---------------- +Messages +======== -A **Message** is a task, a notification, or any meaningful data that a -producer or publisher sends to the queue. A message exists until it is -deleted by a recipient or automatically by the system based on a TTL -(time-to-live) value. +Setup +----- -2. Setup --------- +In order to work with messages, you must first retrieve a queue by its name: -A message is initialized from its parent object, a Queue: +.. code-block:: php -.. code:: php + $queue = $service->getQueue('{queueName}'); - // Setup an empty object - $message = $queue->getMessage(); - // or retrieve an existing one - $message = $queue->getMessage(''); - -3. Post message ---------------- - -3.1 Description -~~~~~~~~~~~~~~~ +Post new message +---------------- This operation posts the specified message or messages. You can submit up to 10 messages in a single request. @@ -31,73 +20,67 @@ up to 10 messages in a single request. When posting new messages, you specify only the ``body`` and ``ttl`` for the message. The API will insert metadata, such as ID and age. -3.2 Parameters -~~~~~~~~~~~~~~ - How you pass through the array structure depends on whether you are -executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the -same. +executing multiple or single posts, but the keys are the +same: -The ``body`` attribute specifies an arbitrary document that constitutes -the body of the message being sent. The size of this body is limited to -256 KB, excluding whitespace. +* The ``body`` attribute specifies an arbitrary document that constitutes + the body of the message being sent. The size of this body is limited to + 256 KB, excluding whitespace. -The ``ttl`` attribute specifies how long the server waits before marking -the message as expired and removing it from the queue. The value of ttl -must be between 60 and 1209600 seconds (14 days). Note that the server -might not actually delete the message until its age has reached up to -(ttl + 60) seconds, to allow for flexibility in storage implementations. +* The ``ttl`` attribute specifies how long the server waits before marking + the message as expired and removing it from the queue. The value of ttl + must be between 60 and 1209600 seconds (14 days). Note that the server + might not actually delete the message until its age has reached up to + (ttl + 60) seconds, to allow for flexibility in storage implementations. -3.3 Code samples -~~~~~~~~~~~~~~~~ -3.3.1 Posting a single message -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Posting a single message +~~~~~~~~~~~~~~~~~~~~~~~~ -:: +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $queue->createMessage(array( - 'body' => (object) array( - 'event' => 'BackupStarted', - 'deadline' => '26.12.2013 - ), - 'ttl' => 2 * Datetime::DAY - )); + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013', + ), + 'ttl' => 2 * Datetime::DAY + )); -3.3.2 Post a batch of messages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Post a batch of messages +~~~~~~~~~~~~~~~~~~~~~~~~ Please note that the list of messages will be truncated at 10. For more, please execute another method call. -.. code:: php +.. code-block:: php - use OpenCloud\Common\Constants\Datetime; + use OpenCloud\Common\Constants\Datetime; - $messages = array( - array( - 'body' => (object) array( - 'play' => 'football' - ), - 'ttl' => 2 * Datetime::DAY - ), - array( - 'body' => (object) array( - 'play' => 'tennis' - ), - 'ttl' => 50 * Datetime::HOUR - ) - ); + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); - $queue->createMessages($messages); + $queue->createMessages($messages); -4. Get messages ---------------- -4.1 Description -~~~~~~~~~~~~~~~ +Get messages +------------ This operation gets the message or messages in the specified queue. @@ -109,10 +92,11 @@ variety of storage driver implementations. Results are ordered by age, oldest message first. -4.2 Parameters -~~~~~~~~~~~~~~ -A hash of options. +Parameters +~~~~~~~~~~ + +When retrieving messages, you can filter using these options: +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Name | Style | Type | Description | @@ -123,135 +107,95 @@ A hash of options. +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | +| include_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | +--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -4.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $messages = $queue->listMessages(array( - 'marker' => '51db6f78c508f17ddc924357', - 'limit' => 20, - 'echo' => true - )); + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); - while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; - } + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } -5. Get a set of messages by ID ------------------------------- -5.1 Description -~~~~~~~~~~~~~~~ +Get a set of messages by ID +--------------------------- This operation provides a more efficient way to query multiple messages compared to using a series of individual GET. Note that the list of IDs cannot exceed 20. If a malformed ID or a nonexistent message ID is provided, it is ignored, and the remaining messages are returned. -5.2 Parameters -~~~~~~~~~~~~~~ - -A hash of options. +Parameters +~~~~~~~~~~ -\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| -\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format -multiple message ID values by separating them with commas -(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies -the claim ID with which the message is associated. Optional.\| -\|----\|-----\|----\|-----------\| ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++============+=========+============+========================================================================================================================================+ +| ids | Query | String | Specifies the IDs of the messages to get. Format multiple message ID values by separating them with commas (comma-separated). Optional | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| claim_id | Query | ​Boolean | Specifies the claim ID with which the message is associated. Optional. | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ -5.3 Code sample -~~~~~~~~~~~~~~~ -.. code:: php +.. code-block:: php - $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + $ids = array('id_1', 'id_2'); - $messages = $queue->listMessages(array('ids' => $ids)); + $messages = $queue->listMessages(array('ids' => $ids)); - while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; - } + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } -6. Delete a set of messages by ID ---------------------------------- -6.1 Description -~~~~~~~~~~~~~~~ +Delete a set of messages by ID +------------------------------ This operation immediately deletes the specified messages. If any of the message IDs are malformed or non-existent, they are ignored. The remaining valid messages IDs are deleted. -6.2 Parameters -~~~~~~~~~~~~~~ - -An array of IDs. - -6.3 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php +.. code-block:: php - $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + $ids = array('id_1', 'id_2'); + $response = $queue->deleteMessages($ids); - $response = $queue->deleteMessages($ids); -7. Get a specific message -------------------------- - -7.1 Description -~~~~~~~~~~~~~~~ +Get a specific message +---------------------- This operation gets the specified message from the specified queue. -7.2 Parameters -~~~~~~~~~~~~~~ - -Message ID. - -7.3 Object properties -~~~~~~~~~~~~~~~~~~~~~ - -``href`` is an opaque relative URI that the client can use to uniquely -identify a message resource and interact with it. - -``ttl`` is the TTL that was set on the message when it was posted. The -message expires after (ttl - age) seconds. - -``age`` is the number of seconds relative to the server's clock. - -``body`` is the arbitrary document that was submitted with the original -request to post the message. - -7.4 Code sample -~~~~~~~~~~~~~~~ - -.. code:: php - - $message = $queue->getMessage('51db6f78c508f17ddc924357'); - -8. Delete message ------------------ +.. code-block:: php -8.1 Description -~~~~~~~~~~~~~~~ + /** @var $message OpenCloud\Queues\Message */ + $message = $queue->getMessage('{messageId}'); -This operation immediately deletes the specified message. -8.2 Parameters -~~~~~~~~~~~~~~ +Once you have access to the ``Message`` object, you access its attributes: -None. ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| attribute | method | description | ++===========+=============+==============================================================================================================+ +| href | ``getHref`` | An opaque relative URI that the client can use to uniquely identify a message resource and interact with it. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| ttl | ``getTtl`` | The TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| age | ``getAge`` | The number of seconds relative to the server's clock. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| body | ``getBody`` | The arbitrary document that was submitted with the original request to post the message. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ -8.3 Code sample -~~~~~~~~~~~~~~~ -.. code:: php +Delete message +-------------- - $message->delete(); +.. code-block:: php + $message->delete(); diff --git a/doc/services/queues/queues.rst b/doc/services/queues/queues.rst new file mode 100644 index 000000000..7958adfae --- /dev/null +++ b/doc/services/queues/queues.rst @@ -0,0 +1,124 @@ +Queues +====== + +A note on Client IDs +-------------------- + +For most of the operations in Cloud Queues, you must specify a **Client ID** +which will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code-block:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + + +List queues +----------- + +This operation lists queues for the project. The queues are sorted alphabetically by name. + +.. code-block:: php + + $queues = $service->listQueues(); + + foreach ($queues as $queue) { + echo $queue->getName() , PHP_EOL; + } + + +Filtering lists +~~~~~~~~~~~~~~~ + +You can also filter collections using the following query parameters: + ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++==========+=======+=========+================================================================================================================================================================================================+ +| marker | Query | ​String | Specifies the name of the last queue received in a previous request, or none to get the first page of results. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | Specifies the number of queues to return. The default value for the number of queues returned is 10. If you do not specify this parameter, the default number of queues is returned. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| detailed | Query | ​Boolean | Determines whether queue metadata is included in the response. The default value for this parameter is false, which excludes the metadata. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. code-block:: php + + $queues = $service->listQueues(array('detailed' => false)); + + +Create queue +------------ + +The only parameter required is the name of the queue you're creating. The name +must not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +.. code-block:: php + + $queue = $service->createQueue('new_queue'); + + +Find queue details +------------------ + +.. code-block:: php + + /** @var $queue OpenCloud\Queues\Resource\Queues */ + $queue = $service->getQueue('{name}'); + + +Check queue existence +--------------------- + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +.. code-block:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + + +Update queue metadata +--------------------- + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +.. code-block:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + + +Retrieve the queue metadata +--------------------------- + +This operation returns metadata, such as message TTL, for the queue. + +.. code-block:: php + + $metadata = $queue->retrieveMetadata(); + print_r($metadata->toArray()); + + +Get queue stats +--------------- + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +.. code-block:: php + + $queue->getStats(); From 26f3d6fc5a2c14efdd51b3ba383a30b2ec0f2bc2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:20:43 -0800 Subject: [PATCH 730/835] Fix what properties can be updated. --- lib/OpenCloud/Networking/Resource/Network.php | 4 +--- lib/OpenCloud/Networking/Resource/Port.php | 6 +----- tests/OpenCloud/Smoke/Unit/Networking.php | 8 +------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/Network.php b/lib/OpenCloud/Networking/Resource/Network.php index 53716a8f0..19454445e 100644 --- a/lib/OpenCloud/Networking/Resource/Network.php +++ b/lib/OpenCloud/Networking/Resource/Network.php @@ -57,9 +57,7 @@ class Network extends PersistentResource implements NetworkInterface ); protected $updateKeys = array( - 'adminStateUp', - 'name', - 'shared' + 'name' ); /** diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index 484de675e..bc7ee4dd0 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -78,11 +78,7 @@ class Port extends PersistentResource protected $updateKeys = array( 'name', - 'adminStateUp', - 'deviceId', - 'deviceOwner', - 'fixedIps', - 'securityGroups' + 'deviceId' ); /** diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index b6ab4592e..2518119bd 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -245,13 +245,7 @@ protected function testPortOperations() $this->step('Update port'); $port->update(array( - 'name' => 'updated_test_port', - 'fixedIps' => array( - array( - 'subnetId' => $subnet1->getId(), - 'ipAddress' => '192.168.62.17' - ) - ) + 'name' => 'updated_test_port' )); } From 1e2850ef30aa19582ab4245a43591bfe0c3e89a9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:12 -0800 Subject: [PATCH 731/835] Fix variable names. --- tests/OpenCloud/Smoke/Unit/Networking.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 2518119bd..7b424c46f 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -252,7 +252,7 @@ protected function testPortOperations() protected function testSecurityGroupOperations() { $this->step('Create security group'); - $security group = $this->getService()->createSecurityGroup(array( + $securityGroup = $this->getService()->createSecurityGroup(array( 'name' => 'new-webservers', 'description' => 'security group for webservers' )); @@ -269,7 +269,7 @@ protected function testSecurityGroupOperations() } $this->step('Get security group'); - $security group = $this->getService()->getSecurityGroup($securityGroup->getId()); + $securityGroup = $this->getService()->getSecurityGroup($securityGroup->getId()); $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); } @@ -282,7 +282,7 @@ protected function testSecurityGroupRuleOperations() $this->cleanupSecurityGroupIds[] = $securityGroup1->getId(); $this->step('Create security group rule'); - $security group rule = $this->getService()->createSecurityGroupRule(array( + $securityGroupRule = $this->getService()->createSecurityGroupRule(array( 'securityGroupId' => $securityGroup1->getId(), 'direction' => 'egress', 'ethertype' => 'IPv4', From 073aef3ae0cb7aecaa8ff29f90ff05d1a63799f6 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:27 -0800 Subject: [PATCH 732/835] Only ingress rules are currently allowed. --- tests/OpenCloud/Smoke/Unit/Networking.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 7b424c46f..38d903ff0 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -284,7 +284,7 @@ protected function testSecurityGroupRuleOperations() $this->step('Create security group rule'); $securityGroupRule = $this->getService()->createSecurityGroupRule(array( 'securityGroupId' => $securityGroup1->getId(), - 'direction' => 'egress', + 'direction' => 'ingress', 'ethertype' => 'IPv4', 'portRangeMin' => 80, 'portRangeMax' => 80, From aed62d9c20931085bc4cc57f11f7d25f8db7074b Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:41 -0800 Subject: [PATCH 733/835] Remote groups are not currently allowed. --- tests/OpenCloud/Smoke/Unit/Networking.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 38d903ff0..deaefda33 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -288,8 +288,7 @@ protected function testSecurityGroupRuleOperations() 'ethertype' => 'IPv4', 'portRangeMin' => 80, 'portRangeMax' => 80, - 'protocol' => 'tcp', - 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + 'protocol' => 'tcp' )); $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); From 14099cc13887f6066fcf83fc5d175a3da86649dc Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:21:55 -0800 Subject: [PATCH 734/835] Security group rules don't have names. s/name/direction/. --- tests/OpenCloud/Smoke/Unit/Networking.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index deaefda33..8ccd30a94 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -296,16 +296,16 @@ protected function testSecurityGroupRuleOperations() $this->step('List security group rules'); $securityGroupRules = $this->getService()->listSecurityGroupRules(); - $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule name'); + $this->stepInfo('%-40s | %s', 'Security Group Rule ID', 'Security Group Rule direction'); $this->stepInfo('%-40s | %s', str_repeat('-', 40), str_repeat('-', 40)); foreach ($securityGroupRules as $securityGroupRule) { - $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getName()); + $this->stepInfo('%-40s | %s', $securityGroupRule->getId(), $securityGroupRule->getDirection()); } $this->step('Get security group rule'); - $security group rule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); + $securityGroupRule = $this->getService()->getSecurityGroupRule($securityGroupRule->getId()); $this->stepInfo('Security Group Rule ID: ' . $securityGroupRule->getId()); - $this->stepInfo('Security Group Rule Name: ' . $securityGroupRule->getName()); + $this->stepInfo('Security Group Rule Direction: ' . $securityGroupRule->getDirection()); } public function teardown() From 623f5cc9936efa0fa027599b519e2313a07ae674 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Thu, 5 Mar 2015 17:38:43 -0800 Subject: [PATCH 735/835] Appeasing the linter. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 +- lib/OpenCloud/Networking/Resource/SecurityGroupRule.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index 105fc22a1..2892f5471 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * + * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * * @package OpenCloud\Networking\Resource diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php index 3659d08ac..5527ce272 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * + * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * * @package OpenCloud\Networking\Resource From 1698223117ef8e3f75437472977692a735a20baa Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 11:24:03 +0100 Subject: [PATCH 736/835] Shuffle setup org --- doc/services/autoscale/index.rst | 8 +- doc/services/common/clients.sample.rst | 22 ++++++ doc/services/common/os-client.sample.rst | 13 --- .../{rs-client.sample.rst => rs-client.rst} | 8 +- doc/services/common/rs-only.sample.rst | 8 ++ doc/services/common/service-args.rst | 23 +++--- doc/services/compute/index.rst | 13 +-- doc/services/database/index.rst | 8 +- doc/services/dns/index.rst | 8 +- doc/services/identity/index.rst | 8 +- doc/services/image/index.rst | 8 +- doc/services/load-balancer/index.rst | 12 +-- doc/services/monitoring/index.rst | 8 +- doc/services/networking/index.rst | 8 +- doc/services/object-store/index.rst | 10 +-- doc/services/orchestration/index.rst | 8 +- doc/services/queues/index.rst | 6 +- doc/services/volume/index.rst | 43 ++++++++++ doc/services/volume/snapshots.rst | 48 +++++++++++ doc/services/volume/volume-types.rst | 30 +++++++ doc/services/volume/volumes.rst | 79 +++++++++++++++++++ doc/url-types.rst | 2 + 22 files changed, 293 insertions(+), 88 deletions(-) create mode 100644 doc/services/common/clients.sample.rst delete mode 100644 doc/services/common/os-client.sample.rst rename doc/services/common/{rs-client.sample.rst => rs-client.rst} (58%) create mode 100644 doc/services/common/rs-only.sample.rst create mode 100644 doc/services/volume/snapshots.rst create mode 100644 doc/services/volume/volume-types.rst create mode 100644 doc/services/volume/volumes.rst create mode 100644 doc/url-types.rst diff --git a/doc/services/autoscale/index.rst b/doc/services/autoscale/index.rst index d6ec512ac..1bfd20e68 100644 --- a/doc/services/autoscale/index.rst +++ b/doc/services/autoscale/index.rst @@ -1,12 +1,12 @@ Auto Scale v2 ============= -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +Auto Scale service +~~~~~~~~~~~~~~~~~~ -Now, set up the Auto Scale service: +Now to instantiate the Auto Scale service: .. code-block:: php diff --git a/doc/services/common/clients.sample.rst b/doc/services/common/clients.sample.rst new file mode 100644 index 000000000..a952da730 --- /dev/null +++ b/doc/services/common/clients.sample.rst @@ -0,0 +1,22 @@ +Setup +----- + +Rackspace setup +~~~~~~~~~~~~~~~ + +.. include:: /services/common/rs-client.rst + + +OpenStack setup +~~~~~~~~~~~~~~~ + +If you're an OpenStack user, you will also need to prove a few other +configuration parameters: + +.. code-block:: php + + $client = new OpenCloud\OpenStack('{keystoneUrl}', array( + 'username' => '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/services/common/os-client.sample.rst b/doc/services/common/os-client.sample.rst deleted file mode 100644 index b4614474d..000000000 --- a/doc/services/common/os-client.sample.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. code-block:: php - - '{username}', - 'password' => '{apiKey}', - 'tenantId' => '{tenantId}', - )); diff --git a/doc/services/common/rs-client.sample.rst b/doc/services/common/rs-client.rst similarity index 58% rename from doc/services/common/rs-client.sample.rst rename to doc/services/common/rs-client.rst index e33983270..46f9a5291 100644 --- a/doc/services/common/rs-client.sample.rst +++ b/doc/services/common/rs-client.rst @@ -1,12 +1,8 @@ -The first thing to do is pass in your credentials and instantiate a Rackspace -client: +The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key: .. code-block:: php - `_. +* ``{region}`` is the region the service will operate in. For Rackspace + users, you can select one of the following from the `supported regions page + `_. -``{urlType}`` is the type of URL to use, depending on what endpoints your -catalog provides. For Rackspace, you may use either ``internalURL`` or -``publicURL``. The former will execute HTTP transactions over the internal -network configured for your service, possibly reducing latency and the overall -bandwidth cost - the caveat is that all of your resources must be in same region. -``publicURL``, however, which is the default, will operate over the public -Internet and is to be used for multi-region installations. +* ``{urlType}`` is the `type of URL `_ to use, depending on which + endpoints your catalog provides. If omitted, it will default to the public + network. diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 207151d50..234517661 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -1,17 +1,12 @@ Compute v2 ========== -.. note:: +.. include:: ../common/clients.sample.rst - This is a joint service that supports both Rackspace Cloud Servers v2 API, and - OpenStack Nova v2 API. +Compute service +~~~~~~~~~~~~~~~ -Setup ------ - -.. include:: ../common/rs-client.sample.rst - -Now, set up the Compute service: +Now to instantiate the Compute service: .. code-block:: php diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index 850e56c8a..c330d4ac8 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -1,12 +1,12 @@ Databases v1 ============ -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Databases service +~~~~~~~~~~~~~~~~~ -Now, set up the Database service: +Now to instantiate the Databases service: .. code-block:: php diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index 4caca9fe5..99d19c5e8 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -1,12 +1,12 @@ DNS v1 ====== -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +DNS service +~~~~~~~~~~~ -Now, set up the DNS service: +Now to instantiate the DNS service: .. code-block:: php diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index 35c27131b..3eaa08de6 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -1,10 +1,12 @@ Identity v2 =========== -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample +Identity service +~~~~~~~~~~~~~~~~ + +Now to instantiate the Identity service: .. code-block:: php diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index d54b09ad0..c6451554b 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -1,10 +1,12 @@ Images v1 ========= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Images service +~~~~~~~~~~~~~~ + +Now to instantiate the Images service: .. code-block:: php diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index bf2b6dd0c..e2e4ca973 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -1,17 +1,13 @@ Load Balancer v1 ================ -.. note:: +.. include:: ../common/rs-only.sample.rst - This service is only available for Rackspace users. +Load Balancer service +~~~~~~~~~~~~~~~~~~~~~ -Setup ------ - -.. include:: ../common/rs-client.sample.rst - -Now, set up the Load Balancer service: +Now to instantiate the Load Balancer service: .. code-block:: php diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index df0597e8c..be38d1067 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -1,12 +1,12 @@ Monitoring v1 ============= -Setup ------ +.. include:: ../common/rs-only.sample.rst -.. include:: ../common/rs-client.sample.rst +Monitoring service +~~~~~~~~~~~~~~~~~~ -Now, set up the Cloud Monitoring service: +Now to instantiate the Monitoring service: .. code-block:: php diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 92270fe86..89a159d4f 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -1,12 +1,12 @@ Networking v2 ============= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: rs-client.sample.rst +Networking service +~~~~~~~~~~~~~~~~~~ -Now, set up the Cloud Monitoring service: +Now to instantiate the Networking service: .. code-block:: php diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index 6b3a18b6f..8c50d7de0 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -1,12 +1,12 @@ Object Store v1 =============== -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Object Store service +~~~~~~~~~~~~~~~~~~~~ -Now, set up the Object Store service: +Now to instantiate the Object Store service: .. code-block:: php @@ -38,7 +38,7 @@ Glossary account is your portion of it. container - A storage compartment that provides a way for you to organize data. A + A storage compartment that provides a way for you to organize data. A container is similar to a folder in Windows or a directory in UNIX. The primary difference between a container and these other file system concepts is that containers cannot be nested. diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index 0dce8ac43..fcb5e46b9 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -1,12 +1,12 @@ Orchestration v1 ================ -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: rs-client.sample.rst +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ -Now set up the Orchestration service: +Now to instantiate the Orchestration service: .. code-block:: php diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index e37845d76..317257d65 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -1,10 +1,10 @@ Queues v1 ========= -Setup ------ +.. include:: ../common/clients.sample.rst -.. include:: ../common/rs-client.sample.rst +Queues service +~~~~~~~~~~~~~~ Now to instantiate the Queues service: diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst index e69de29bb..7e318a5cc 100644 --- a/doc/services/volume/index.rst +++ b/doc/services/volume/index.rst @@ -0,0 +1,43 @@ +Volumes v1 +========== + +.. include:: ../common/clients.sample.rst + +Volume service +~~~~~~~~~~~~~~ + +Now to instantiate the Volume service: + +.. code-block:: php + + $service = $client->volumeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + volumes + volume-types + snapshots + + +Glossary +-------- + +.. glossary:: + + volume + A volume is a detachable block storage device. You can think of it as a USB + hard drive. It can only be attached to one instance at a time. + + volume type + Providers may support multiple types of volumes; at Rackspace, a volume + can either be ``SSD`` (solid state disk: expensive, high-performance) or + ``SATA`` (serial attached storage: regular disks, less expensive). + + snapshot + A snapshot is a point-in-time copy of the data contained in a volume. diff --git a/doc/services/volume/snapshots.rst b/doc/services/volume/snapshots.rst new file mode 100644 index 000000000..c2d8ef6cc --- /dev/null +++ b/doc/services/volume/snapshots.rst @@ -0,0 +1,48 @@ +Snapshots +========= + +Create a snapshot +----------------- + +A ``Snapshot`` object is created from the Cloud Block Storage service. +However, it is associated with a volume, and you must specify a volume +to create one: + +.. code-block:: php + + // New instance of OpenCloud\Volume\Resource\Snapshot + $snapshot = $service->snapshot(); + + // Send to API + $snapshot->create(array( + 'display_name' => 'Name that snapshot', + 'volume_id' => $volume->id + )); + + +List snapshots +-------------- + +.. code-block:: php + + $snapshots = $service->snapshotList(); + + foreach ($snapshots as $snapshot) { + /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ + } + + +To get details on a single snapshot +----------------------------------- + +.. code-block:: php + + $snapshot = $dallas->snapshot('{snapshotId}'); + + +To delete a snapshot +-------------------- + +.. code-block:: php + + $snapshot->delete(); diff --git a/doc/services/volume/volume-types.rst b/doc/services/volume/volume-types.rst new file mode 100644 index 000000000..ba3176df9 --- /dev/null +++ b/doc/services/volume/volume-types.rst @@ -0,0 +1,30 @@ +Volume Types +============ + +List volume types +----------------- + +.. code-block:: php + + $volumeTypes = $service->volumeTypeList(); + + foreach ($volumeTypes as $volumeType) { + /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ + } + + +Describe a volume type +---------------------- + +If you know the ID of a volume type, use the ``volumeType`` method to +retrieve information on it: + +.. code-block:: php + + $volumeType = $service->volumeType(1); + +A volume type has three attributes: + +- ``id`` the volume type identifier +- ``name`` its name +- ``extra_specs`` additional information for the provider diff --git a/doc/services/volume/volumes.rst b/doc/services/volume/volumes.rst new file mode 100644 index 000000000..6a35bb135 --- /dev/null +++ b/doc/services/volume/volumes.rst @@ -0,0 +1,79 @@ +Volumes +======= + +Create a volume +--------------- + +To create a volume, you must specify its size (in gigabytes). All other +parameters are optional: + +.. code-block:: php + + // Create instance of OpenCloud\Volume\Resource\Volume + $volume = $service->volume(); + + $volume->create(array( + 'size' => 200, + 'volume_type' => $service->volumeType(''), + 'display_name' => 'My Volume', + 'display_description' => 'Used for large object storage' + )); + +List volumes +------------ + +.. code-block:: php + + $volumes = $service->volumeList(); + + foreach ($volumes as $volume) { + /** @param $volumeType OpenCloud\Volume\Resource\Volume */ + } + + +Get details on a single volume +------------------------------ + +If you specify an ID on the ``volume()`` method, it retrieves +information on the specified volume: + +.. code-block:: php + + $volume = $dallas->volume(''); + echo $volume->size; + + +To delete a volume +------------------ + +.. code-block:: php + + $volume->delete(); + + +Attach a volume to a server +--------------------------- + +.. code-block:: php + + // retrieve server + $computeService = $client->computeService('{catalogName}', '{region}'); + $server = $computeService->server('{serverId}'); + + // attach volume + $server->attachVolume($volume, '{mountPoint}') + +The ``{mountPoint}`` is the location on the server on which to mount +the volume (usually ``/dev/xvhdd`` or similar). You can also supply +``'auto'`` as the mount point, in which case the mount point will be +automatically selected for you. ``auto`` is the default value for +``{mountPoint}``, so you do not actually need to supply anything for +that parameter. + + +Detach a volume from a server +----------------------------- + +.. code-block:: php + + $server->detachVolume($volume); diff --git a/doc/url-types.rst b/doc/url-types.rst new file mode 100644 index 000000000..b0e2cc0ea --- /dev/null +++ b/doc/url-types.rst @@ -0,0 +1,2 @@ +URL types +========= From 63a3a2c8a493ff8cab9d5c0555ede76fa9a80e40 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 11:43:03 +0100 Subject: [PATCH 737/835] Refactor root pages and index page --- doc/caching-creds.rst | 36 ++++++++++----------- doc/debugging.rst | 60 +++++++++++++++++++---------------- doc/index.rst | 74 ++++++++++++++++++++++++++++++++++++------- doc/iterators.rst | 18 ++++------- doc/regions.rst | 7 +++- doc/url-types.rst | 13 ++++++++ 6 files changed, 138 insertions(+), 70 deletions(-) diff --git a/doc/caching-creds.rst b/doc/caching-creds.rst index 3c3e8df23..4eb5be5c8 100644 --- a/doc/caching-creds.rst +++ b/doc/caching-creds.rst @@ -16,29 +16,29 @@ Filesystem example In this example, credentials will be saved to a file in the local filesystem. Be sure to exclude it from your VCS. -.. code:: php +.. code-block:: php - use OpenCloud\Rackspace; + use OpenCloud\Rackspace; - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' - )); + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); - $cacheFile = __DIR__ . '/.opencloud_token'; + $cacheFile = __DIR__ . '/.opencloud_token'; - // If the cache file exists, try importing it into the client - if (file_exists($cacheFile)) { - $data = unserialize(file_get_contents($cacheFile)); - $client->importCredentials($data); - } + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } - $token = $client->getTokenObject(); + $token = $client->getTokenObject(); - // If no token exists, or the current token is expired, re-authenticate and save the new token to disk - if (!$token || ($token && $token->hasExpired())) { - $client->authenticate(); - file_put_contents($cacheFile, serialize($client->exportCredentials())); - } + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/debugging.rst b/doc/debugging.rst index 445915284..5d35d5bc1 100644 --- a/doc/debugging.rst +++ b/doc/debugging.rst @@ -36,38 +36,40 @@ If you're trying to retrieve a Swift resource, such as a Data Object, and you're not completely certain that it exists, it makes sense to wrap your call in a try/catch block: -.. code:: php +.. code-block:: php - use Guzzle\Http\Exception\ClientErrorResponseException; + use Guzzle\Http\Exception\ClientErrorResponseException; - try { - return $service->getObject('foo.jpg'); - } catch (ClientErrorResponseException $e) { - // Okay, the resource probably does not exist + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + // Okay, the resource does not exist return false; - } catch (\Exception $e) { - // Some other exception was thrown, probably critical - $this->logException($e); - $this->alertDevs(); - } + } + } catch (\Exception $e) { + // Some other exception was thrown... + } + Both ``ClientErrorResponseException`` and ``ServerErrorResponseException`` have two methods that allow you to access the HTTP transaction: -.. code:: php +.. code-block:: php + + // Find out the faulty request + $request = $e->getRequest(); - // Find out the faulty request - $request = $e->getRequest(); + // Display everything by casting as string + echo (string) $request; - // Display everything by casting as string - echo (string) $request; + // Find out the HTTP response + $response = $e->getResponse(); - // Find out the HTTP response - $response = $e->getResponse(); + // Output that too + echo (string) $response; - // Output that too - echo (string) $response; Strategy 2: Wire logging ------------------------ @@ -80,20 +82,22 @@ don't know what's going on. Here's how you enable it: Install the plugin -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ -.. code:: bash +.. code-block:: bash + + composer require guzzle/guzzle - php composer.phar require guzzle/plugin-log:~3.8 Add to your client -^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php -.. code:: php + use Guzzle\Plugin\Log\LogPlugin; - use Guzzle\Plugin\Log\LogPlugin; + $client->addSubscriber(LogPlugin::getDebugPlugin()); - $client->addSubscriber(LogPlugin::getDebugPlugin()); The above will add a generic logging subscriber to your client, which -will be notified every time a relevant HTTP event is fired off. +will output every HTTP transaction to `STDOUT`. diff --git a/doc/index.rst b/doc/index.rst index 9c06b86e8..8a211dab4 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,12 +1,37 @@ -.. php-opencloud documentation master file, created by - sphinx-quickstart on Tue Mar 3 12:28:19 2015. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +Welcome to php-opencloud! +========================= -Welcome to php-opencloud's documentation! -========================================= +Installation +------------ -Contents: +You must install this library through Composer: + +.. code-block:: bash + + composer require rackspace/php-opencloud + + +If you do not have Composer installed, please consult the `official docs +`_. + +Once you have installed the library, you will need to load Composer's autoloader +(which registers all the required namespaces). To do this, place the following +line of PHP code at the top of your application's PHP files: + +.. code-block:: php + + require 'vendor/autoload.php'; + +This assumes your application's PHP files are located in the same folder as +``vendor/``. If your files are located elsewhere, please supply the path to +``vendor/autoload.php`` in the require statement above. + +Read the `OpenStack Getting Started guide `_ +or `Rackspace Getting Started guide `_ to help +you get started with basic Compute operations. + +Services +-------- .. toctree:: :glob: @@ -14,10 +39,35 @@ Contents: services/**/index +Usage tips +---------- + +.. toctree:: + :maxdepth: 1 + + debugging + caching-creds + iterators + regions + url-types + +Help and support +---------------- + +If you have specific problems or bugs with this SDK, please file an issue on +our official `Github `_. We also +have a `mailing list `_, +so feel free to join to keep up to date with all the latest changes and +announcements to the library. + +For general feedback and support requests, send an email to +sdk-support@rackspace.com. + +You can also find assistance via IRC on #rackspace at freenode.net. -Indices and tables -================== +Contributing +------------ -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +If you'd like to contribute to the project, or require help running the +unit/acceptance tests, please view the `contributing guidelines +`_. diff --git a/doc/iterators.rst b/doc/iterators.rst index e5d3c198d..bef59e339 100644 --- a/doc/iterators.rst +++ b/doc/iterators.rst @@ -1,9 +1,6 @@ Iterators ========= -Intro ------ - Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: @@ -27,27 +24,27 @@ the SDK: Common behaviour ---------------- -.. code:: php +.. code-block:: php $iterator = $computeService->flavorList(); There are two ways to traverse an iterator. The first is the longer, more traditional way: -.. code:: php +.. code-block:: php while ($iterator->valid()) { $flavor = $iterator->current(); - + // do stuff.. echo $flavor->id; - + $iterator->next(); } There is also a shorter and more intuitive version: -.. code:: php +.. code-block:: php foreach ($iterator as $flavor) { // do stuff... @@ -63,7 +60,7 @@ Very important note Until now, users have been expected to do this: -.. code:: php +.. code-block:: php while ($flavor = $iterator->next()) { // ... @@ -119,7 +116,7 @@ precedence. Setting up a PaginatedIterator ------------------------------ -.. code:: php +.. code-block:: php use OpenCloud\Common\Collection\PaginatedIterator; @@ -175,4 +172,3 @@ needs to work. These are: +-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ | request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | +-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ - diff --git a/doc/regions.rst b/doc/regions.rst index 82edf28ec..42c14595c 100644 --- a/doc/regions.rst +++ b/doc/regions.rst @@ -3,13 +3,18 @@ Rackspace regions Below are the supported regions on the Rackspace network: -+======+===========+ ++------+-----------+ | code | location | +======+===========+ | IAD | Virginia | ++------+-----------+ | ORD | Chicago | ++------+-----------+ | DFW | Dallas | ++------+-----------+ | LON | London | ++------+-----------+ | SYD | Sydney | ++------+-----------+ | HKG | Hong Kong | +------+-----------+ diff --git a/doc/url-types.rst b/doc/url-types.rst index b0e2cc0ea..26a0410bd 100644 --- a/doc/url-types.rst +++ b/doc/url-types.rst @@ -1,2 +1,15 @@ URL types ========= + +internalURL +----------- + +An internal URL is a URL that is accessible only from within the Rackspace +Cloud network. Access to an internal URL is always free of charge. + + +publicURL +--------- + +A public URL is a URL that is accessible from anywhere. Access to a public URL +usually incurs traffic charges. From 10af5c6ca685a8cc3ddd216fa8fdfca646d4ed60 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 12:29:29 +0100 Subject: [PATCH 738/835] Add final links --- doc/services/compute/index.rst | 8 +++- doc/services/database/index.rst | 8 ++++ doc/services/dns/index.rst | 8 ++++ doc/services/identity/index.rst | 7 +++ doc/services/image/index.rst | 7 +++ doc/services/load-balancer/index.rst | 9 +++- doc/services/monitoring/index.rst | 65 +++++++++++++++++++++++++++- doc/services/networking/index.rst | 8 ++++ doc/services/object-store/index.rst | 8 ++++ doc/services/orchestration/index.rst | 9 ++++ doc/services/queues/index.rst | 8 ++++ doc/services/volume/index.rst | 8 ++++ 12 files changed, 150 insertions(+), 3 deletions(-) diff --git a/doc/services/compute/index.rst b/doc/services/compute/index.rst index 234517661..ee27bce8e 100644 --- a/doc/services/compute/index.rst +++ b/doc/services/compute/index.rst @@ -46,4 +46,10 @@ Glossary server A server is a virtual machine instance in the Cloud Servers environment. - keypair + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/database/index.rst b/doc/services/database/index.rst index c330d4ac8..32888e27d 100644 --- a/doc/services/database/index.rst +++ b/doc/services/database/index.rst @@ -67,3 +67,11 @@ Glossary for increased performance, scalability, availability and manageability. Applications with high I/O demands are performance optimized and data is protected through both local and network RAID-10. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/dns/index.rst b/doc/services/dns/index.rst index 99d19c5e8..39db3d675 100644 --- a/doc/services/dns/index.rst +++ b/doc/services/dns/index.rst @@ -49,3 +49,11 @@ Glossary DNS usually determines an IP address associated with a domain name. Reverse DNS is the opposite process: resolving a domain name from an IP address. This is usually achieved with a domain name pointer. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/identity/index.rst b/doc/services/identity/index.rst index 3eaa08de6..6cb4dfdf4 100644 --- a/doc/services/identity/index.rst +++ b/doc/services/identity/index.rst @@ -46,3 +46,10 @@ Glossary access the requested resources. Users may be directly assigned to a particular tenant and behave as if they are contained within that tenant. + + +Further Links +------------- + +- `Quickstart for the API `_ +- `API Developer Guide `_ diff --git a/doc/services/image/index.rst b/doc/services/image/index.rst index c6451554b..a7331b430 100644 --- a/doc/services/image/index.rst +++ b/doc/services/image/index.rst @@ -41,3 +41,10 @@ Glossary tag An image tag is a string of characters used to identify a specific image or images. + + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ diff --git a/doc/services/load-balancer/index.rst b/doc/services/load-balancer/index.rst index e2e4ca973..9ea7614ad 100644 --- a/doc/services/load-balancer/index.rst +++ b/doc/services/load-balancer/index.rst @@ -21,7 +21,7 @@ Operations .. toctree:: - load-balancers + load-balancer nodes virtual-ips access @@ -87,3 +87,10 @@ Glossary (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP address (``SERVICENET``), routable only within the region in which the load balancer resides. + + +Further Links +------------- + +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/monitoring/index.rst b/doc/services/monitoring/index.rst index be38d1067..0a7225e36 100644 --- a/doc/services/monitoring/index.rst +++ b/doc/services/monitoring/index.rst @@ -29,6 +29,69 @@ Operations views zones - Glossary -------- + +.. glossary:: + + agent + A monitoring daemon that resides on the server being monitored. The agent + gathers metrics based on agent checks and pushes them to Cloud Monitoring. + The agent provides insight into your servers with checks for information + such as load average and network usage. The agent acts as a single small + service that runs scheduled checks and pushes metrics to the rest of Cloud + Monitoring so the metrics can be analyzed, trigger alerts, and be archived. + These metrics are gathered via checks using agent check types, and can be + used with the other Cloud Monitoring primitives such as alarms. + + agent token + An authentication token used to identify the agent when it communicates + with Cloud Monitoring. + + alarm + An alarm contains a set of rules that determine when the monitoring system + sends a notification. You can create multiple alarms for the different + checks types associated with an entity. For example, if your entity is a + web server that hosts your company's website, you can create one alarm to + monitor the server itself, and another alarm to monitor the website. + + check + Checks explicitly specify how you want to monitor an entity. Once you've + created an entity, you can configure one or more checks for it. A check is + the foundational building block of the monitoring system, and is always + associated with an entity. The check specifies the parts or pieces of the + entity that you want to monitor, the monitoring frequency, how many + monitoring zones are launching the check, and so on. It contains the + specific details of how you are monitoring the entity. + + entity + The object or resource that you want to monitor. It can be any object or + device that you want to monitor. It's commonly a web server, but it might + also be a website, a web page or a web service. + + monitoring zone + A monitoring zone is the "launch point" of a check. When you create a + check, you specify which monitoring zone(s) you want to launch the check + from. This concept of a monitoring zone is similar to that of a datacenter, + however in the monitoring system, you can think of it more as a geographical + region. + + notification + A notification is an informational message sent to one or more addresses + by the monitoring system when an alarm is triggered. You can set up + notifications to alert a single individual or an entire team. Rackspace + Cloud Monitoring currently supports webhooks and email for sending + notifications. + + notification plan + A notification plan contains a set of notification rules to execute when an + alarm is triggered. A notification plan can contain multiple notifications + for each of the following states: + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 89a159d4f..515f9ddce 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -48,3 +48,11 @@ Glossary When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/object-store/index.rst b/doc/services/object-store/index.rst index 8c50d7de0..fcdc5aff2 100644 --- a/doc/services/object-store/index.rst +++ b/doc/services/object-store/index.rst @@ -55,3 +55,11 @@ Glossary object An object (sometimes referred to as a file) is the unit of storage in an Object Store. An object is a combination of content (data) and metadata. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/orchestration/index.rst b/doc/services/orchestration/index.rst index fcb5e46b9..bae5ec6aa 100644 --- a/doc/services/orchestration/index.rst +++ b/doc/services/orchestration/index.rst @@ -25,6 +25,7 @@ Operations resources resource-types build-info + events Glossary @@ -48,3 +49,11 @@ Glossary stack A stack is a running instance of a template. When a stack is created, the resources specified in the template are created. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/queues/index.rst b/doc/services/queues/index.rst index 317257d65..4175df760 100644 --- a/doc/services/queues/index.rst +++ b/doc/services/queues/index.rst @@ -46,3 +46,11 @@ Glossary producer or publisher sends to the queue. A message exists until it is deleted by a recipient or automatically by the system based on a TTL (time-to-live) value. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/services/volume/index.rst b/doc/services/volume/index.rst index 7e318a5cc..bee589086 100644 --- a/doc/services/volume/index.rst +++ b/doc/services/volume/index.rst @@ -41,3 +41,11 @@ Glossary snapshot A snapshot is a point-in-time copy of the data contained in a volume. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ From 21dccc349cb4cedee7d248fa8b7aa259786a5107 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 13:05:09 +0100 Subject: [PATCH 739/835] Add GS guides --- doc/index.rst | 4 ++-- doc/services/load-balancer/lb-setup.sample.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 8a211dab4..297a64c6c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,8 +26,8 @@ This assumes your application's PHP files are located in the same folder as ``vendor/``. If your files are located elsewhere, please supply the path to ``vendor/autoload.php`` in the require statement above. -Read the `OpenStack Getting Started guide `_ -or `Rackspace Getting Started guide `_ to help +Read the `OpenStack Getting Started guide `_ +or `Rackspace Getting Started guide `_ to help you get started with basic Compute operations. Services diff --git a/doc/services/load-balancer/lb-setup.sample.rst b/doc/services/load-balancer/lb-setup.sample.rst index 96e228b8d..d82c80a1f 100644 --- a/doc/services/load-balancer/lb-setup.sample.rst +++ b/doc/services/load-balancer/lb-setup.sample.rst @@ -6,4 +6,4 @@ load balancer, like so: .. code-block:: php - $loadBalancer = $service->loadBalancer('{id}'); + $loadBalancer = $service->loadBalancer('{id}'); From 2c9a7fb2753023ad26600c86fd814341571984aa Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 6 Mar 2015 13:22:42 +0100 Subject: [PATCH 740/835] Tweak GS guides --- doc/getting-started-with-openstack.rst | 233 +++++++++++++++++++++++++ doc/getting-started-with-rackspace.rst | 185 ++++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 doc/getting-started-with-openstack.rst create mode 100644 doc/getting-started-with-rackspace.rst diff --git a/doc/getting-started-with-openstack.rst b/doc/getting-started-with-openstack.rst new file mode 100644 index 000000000..da6c6ee24 --- /dev/null +++ b/doc/getting-started-with-openstack.rst @@ -0,0 +1,233 @@ +Getting Started with OpenStack +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Nova instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against Keystone: + +.. code-block:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( + 'username' => 'foo', + 'password' => 'bar', + 'tenantName' => 'baz' + )); + +You will need to substitute in the public URL endpoint for your Keystone +service, as well as your ``username``, ``password`` and ``tenantName``. +You can also specify your ``tenantId`` instead of ``tenantName`` if you +prefer. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Nova service: + +.. code-block:: php + + $compute = $client->computeService('nova', 'regionOne'); + + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. For OpenStack users, this must be retrieved +and entered in your code. If you are unsure how to retrieve the service +name, follow these steps: + +1. Setup the ``$client`` object, as above +2. Copy and run this code: + +.. code-block:: php + + $client->authenticate(); + print_r($client->getCatalog()->getItems()); + + +3. This will output all the items in your service catalog. Go through + the outputted list and find your service, making note of the "name" + field. This is the name you will need to enter as the first argument. + You will also be able to see the available regions. + +The second argument is the region. The third and last argument is the +type of URL; you may use either ``publicURL`` or ``internalURL``. + + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instances are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of an instance is its +hardware configuration. So if you want a 2GB instance but don't know the +ID, you have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + + use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +As you can see, you're creating a server called "My lovely server" - +this will take a few minutes for the build to complete. You can always +check the progress by logging into your Controller node and running: + +.. code-block:: bash + + nova list + +You can also execute a polling function immediately after the ``create`` +method that checks the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +More fun with Nova +------------------ + +Once you've booted up your instance, you can use other API operations to +monitor your Compute nodes. To list every node on record, you can +execute: + +.. code-block:: php + + $servers = $compute->serverList(); + + foreach ($servers as $server) { + // do something with each server... + echo $server->name, PHP_EOL; + } + +or, if you know a particular instance ID you can retrieve its details: + +.. code-block:: php + + $server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); + +allowing you to update its properties: + +.. code-block:: php + + $server->update(array( + 'name' => 'New server name' + )); + +or delete it entirely: + +.. code-block:: php + + $server->delete(); + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. diff --git a/doc/getting-started-with-rackspace.rst b/doc/getting-started-with-rackspace.rst new file mode 100644 index 000000000..35317a3e6 --- /dev/null +++ b/doc/getting-started-with-rackspace.rst @@ -0,0 +1,185 @@ +Getting Started with Rackspace +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Cloud Servers instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against the Rackspace API and use its services: + +.. code-block:: php + + 'foo', + 'apiKey' => 'bar' + )); + +You can see in the first example that the constant +``Rackspace::US_IDENTITY_ENDPOINT`` is just a string representation of +Rackspace's identity endpoint +(``https://identity.api.rackspacecloud.com/v2.0/``). Another difference +is that Rackspace uses API key for authentication, whereas OpenStack +uses a generic password. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Compute (Nova) service: + +.. code-block:: php + + $compute = $client->computeService(null, 'ORD'); + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. If in doubt, you can leave blank and it will +revert to the default name for the service. The second argument is the +region; you may use: + +- **DFW** (Dallas) +- **ORD** (Chicago) +- **IAD** (Virginia) +- **LON** (London) +- **HKG** (Hong Kong) +- **SYD** (Sydney) + +The third and last argument is the type of URL; you may use either +``publicURL`` or ``internalURL``. If you select ``internalUrl`` all API +traffic will use ServiceNet (internal IPs) and will receive a +performance boost. + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Servers are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of a server is its hardware +configuration. So if you want a 2GB instance but don't know the ID, you +have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + +use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +You can also call a polling function that checks on the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. From 6493280bc77bb2ace53a1bf956bc3cff79b4597f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:15:29 -0800 Subject: [PATCH 741/835] Adding descriptions to docblocks. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 ++ lib/OpenCloud/Networking/Resource/SecurityGroupRule.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index 2892f5471..b4a8e08d2 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,6 +20,8 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * A security group is a named container for security group rules, each of which is + * represented by {@see \OpenCloud\Networking\Resource\SecurityGroupRule}. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php index 5527ce272..06ad028b2 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroupRule.php @@ -20,6 +20,10 @@ use OpenCloud\Common\Resource\PersistentResource; /** + * + * Security group rules provide users the ability to specify the types of traffic that are allowed + * to pass through to and from ports (represented by {@see \OpenCloud\Networking\Resource\Port}) + * on a virtual server instance. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups * From 3409c425da7f8d3955c346b585efcdb22adaee89 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:32:30 -0800 Subject: [PATCH 742/835] Fixing typo. --- docs/userguide/Networking/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index bfa1a217c..13342977f 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -65,9 +65,9 @@ be assigned to the interfaces plugged into them. When IP addresses are associated with a port, this also implies the port is associated with a subnet because the IP address is taken from the allocation pool for a specific subnet. -* **Security Group**: TODO +* **Security Group**: A named container for security group rules. -* **Security Group Rule**: TODO +* **Security Group Rule**: Provide users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. ## Prerequisites From 78c3dc004e90e295fbdf8e3aa0049438e675560c Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:32:38 -0800 Subject: [PATCH 743/835] Adding descriptions for Security Groups and Security Group Rules sections. --- docs/userguide/Networking/USERGUIDE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 13342977f..5a7beb07f 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -485,7 +485,7 @@ $port->delete(); ## Security Groups -TODO: Add description +A security group is a named container for [security group rules](#security-group-rules). ### Create a security group @@ -546,7 +546,7 @@ $securityGroup->delete(); ## Security Group Rules -TODO: Add description +A security group rule provides users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. ### Create a security group rule From 79f0c78dba4b01a7835e98c2960e60f0d5005c26 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:51:36 -0800 Subject: [PATCH 744/835] Moving @see annotations to their own lines. --- lib/OpenCloud/Networking/Service.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Networking/Service.php b/lib/OpenCloud/Networking/Service.php index 12bb7477f..e3fcc879e 100644 --- a/lib/OpenCloud/Networking/Service.php +++ b/lib/OpenCloud/Networking/Service.php @@ -51,8 +51,10 @@ public function network($id = null) /** * Creates a new Network and returns it. * - * @param array $params Network creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network + * @param array $params Network creation parameters. * @return \OpenCloud\Networking\Resource\Network Object representing created network + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-network */ public function createNetwork(array $params = array()) { @@ -136,8 +138,10 @@ public function subnet($id = null) /** * Creates a new Subnet and returns it. * - * @param array $params Subnet creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet + * @param array $params Subnet creation parameters. * @return \OpenCloud\Networking\Resource\Subnet Object representing created subnet + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-subnet */ public function createSubnet(array $params = array()) { @@ -221,8 +225,10 @@ public function port($id = null) /** * Creates a new Port and returns it. * - * @param array $params Port creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port + * @param array $params Port creation parameters. * @return \OpenCloud\Networking\Resource\Port Object representing created port + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-port */ public function createPort(array $params = array()) { @@ -306,8 +312,10 @@ public function securityGroup($id = null) /** * Creates a new SecurityGroup and returns it. * - * @param array $params SecurityGroup creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group + * @param array $params SecurityGroup creation parameters. * @return \OpenCloud\Networking\Resource\SecurityGroup Object representing created security group + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group */ public function createSecurityGroup(array $params = array()) { @@ -355,8 +363,10 @@ public function securityGroupRule($id = null) /** * Creates a new SecurityGroupRule and returns it. * - * @param array $params SecurityGroupRule creation parameters. @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule + * @param array $params SecurityGroupRule creation parameters. * @return \OpenCloud\Networking\Resource\SecurityGroupRule Object representing created security group rule + * + * @see https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Networking/USERGUIDE.md#create-a-security-group-rule */ public function createSecurityGroupRule(array $params = array()) { From 1b2f0490604bae0a3bbfe25537c8f323c49ef280 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Fri, 6 Mar 2015 07:53:56 -0800 Subject: [PATCH 745/835] Appeasing the PSR-2 linter. --- lib/OpenCloud/Networking/Resource/SecurityGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/SecurityGroup.php b/lib/OpenCloud/Networking/Resource/SecurityGroup.php index b4a8e08d2..4b2f85efd 100644 --- a/lib/OpenCloud/Networking/Resource/SecurityGroup.php +++ b/lib/OpenCloud/Networking/Resource/SecurityGroup.php @@ -20,7 +20,7 @@ use OpenCloud\Common\Resource\PersistentResource; /** - * A security group is a named container for security group rules, each of which is + * A security group is a named container for security group rules, each of which is * represented by {@see \OpenCloud\Networking\Resource\SecurityGroupRule}. * * @see http://developer.openstack.org/api-ref-networking-v2.html#security_groups From 48f3bebd229810b540b1b0c517bf47017500a9be Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Fri, 6 Mar 2015 17:29:07 -0600 Subject: [PATCH 746/835] Swap support email address for support URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42948e8c3..6031cf4ac 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Your feedback is appreciated! If you have specific problems or bugs with this SD also have a [mailing list](https://groups.google.com/forum/#!forum/php-opencloud), so feel free to join to keep up to date with all the latest changes and announcements to the library. -For general feedback and support requests, send an email to sdk-support@rackspace.com. +For general feedback and support requests, contact us at https://developer.rackspace.com/support/ You can also find assistance via IRC on #rackspace at freenode.net. From 43ee5426661b61ab784e244794f6649287e13fc8 Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Fri, 6 Mar 2015 17:30:33 -0600 Subject: [PATCH 747/835] Swap support email address for support URL --- docs/getting-started-openstack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index b00e58693..6bb1ae260 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -198,4 +198,4 @@ $server->delete(); Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or [Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to e-mail sdk-support@rackspace.com or open a Github issue with details. +troubles, feel free to contact us at https://developer.rackspace.com/support/ or open a Github issue with details. From 67cf54ef207e5811e9f578f0644f9c7f39215f63 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:34:55 +0100 Subject: [PATCH 748/835] Use full paths for downloadable samples --- doc/services/database/configurations.rst | 14 +- doc/services/database/datastores.rst | 8 +- doc/services/database/instances.rst | 4 +- doc/services/networking/networks.rst | 12 +- doc/services/networking/ports.rst | 12 +- doc/services/networking/subnets.rst | 12 +- doc/services/orchestration/build-info.rst | 2 +- doc/services/orchestration/events.rst | 6 +- doc/services/orchestration/resource-types.rst | 6 +- doc/services/orchestration/resources.rst | 6 +- doc/services/orchestration/stacks.rst | 24 +- doc/services/orchestration/stacks.rst-e | 299 ++++++++++++++++++ doc/services/orchestration/templates.rst | 4 +- 13 files changed, 354 insertions(+), 55 deletions(-) create mode 100644 doc/services/orchestration/stacks.rst-e diff --git a/doc/services/database/configurations.rst b/doc/services/database/configurations.rst index edfb9dc89..f50619914 100644 --- a/doc/services/database/configurations.rst +++ b/doc/services/database/configurations.rst @@ -22,7 +22,7 @@ Creating a configuration ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Listing configurations @@ -37,7 +37,7 @@ You can list out all the configurations you have created as shown below: /** @var $configuration OpenCloud\Database\Resource\Configuration **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a configuration @@ -50,7 +50,7 @@ You can retrieve a specific configuration, using its ID, as shown below: $configuration = $service->configuration('{configId}'); /** @var OpenCloud\Database\Resource\Configuration **/ -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Updating a configuration @@ -77,7 +77,7 @@ You can patch a configuration as shown below: ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Replacing a configuration @@ -94,7 +94,7 @@ You can replace a configuration as shown below: ) )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Deleting a configuration @@ -104,7 +104,7 @@ Deleting a configuration $configuration->delete(); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ .. note:: @@ -124,4 +124,4 @@ as shown below: /** @var $instance OpenCloud\Database\Resource\Instance **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/datastores.rst b/doc/services/database/datastores.rst index 6ca45daed..ad6d90e85 100644 --- a/doc/services/database/datastores.rst +++ b/doc/services/database/datastores.rst @@ -13,7 +13,7 @@ You can list out all the datastores available as shown below: /** @var $datastore OpenCloud\Database\Resource\Datastore **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a datastore @@ -27,7 +27,7 @@ shown below: /** @var OpenCloud\Database\Resource\Datastore **/ $datastore = $service->datastore('{datastoreId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Listing datastore versions @@ -43,7 +43,7 @@ shown below: /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ } -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Retrieving a datastore version @@ -56,4 +56,4 @@ below: $datastoreVersion = $datastore->version('{versionId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ diff --git a/doc/services/database/instances.rst b/doc/services/database/instances.rst index 082e56c05..6f3afc1e0 100644 --- a/doc/services/database/instances.rst +++ b/doc/services/database/instances.rst @@ -16,7 +16,7 @@ Create a new instance 'volume' => array('size' => 4) // 4GB of volume disk )); -`Get the executable PHP script for this sample `__ +`Get the executable PHP script for this sample `__ Waiting for the instance to build @@ -96,7 +96,7 @@ Retrieving an instance $instance = $service->instance('{instanceId}'); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Updating an instance diff --git a/doc/services/networking/networks.rst b/doc/services/networking/networks.rst index a290a34e7..b95d44eba 100644 --- a/doc/services/networking/networks.rst +++ b/doc/services/networking/networks.rst @@ -28,7 +28,7 @@ You can create a network as shown in the following example: 'name' => 'My private backend network' )); -`Get the executable PHP script for this example `__ +`Get the executable PHP script for this example `__ Create multiple networks @@ -55,7 +55,7 @@ You can create multiple networks as shown in the following example: /** @var $network OpenCloud\Networking\Resource\Network **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List networks ------------- @@ -72,7 +72,7 @@ following example: } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a network @@ -86,7 +86,7 @@ in the following example: /** @var $network OpenCloud\Networking\Resource\Network **/ $network = $networkingService->getNetwork('{networkId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a network @@ -113,7 +113,7 @@ You can update a network as shown in the following example: 'name' => 'My updated private backend network' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a network @@ -125,4 +125,4 @@ You can delete a network as shown in the following example: $network->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst index 06d2ec2cf..8eeb07bf0 100644 --- a/doc/services/networking/ports.rst +++ b/doc/services/networking/ports.rst @@ -39,7 +39,7 @@ You can create a port as shown in the following example: )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create multiple ports @@ -68,7 +68,7 @@ You can create multiple ports as shown in the following example: /** @var $port OpenCloud\Networking\Resource\Port **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List ports @@ -84,7 +84,7 @@ You can list all the ports to which you have access as shown in the following ex /** @var $port OpenCloud\Networking\Resource\Port **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a port @@ -98,7 +98,7 @@ the following example: /** @var $port OpenCloud\Networking\Resource\Port **/ $port = $networkingService->getPort('{portId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a port @@ -135,7 +135,7 @@ You can update a port as shown in the following example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a port @@ -147,4 +147,4 @@ You can delete a port as shown in the following example: $port->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/subnets.rst b/doc/services/networking/subnets.rst index e8d6951a0..9a6c2a7b4 100644 --- a/doc/services/networking/subnets.rst +++ b/doc/services/networking/subnets.rst @@ -42,7 +42,7 @@ You can create a subnet as shown in the following example: 'cidr' => '192.168.199.0/25' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create multiple subnets @@ -69,7 +69,7 @@ You can create multiple subnets as shown in the following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List subnets @@ -85,7 +85,7 @@ following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a subnet @@ -99,7 +99,7 @@ in the following example: /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ $subnet = $networkingService->getSubnet('{subnetId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update a subnet @@ -137,7 +137,7 @@ You can update a subnet as shown in the following example: 'gatewayIp' => '192.168.62.155' )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a subnet @@ -149,4 +149,4 @@ You can delete a subnet as shown in the following example: $subnet->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/build-info.rst b/doc/services/orchestration/build-info.rst index c30ce5cfa..b4016bab2 100644 --- a/doc/services/orchestration/build-info.rst +++ b/doc/services/orchestration/build-info.rst @@ -12,4 +12,4 @@ build as shown in the following example: /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ $buildInfo = $orchestrationService->getBuildInfo(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/events.rst b/doc/services/orchestration/events.rst index 68dbf611b..bceec0f5a 100644 --- a/doc/services/orchestration/events.rst +++ b/doc/services/orchestration/events.rst @@ -19,7 +19,7 @@ shown in the following example: /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List stack resource events @@ -36,7 +36,7 @@ shown in the following example: /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource event @@ -50,4 +50,4 @@ using the resource event's ID, as shown in the following example: /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resource-types.rst b/doc/services/orchestration/resource-types.rst index cd229d660..caa58b013 100644 --- a/doc/services/orchestration/resource-types.rst +++ b/doc/services/orchestration/resource-types.rst @@ -17,7 +17,7 @@ example: /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get resource type @@ -31,7 +31,7 @@ following example: /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get resource type template @@ -45,4 +45,4 @@ appear in a template, as shown in the following example: /** @var $resourceTypeTemplate string **/ $resourceTypeTemplate = $resourceType->getTemplate(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/resources.rst b/doc/services/orchestration/resources.rst index 049150f58..b04115122 100644 --- a/doc/services/orchestration/resources.rst +++ b/doc/services/orchestration/resources.rst @@ -19,7 +19,7 @@ example: /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource @@ -33,7 +33,7 @@ name, as shown in the following example: /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ $resource = $stack->getResource('load-balancer'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack resource metadata @@ -47,4 +47,4 @@ shown in the following example: /** @var $resourceMetadata \stdClass **/ $resourceMetadata = $resource->getMetadata(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst b/doc/services/orchestration/stacks.rst index 75a14cd4c..29b6c4f7f 100644 --- a/doc/services/orchestration/stacks.rst +++ b/doc/services/orchestration/stacks.rst @@ -46,7 +46,7 @@ example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Preview a stack from a template URL @@ -68,7 +68,7 @@ in the following example: ) )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create stack @@ -111,7 +111,7 @@ example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Create a stack from a template URL @@ -134,7 +134,7 @@ in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ List stacks ----------- @@ -149,7 +149,7 @@ following example: /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack @@ -163,7 +163,7 @@ following example: /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ $stack = $orchestrationService->getStack('simple-lamp-setup'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get stack template @@ -178,7 +178,7 @@ used to create the stack. /** @var $stackTemplate string **/ $stackTemplate = $stack->getTemplate(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update stack @@ -221,7 +221,7 @@ example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Update Stack from Template URL @@ -243,7 +243,7 @@ in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete stack @@ -256,7 +256,7 @@ stack *and* the resources as shown in the following example: $stack->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Abandon Stack @@ -277,7 +277,7 @@ abandon the stack as shown in the following example: $abandonStackData = $stack->abandon(); file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Adopt stack @@ -296,4 +296,4 @@ shown in the following example: 'timeoutMins' => 5 )); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst-e b/doc/services/orchestration/stacks.rst-e new file mode 100644 index 000000000..29b6c4f7f --- /dev/null +++ b/doc/services/orchestration/stacks.rst-e @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/templates.rst b/doc/services/orchestration/templates.rst index 5eca2519d..24bc896bc 100644 --- a/doc/services/orchestration/templates.rst +++ b/doc/services/orchestration/templates.rst @@ -31,7 +31,7 @@ file, you can validate it as shown in the following example: // Use $e->getMessage() for explanation of why template is invalid } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Validate Template from URL ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,4 +52,4 @@ following example: // Use $e->getMessage() for explanation of why template is invalid } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ From 0df0c84d8b9d73610fb002bc94fb10ce5b1dc037 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:37:13 +0100 Subject: [PATCH 749/835] Remove MD links --- doc/services/identity/roles.rst | 57 ++++++++++++--------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/doc/services/identity/roles.rst b/doc/services/identity/roles.rst index 96a56488f..09f0600fa 100644 --- a/doc/services/identity/roles.rst +++ b/doc/services/identity/roles.rst @@ -1,9 +1,6 @@ Roles ===== -Intro ------ - A role is a personality that a user assumes when performing a specific set of operations. A role includes a set of rights and privileges. A user assuming a role inherits the rights and privileges associated with @@ -13,12 +10,6 @@ determines how to interpret a user's roles. A role that grants access to a list of operations or resources within one service may grant access to a completely different list when interpreted by a different service. -Setup ------ - -Role objects are instantiated from the Identity service. For more -details, see the `Service `__ docs. - Useful object properties/methods -------------------------------- @@ -37,16 +28,13 @@ List roles This call lists the global roles available within a specified service. -.. code:: php - - $roles = $service->getRoles(); +.. code-block:: php - foreach ($roles as $role) { - // ... - } + $roles = $service->getRoles(); -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. + foreach ($roles as $role) { + // ... + } Get role -------- @@ -54,39 +42,36 @@ Get role This call lists detailed information (id, name, description) for a specified role. -.. code:: php +.. code-block:: php + + $role = $service->getRole('{roleId}'); - $roleId = '123abc'; - $role = $service->getRole($roleId); Add/delete user roles --------------------- -To add/remove user roles, you must first instantiate a -`user `__ object: +.. code-block:: php + + $user = $service->getUser('{userId}'); -.. code:: php + $roleId = '{roleId}'; - $roleId = '123abc'; + // add role to user + $user->addRole($roleId); - // add role to user - $user->addRole($roleId); + // remove role from user + $user->removeRole($roleId); - // remove role from user - $user->removeRole($roleId); List user global roles ---------------------- This call returns a list of global roles associated with a user: -.. code:: php - - $roles = $user->getRoles(); +.. code-block:: php - foreach ($roles as $role) { - // ... - } + $roles = $user->getRoles(); -For more information about how to use iterators, see the -`documentation <../Iterators.md>`__. + foreach ($roles as $role) { + // ... + } From 59eefc237cc9c19fed69a3582dbb6c7cce02d2e3 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 11:43:57 +0100 Subject: [PATCH 750/835] Add Security Groups docs --- doc/services/networking/index.rst | 10 +++ .../networking/security-group-rules.rst | 61 +++++++++++++++++ doc/services/networking/security-groups.rst | 67 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 doc/services/networking/security-group-rules.rst create mode 100644 doc/services/networking/security-groups.rst diff --git a/doc/services/networking/index.rst b/doc/services/networking/index.rst index 515f9ddce..335f6a10c 100644 --- a/doc/services/networking/index.rst +++ b/doc/services/networking/index.rst @@ -23,6 +23,8 @@ Operations networks subnets ports + security-groups + security-group-rules Glossary -------- @@ -49,6 +51,14 @@ Glossary associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. + security group + A security group is a named container for security group rules. + + security group rule + A security group rule provides users the ability to specify the types of + traffic that are allowed to pass through to and from ports on a virtual + server instance. + Further links ------------- diff --git a/doc/services/networking/security-group-rules.rst b/doc/services/networking/security-group-rules.rst new file mode 100644 index 000000000..1ca6041d1 --- /dev/null +++ b/doc/services/networking/security-group-rules.rst @@ -0,0 +1,61 @@ +Security Group Rules +==================== + +Create a security group rule +---------------------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================================================================================================================================================================+=======================================+=============+=================+============================================+ +| ``securityGroupId`` | The security group ID to associate with this security group rule. | String | Yes | - | ``2076db17-a522-4506-91de-c6dd8e837028`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``direction`` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (``ingress`` or ``egress``) | Yes | - | ``ingress`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``ethertype`` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (``IPv4`` or ``IPv6``) | No | ``IPv4`` | ``IPv6`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMin`` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the ``portRangeMax`` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMax`` | The maximum port number in the range that is matched by the security group rule. The port\_range\_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``protocol`` | The protocol that is matched by the security group rule. | String (``tcp``, ``udp``, ``icmp``) | No | ``null`` | ``tcp`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteGroupId`` | The remote group ID to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``85cc3048-abc3-43cc-89b3-377341426ac5`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteIpPrefix`` | The remote IP prefix to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``192.168.5.0`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ + +You can create a security group rule as shown in the following example: + +.. code:: php + + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + $securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + +`Get the executable PHP script for this example `_ + + +List security group rules +------------------------- + +You can list all the security group rules to which you have access as +shown in the following example: + +.. code:: php + + $securityGroupRules = $networkingService->listSecurityGroupRules(); + foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + } + +`Get the executable PHP script for this example `_ diff --git a/doc/services/networking/security-groups.rst b/doc/services/networking/security-groups.rst new file mode 100644 index 000000000..2ec8b384d --- /dev/null +++ b/doc/services/networking/security-groups.rst @@ -0,0 +1,67 @@ +Security Groups +=============== + +Create a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+================================================================================+=============+=============+=================+=====================================+ +| ``name`` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | ``new-webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| ``description`` | Description of the security group. | String | No | ``null`` | ``security group for webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ + +You can create a security group as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + +`Get the executable PHP script for this example `_ + +List security groups +~~~~~~~~~~~~~~~~~~~~ + +You can list all the security groups to which you have access as shown +in the following example: + +.. code-block:: php + + $securityGroups = $networkingService->listSecurityGroups(); + foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + } + +`Get the executable PHP script for this example `_ + +Get a security group +~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific security group by using that security +group’s ID, as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); + +`Get the executable PHP script for this example `_ + +Delete a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete a security group as shown in the following example: + +.. code-block:: php + + $securityGroup->delete(); + +`Get the executable PHP script for this example `_ From 9ef5e6856a31c012fd0e7a4ab21e0ad35308217e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 16:54:07 +0100 Subject: [PATCH 751/835] Fixing relative links --- doc/services/networking/security-groups.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/services/networking/security-groups.rst b/doc/services/networking/security-groups.rst index 2ec8b384d..cde5c9a2d 100644 --- a/doc/services/networking/security-groups.rst +++ b/doc/services/networking/security-groups.rst @@ -40,7 +40,7 @@ in the following example: /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ } -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Get a security group ~~~~~~~~~~~~~~~~~~~~ @@ -53,7 +53,7 @@ group’s ID, as shown in the following example: /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ Delete a security group ~~~~~~~~~~~~~~~~~~~~~~~ @@ -64,4 +64,4 @@ You can delete a security group as shown in the following example: $securityGroup->delete(); -`Get the executable PHP script for this example `_ +`Get the executable PHP script for this example `_ From 916911cfbff0b3e3ccfab2105d238ed100fe405f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 17:05:37 +0100 Subject: [PATCH 752/835] Use :doc: to link to inline documents --- doc/index.rst | 6 +++--- doc/services/identity/roles.rst | 1 + doc/services/identity/tokens.rst | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 297a64c6c..fc7c280c3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -26,9 +26,9 @@ This assumes your application's PHP files are located in the same folder as ``vendor/``. If your files are located elsewhere, please supply the path to ``vendor/autoload.php`` in the require statement above. -Read the `OpenStack Getting Started guide `_ -or `Rackspace Getting Started guide `_ to help -you get started with basic Compute operations. +Read the :doc:`getting-started-with-openstack` or +:doc:`getting-started-with-rackspace` to help you get started with basic +Compute operations. Services -------- diff --git a/doc/services/identity/roles.rst b/doc/services/identity/roles.rst index 09f0600fa..614518b9b 100644 --- a/doc/services/identity/roles.rst +++ b/doc/services/identity/roles.rst @@ -36,6 +36,7 @@ This call lists the global roles available within a specified service. // ... } + Get role -------- diff --git a/doc/services/identity/tokens.rst b/doc/services/identity/tokens.rst index f49484105..e4601c7c4 100644 --- a/doc/services/identity/tokens.rst +++ b/doc/services/identity/tokens.rst @@ -76,7 +76,7 @@ Interacting with users $user = $service->resource('User', $data); To see which methods you can call on ``$user`` (which implements -``OpenCloud\Identity\Resource\User``), see our `user documentation `_ +``OpenCloud\Identity\Resource\User``), see our :doc:`user documentation ` which accompanies this guide. @@ -89,7 +89,7 @@ Interacting with tenants $tenant = $service->resource('Tenant', $data); To see which methods you can call on ``$tenant`` (which implements -``OpenCloud\Identity\Resource\Tenant``), see our `user documentation `_ +``OpenCloud\Identity\Resource\Tenant``), see our :doc:`user documentation ` which accompanies this guide. From 25de01ed8d4b5b65719251bb54430ae5f824aba2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 18:23:21 +0100 Subject: [PATCH 753/835] Adding sphinx extension --- doc/requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/requirements.txt diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000..9873100ea --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1 @@ +sphinxcontrib.phpdomain From ba89da5f301607c0c01fd6897a7bb0d3ee2c4637 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 9 Mar 2015 18:31:28 +0100 Subject: [PATCH 754/835] Use correct ext name --- doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 9873100ea..3f092f372 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1 +1 @@ -sphinxcontrib.phpdomain +sphinxcontrib-phpdomain From 840c1407d2b1d5fbba5d2d5989c95d56716006ee Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 11:44:00 +0100 Subject: [PATCH 755/835] Adding sample links --- doc/services/compute/flavors.rst | 2 + doc/services/compute/images.rst | 3 + doc/services/compute/keypairs.rst | 5 + doc/services/compute/servers.rst | 29 +- doc/services/identity/users.rst | 14 + doc/services/load-balancer/access.rst | 7 +- doc/services/load-balancer/load-balancer.rst | 14 +- doc/services/load-balancer/ssl.rst | 2 + doc/services/object-store/access.rst | 11 +- doc/services/object-store/account.rst | 6 + doc/services/object-store/cdn.rst | 6 + doc/services/object-store/containers.rst | 22 ++ doc/services/object-store/objects.rst | 22 ++ doc/services/orchestration/stacks.rst-e | 299 ------------------- doc/services/queues/claims.rst | 2 + doc/services/queues/messages.rst | 2 + doc/services/queues/queues.rst | 11 + doc/services/volume/snapshots.rst | 8 + doc/services/volume/volumes.rst | 9 + 19 files changed, 162 insertions(+), 312 deletions(-) delete mode 100644 doc/services/orchestration/stacks.rst-e diff --git a/doc/services/compute/flavors.rst b/doc/services/compute/flavors.rst index eeb447d3c..470ce8368 100644 --- a/doc/services/compute/flavors.rst +++ b/doc/services/compute/flavors.rst @@ -20,6 +20,8 @@ List flavors /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ } +`Get the executable PHP script for this example `_ + Detailed results ~~~~~~~~~~~~~~~~ diff --git a/doc/services/compute/images.rst b/doc/services/compute/images.rst index fb68af7f7..570790c04 100644 --- a/doc/services/compute/images.rst +++ b/doc/services/compute/images.rst @@ -23,6 +23,9 @@ Below is the simplest usage for retrieving a list of images: } +`Get the executable PHP script for this example `_ + + Detailed results ~~~~~~~~~~~~~~~~ diff --git a/doc/services/compute/keypairs.rst b/doc/services/compute/keypairs.rst index f7e67b113..823fcf5a2 100644 --- a/doc/services/compute/keypairs.rst +++ b/doc/services/compute/keypairs.rst @@ -21,6 +21,8 @@ value is automatically generated for you. $pubKey = $keypair->getPublicKey(); $priKey = $keypair->getPrivateKey(); +`Get the executable PHP script for this example `_ + Upload existing keypair ----------------------- @@ -41,6 +43,9 @@ filesystem. 'publicKey' => $content )); +`Get the executable PHP script for this example `_ + + List keypairs ------------- diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 229e8e4ff..8a55ce57f 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -49,6 +49,9 @@ URL parameters for filtering servers | RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | +--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +`Get the executable PHP script for this example `_ + + Create server ------------- @@ -59,19 +62,22 @@ Now we're ready to create our instance: .. code-block:: php - $server = $compute->server(); + $server = $compute->server(); - $server->create(array( - 'name' => 'My lovely server', - 'imageId' => '{imageId}', - 'flavorId' => '{flavorId}', - )); + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. +`Get the executable PHP script for this example `_ + + Using a bootable volume ~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,6 +104,9 @@ you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. +`Get the executable PHP script for this example `_ + + Create parameters ~~~~~~~~~~~~~~~~~ @@ -146,6 +155,8 @@ as usual, with one extra parameter: So, as you can see, you specify the **name** of an existing keypair that you previously created on the API. +`Get the executable PHP script for this example `_ + Creating a server with personality files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,6 +187,10 @@ attributes are detailed in the next section. 'name' => 'NEW SERVER NAME' )); + +`Get the executable PHP script for this example `_ + + Updatable attributes ~~~~~~~~~~~~~~~~~~~~ @@ -195,3 +210,5 @@ Delete server .. code-block:: php $server->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/identity/users.rst b/doc/services/identity/users.rst index b55236a74..944888902 100644 --- a/doc/services/identity/users.rst +++ b/doc/services/identity/users.rst @@ -34,6 +34,8 @@ List users // ... } +`Get the executable PHP script for this example `_ + Retrieve a user by username --------------------------- @@ -42,6 +44,8 @@ Retrieve a user by username $user = $service->getUser('jamie'); +`Get the executable PHP script for this example `_ + Retrieve a user by user ID -------------------------- @@ -52,6 +56,8 @@ Retrieve a user by user ID $user = $service->getUser('{userId}', UserConst::MODE_ID); +`Get the executable PHP script for this example `_ + Retrieve a user by email address -------------------------------- @@ -62,6 +68,8 @@ Retrieve a user by email address $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); +`Get the executable PHP script for this example `_ + Create user ----------- @@ -98,6 +106,8 @@ automatically generated and provided in the response. // show generated password echo $user->getPassword(); +`Get the executable PHP script for this example `_ + Update user ----------- @@ -129,6 +139,8 @@ Delete user $user->delete(); +`Get the executable PHP script for this example `_ + List credentials ---------------- @@ -159,3 +171,5 @@ you: $user->resetApiKey(); echo $user->getApiKey(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/load-balancer/access.rst b/doc/services/load-balancer/access.rst index 4387dd8de..42cd68315 100644 --- a/doc/services/load-balancer/access.rst +++ b/doc/services/load-balancer/access.rst @@ -68,6 +68,11 @@ You can add network items to a load balancer's access list very easily: In the above example, we allowed access for 1 IP address, and used the "0.0.0.0" wildcard to blacklist all other traffic. +Get the executable PHP scripts for this example: + +* `Blacklist IP range `_ +* `Limit access to 1 IP `_ + Remove Network Item From Access List ------------------------------------ @@ -76,4 +81,4 @@ You an remove a network item from a load balancer's access list: .. code-block:: php - $networkItem->delete(); + $networkItem->delete(); diff --git a/doc/services/load-balancer/load-balancer.rst b/doc/services/load-balancer/load-balancer.rst index 7071794df..d7867755d 100644 --- a/doc/services/load-balancer/load-balancer.rst +++ b/doc/services/load-balancer/load-balancer.rst @@ -51,9 +51,11 @@ port number, before submitting to the API: For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ please see the sections below. +`Get the executable PHP script for this example `_ -List Load Balancer Details --------------------------- + +Get Load Balancer Details +------------------------- You can retrieve a single load balancer's details by using its ID: @@ -64,7 +66,7 @@ You can retrieve a single load balancer's details by using its ID: List Load Balancers -~~~~~~~~~~~~~~~~~~~ +------------------- You can retrieve a list of all your load balancers: @@ -76,6 +78,8 @@ You can retrieve a list of all your load balancers: /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ } +`Get the executable PHP script for this example `_ + Update a Load Balancer ---------------------- @@ -121,6 +125,8 @@ When you no longer have a need for the load balancer, you can remove it: $loadBalancer->delete(); +`Get the executable PHP script for this example `_ + Protocols --------- @@ -159,7 +165,7 @@ You can programmatically list all supported load balancing algorithms: .. code-block:: php $algorithms = $service->algorithmList(); - + foreach ($algorithms as $algorithm) { /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ } diff --git a/doc/services/load-balancer/ssl.rst b/doc/services/load-balancer/ssl.rst index 12b28a6af..413bdc612 100644 --- a/doc/services/load-balancer/ssl.rst +++ b/doc/services/load-balancer/ssl.rst @@ -43,6 +43,8 @@ For a full list, with explanations, of required and optional attributes, please consult the `official documentation `__ +`Get the executable PHP script for this example `_ + Delete configuration -------------------- diff --git a/doc/services/object-store/access.rst b/doc/services/object-store/access.rst index 62cb541ca..40fe61670 100644 --- a/doc/services/object-store/access.rst +++ b/doc/services/object-store/access.rst @@ -23,6 +23,11 @@ in a global state: The string argument of ``setTempUrlSecret()`` is optional - if left out, the SDK will generate a random hashed secret for you. +Get the executable PHP script for this example: + +* `Specify a URL secret `_ +* `Generate random URL secret `_ + Create a temporary URL ---------------------- @@ -32,14 +37,16 @@ your object. To allow GET access to your object for 1 minute: .. code-block:: php - $object->getTemporaryUrl(60, 'GET'); + $object->getTemporaryUrl(60, 'GET'); To allow PUT access for 1 hour: .. code-block:: php - $object->getTemporaryUrl(360, 'PUT'); + $object->getTemporaryUrl(360, 'PUT'); + +`Get the executable PHP script for this example `_ Hosting HTML sites on CDN diff --git a/doc/services/object-store/account.rst b/doc/services/object-store/account.rst index fb51c8855..50c17f1a8 100644 --- a/doc/services/object-store/account.rst +++ b/doc/services/object-store/account.rst @@ -29,6 +29,8 @@ Retrieve total container count $account->getContainerCount(); +`Get the executable PHP script for this example `_ + Retrieve total object count --------------------- @@ -37,6 +39,8 @@ Retrieve total object count $account->getObjectCount(); +`Get the executable PHP script for this example `_ + Retrieve total bytes used ------------------------- @@ -44,3 +48,5 @@ Retrieve total bytes used .. code-block:: php $account->getBytesUsed(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst index 5ad72b6cd..eb29bb27f 100644 --- a/doc/services/object-store/cdn.rst +++ b/doc/services/object-store/cdn.rst @@ -29,6 +29,8 @@ execute the method on: /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ } +`Get the executable PHP script for this example `_ + CDN-enable a container ---------------------- @@ -47,6 +49,8 @@ refetches and caches the object for the TTL period. $container->enableCdn(); +`Get the executable PHP script for this example `_ + CDN-disable a container ----------------------- @@ -55,6 +59,8 @@ CDN-disable a container $container->disableCdn(); +`Get the executable PHP script for this example `_ + Operations on CDN-enabled containers ------------------------------------ diff --git a/doc/services/object-store/containers.rst b/doc/services/object-store/containers.rst index 4b9c66ac3..a2f0485e8 100644 --- a/doc/services/object-store/containers.rst +++ b/doc/services/object-store/containers.rst @@ -22,6 +22,9 @@ Forward slashes are not currently permitted. (such as spaces or non-English characters), you must ensure they are encoded with `urlencode `_ before passing them in +`Get the executable PHP script for this example `_ + + List containers --------------- @@ -42,6 +45,8 @@ memcmp() function, regardless of text encoding. The list is limited to 10,000 containers at a time. To work with larger collections, please read the next section. +`Get the executable PHP script for this example `_ + Filtering large collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -82,6 +87,8 @@ To retrieve a certain container: /** @param $container OpenCloud\ObjectStore\Resource\Container */ $container = $service->getContainer('{containerName}'); +`Get the executable PHP script for this example `_ + Retrieve a container's name ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,6 +105,8 @@ Retrieve a container's object count $count = $container->getObjectCount(); +`Get the executable PHP script for this example `_ + Retrieve a container's total bytes used ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -106,6 +115,8 @@ Retrieve a container's total bytes used $bytes = $container->getBytesUsed(); +`Get the executable PHP script for this example `_ + Delete container ---------------- @@ -129,6 +140,8 @@ before deleting it. This is done for you if you set the You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ first, and then call ``delete``. +`Get the executable PHP script for this example `_ + Deleting all objects inside a container --------------------------------------- @@ -137,6 +150,8 @@ Deleting all objects inside a container $container->deleteAllObjects(); +`Get the executable PHP script for this example `_ + Create or update container metadata ----------------------------------- @@ -158,6 +173,8 @@ to the current metadata: 'Publisher' => 'Hogarth' )); +`Get the executable PHP script for this example `_ + Container quotas ---------------- @@ -184,6 +201,11 @@ And to retrieve them: echo $container->getCountQuota(); echo $container->getBytesQuota(); +Get the executable PHP scripts for this example: + +* `Set bytes quota `_ +* `Set count quota `_ + Access log delivery ------------------- diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index e38e0eb8b..0eb0ea836 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -43,6 +43,8 @@ its path: The resource handle will be automatically closed by Guzzle in its destructor, so there is no need to execute ``fclose``. +`Get the executable PHP script for this example `_ + Upload a single file (under 5GB) with metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -80,6 +82,8 @@ file handle resource, or a string representation of object content (a temporary resource will be created in memory), and the third is an array of additional headers. +`Get the executable PHP script for this example `_ + Batch upload multiple files (each under 5GB) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -108,6 +112,8 @@ also specify *either* a path key (to an existing file), or a ``body``. The ``body`` can either be a PHP resource or a string representation of the content you want to upload. +`Get the executable PHP script for this example `_ + Upload large files (over 5GB) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -142,6 +148,8 @@ In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. To find out more details, please consult the `official documentation `_. +`Get the executable PHP script for this example `_ + List objects in a container --------------------------- @@ -166,6 +174,8 @@ docs objectList(array('prefix' => 'logFile_')); +`Get the executable PHP script for this example `_ + Get object ---------- @@ -313,6 +323,8 @@ destination path: Where ``container_2`` is the name of the container, and ``new_object_name`` is the name of the object inside the container that does not exist yet. +`Get the executable PHP script for this example `_ + Get object metadata ------------------- @@ -346,6 +358,8 @@ You can also update to get the latest metadata: $object->retrieveMetadata(); +`Get the executable PHP script for this example `_ + Update object metadata ---------------------- @@ -386,6 +400,8 @@ you want to append values to your metadata, use the correct method: $object->saveMetadata($metadata); +`Get the executable PHP script for this example `_ + Extract archive --------------- @@ -406,6 +422,8 @@ the first argument). If you do this, the API will create the containers necessary to house the extracted files - this is done based on the filenames inside the archive. +`Get the executable PHP script for this example `_ + Delete object ------------- @@ -414,6 +432,8 @@ Delete object $object->delete(); +`Get the executable PHP script for this example `_ + Delete multiple objects ----------------------- @@ -425,3 +445,5 @@ Bulk delete a set of paths: $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); $service->bulkDelete($pathsToBeDeleted); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/orchestration/stacks.rst-e b/doc/services/orchestration/stacks.rst-e deleted file mode 100644 index 29b6c4f7f..000000000 --- a/doc/services/orchestration/stacks.rst-e +++ /dev/null @@ -1,299 +0,0 @@ -Stacks -====== - -A stack is a running instance of a template. When a stack is created, -the `resources <#stack-resources>`__ specified in the template are -created. - - -Preview stack -------------- - -Before you create a stack from a template, you might want to see what -that stack will look like. This is called *previewing the stack*. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Preview a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to preview a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - -`Get the executable PHP script for this example `_ - - -Preview a stack from a template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to preview a stack as shown -in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) - )); - -`Get the executable PHP script for this example `_ - - -Create stack ------------- - -You can create a stack from a template. This operation takes one parameter, an -associative array, with the following keys: - -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ -| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | -+-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ - -Create a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to create a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Create a stack from a template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to create a stack as shown -in the following example: - -.. code-block:: php - - $stack = $orchestrationService->stack(); - $stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - -List stacks ------------ - -You can list all the stacks that you have created as shown in the -following example: - -.. code-block:: php - - $stacks = $orchestrationService->listStacks(); - foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - } - -`Get the executable PHP script for this example `_ - - -Get stack ---------- - -You can retrieve a specific stack using its name, as shown in the -following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->getStack('simple-lamp-setup'); - -`Get the executable PHP script for this example `_ - - -Get stack template ------------------- - -You can retrieve the template used to create a stack. Note that a JSON -string is returned, regardless of whether a JSON or YAML template was -used to create the stack. - -.. code-block:: php - - /** @var $stackTemplate string **/ - $stackTemplate = $stack->getTemplate(); - -`Get the executable PHP script for this example `_ - - -Update stack ------------- - -You can update a running stack. - -This operation takes one parameter, an associative array, with the -following keys: - -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| Name | Description | Data type | Required? | Default value | Example value | -+===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ -| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ -| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | -+-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ - - -Update a stack from a template file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored on your local computer as a JSON or YAML -file, you can use it to update a stack as shown in the following -example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Update Stack from Template URL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your template is stored as a JSON or YAML file in a remote location -accessible via HTTP or HTTPS, you can use it to update a stack as shown -in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ - - -Delete stack ------------- - -If you no longer need a stack and all its resources, you can delete the -stack *and* the resources as shown in the following example: - -.. code-block:: php - - $stack->delete(); - -`Get the executable PHP script for this example `_ - - -Abandon Stack -------------- - -.. note:: - - This operation returns data about the abandoned stack as a string. You can - use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ - operation. - -If you want to delete a stack but preserve all its resources, you can -abandon the stack as shown in the following example: - -.. code-block:: php - - /** @var $abandonStackData string **/ - $abandonStackData = $stack->abandon(); - file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); - -`Get the executable PHP script for this example `_ - - -Adopt stack ------------ - -If you have data from an abandoned stack, you can re-create the stack as -shown in the following example: - -.. code-block:: php - - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ - $stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 - )); - -`Get the executable PHP script for this example `_ diff --git a/doc/services/queues/claims.rst b/doc/services/queues/claims.rst index 7e375c8d3..1111cb9eb 100644 --- a/doc/services/queues/claims.rst +++ b/doc/services/queues/claims.rst @@ -67,6 +67,8 @@ limit parameter is optional. 'ttl' => 5 * Datetime::MINUTE )); +`Get the executable PHP script for this example `_ + Query claim ----------- diff --git a/doc/services/queues/messages.rst b/doc/services/queues/messages.rst index ea23caea3..3a5dee89d 100644 --- a/doc/services/queues/messages.rst +++ b/doc/services/queues/messages.rst @@ -50,6 +50,8 @@ Posting a single message 'ttl' => 2 * Datetime::DAY )); +`Get the executable PHP script for this example `_ + Post a batch of messages ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/services/queues/queues.rst b/doc/services/queues/queues.rst index 7958adfae..f324e3f3b 100644 --- a/doc/services/queues/queues.rst +++ b/doc/services/queues/queues.rst @@ -64,6 +64,8 @@ digits, underscores, and hyphens. $queue = $service->createQueue('new_queue'); +`Get the executable PHP script for this example `_ + Find queue details ------------------ @@ -122,3 +124,12 @@ in the queue, categorized by status. .. code-block:: php $queue->getStats(); + +Delete queue +------------ + +.. code-block:: php + + $queue->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/volume/snapshots.rst b/doc/services/volume/snapshots.rst index c2d8ef6cc..555824a9d 100644 --- a/doc/services/volume/snapshots.rst +++ b/doc/services/volume/snapshots.rst @@ -19,6 +19,8 @@ to create one: 'volume_id' => $volume->id )); +`Get the executable PHP script for this example `_ + List snapshots -------------- @@ -31,6 +33,8 @@ List snapshots /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ } +`Get the executable PHP script for this example `_ + To get details on a single snapshot ----------------------------------- @@ -39,6 +43,8 @@ To get details on a single snapshot $snapshot = $dallas->snapshot('{snapshotId}'); +`Get the executable PHP script for this example `_ + To delete a snapshot -------------------- @@ -46,3 +52,5 @@ To delete a snapshot .. code-block:: php $snapshot->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/services/volume/volumes.rst b/doc/services/volume/volumes.rst index 6a35bb135..a84ed0421 100644 --- a/doc/services/volume/volumes.rst +++ b/doc/services/volume/volumes.rst @@ -19,6 +19,9 @@ parameters are optional: 'display_description' => 'Used for large object storage' )); +`Get the executable PHP script for this example `_ + + List volumes ------------ @@ -30,6 +33,8 @@ List volumes /** @param $volumeType OpenCloud\Volume\Resource\Volume */ } +`Get the executable PHP script for this example `_ + Get details on a single volume ------------------------------ @@ -42,6 +47,8 @@ information on the specified volume: $volume = $dallas->volume(''); echo $volume->size; +`Get the executable PHP script for this example `_ + To delete a volume ------------------ @@ -50,6 +57,8 @@ To delete a volume $volume->delete(); +`Get the executable PHP script for this example `_ + Attach a volume to a server --------------------------- From 02eb989508a33b7fa060bb116ba1ca295b22beac Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 11:45:55 +0100 Subject: [PATCH 756/835] Add list CDN containers sample file --- samples/ObjectStore/list-cdn-containers.php | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 samples/ObjectStore/list-cdn-containers.php diff --git a/samples/ObjectStore/list-cdn-containers.php b/samples/ObjectStore/list-cdn-containers.php new file mode 100644 index 000000000..a71a8bed9 --- /dev/null +++ b/samples/ObjectStore/list-cdn-containers.php @@ -0,0 +1,40 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Object a CDN service object +$cdnService = $objectStoreService->getCdnService(); + +// 4. Get container list. +$containers = $cdnService->listContainers(); + +foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\CDNContainer **/ +} From 4839afc144e66ba298213373d9c6a169a2ca420f Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Mar 2015 10:09:13 -0700 Subject: [PATCH 757/835] Adding smoke test to apply security group to port. --- tests/OpenCloud/Smoke/Unit/Networking.php | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/OpenCloud/Smoke/Unit/Networking.php b/tests/OpenCloud/Smoke/Unit/Networking.php index 8ccd30a94..02ed838bb 100644 --- a/tests/OpenCloud/Smoke/Unit/Networking.php +++ b/tests/OpenCloud/Smoke/Unit/Networking.php @@ -272,6 +272,30 @@ protected function testSecurityGroupOperations() $securityGroup = $this->getService()->getSecurityGroup($securityGroup->getId()); $this->stepInfo('Security Group ID: ' . $securityGroup->getId()); $this->stepInfo('Security Group Name: ' . $securityGroup->getName()); + + $network1 = $this->getService()->createNetwork(array( + 'name' => 'test_network_for_test_port_sg' + )); + $this->cleanupNetworkIds[] = $network1->getId(); + + $subnet1 = $this->getService()->createSubnet(array( + 'cidr' => '192.165.66.0/25', + 'networkId' => $network1->getId(), + 'ipVersion' => 4, + 'name' => 'test_subnet_for_test_port_sg' + )); + $this->cleanupSubnetIds[] = $subnet1->getId(); + + $port1 = $this->getService()->createPort(array( + 'networkId' => $network1->getId(), + 'name' => 'test_port_for_test_port_sg' + )); + $this->cleanupPortIds[] = $port1->getId(); + + $this->step('Apply security group to port'); + $port1->update(array( + 'securityGroups' => array($securityGroup->getId()) + )); } protected function testSecurityGroupRuleOperations() From 5c8e38e234568b4dca8479cff265435a9d5d506e Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Wed, 11 Mar 2015 10:21:52 -0700 Subject: [PATCH 758/835] Minor formatting fix so code block renders correctly. --- doc/getting-started-with-rackspace.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/getting-started-with-rackspace.rst b/doc/getting-started-with-rackspace.rst index 35317a3e6..73aa3a8d4 100644 --- a/doc/getting-started-with-rackspace.rst +++ b/doc/getting-started-with-rackspace.rst @@ -138,7 +138,7 @@ Okay, you're ready to spin up a server: .. code-block:: php -use Guzzle\Http\Exception\BadResponseException; + use Guzzle\Http\Exception\BadResponseException; $server = $compute->server(); From f017f31110f647490c846d1c1ac2b10c7345f8a2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 10 Mar 2015 13:10:35 +0100 Subject: [PATCH 759/835] Add deprecation and redirect links to existing docs --- doc/services/compute/servers.rst | 36 + docs/getting-started-openstack.md | 200 +----- docs/getting-started.md | 182 +---- docs/userguide/Autoscale/Config.md | 46 +- docs/userguide/Autoscale/Groups.md | 95 +-- docs/userguide/Autoscale/Policies.md | 67 +- docs/userguide/Autoscale/Webhooks.md | 51 +- docs/userguide/Clients.md | 148 +---- docs/userguide/CloudMonitoring/Agents.md | 148 +---- docs/userguide/CloudMonitoring/Alarms.md | 55 +- docs/userguide/CloudMonitoring/Changelogs.md | 13 +- docs/userguide/CloudMonitoring/Checks.md | 140 +--- docs/userguide/CloudMonitoring/Entities.md | 54 +- docs/userguide/CloudMonitoring/Metrics.md | 78 +-- .../CloudMonitoring/Notifications.md | 202 +----- docs/userguide/CloudMonitoring/Service.md | 15 +- docs/userguide/CloudMonitoring/Views.md | 18 +- docs/userguide/CloudMonitoring/Zones.md | 45 +- docs/userguide/Compute/Images.md | 64 +- docs/userguide/Compute/Keypair.md | 90 +-- docs/userguide/Compute/Server.md | 179 +---- docs/userguide/Compute/Service.md | 17 +- docs/userguide/DNS/Domains.md | 229 +------ docs/userguide/DNS/Limits.md | 57 +- docs/userguide/DNS/Records.md | 86 +-- docs/userguide/DNS/Reverse-DNS.md | 70 +- docs/userguide/DNS/Service.md | 10 +- docs/userguide/Database/README.md | 115 +--- docs/userguide/Debugging.md | 88 +-- docs/userguide/Identity/Roles.md | 67 +- docs/userguide/Identity/Service.md | 23 +- docs/userguide/Identity/Tenants.md | 20 +- docs/userguide/Identity/Tokens.md | 83 +-- docs/userguide/Identity/Users.md | 127 +--- docs/userguide/Image/Images.md | 96 +-- docs/userguide/Image/Schemas.md | 158 +---- docs/userguide/Image/Sharing.md | 109 +-- docs/userguide/Image/Tags.md | 22 +- docs/userguide/Iterators.md | 117 +--- docs/userguide/LoadBalancer/README.md | 79 +-- docs/userguide/LoadBalancer/USERGUIDE.md | 625 +----------------- docs/userguide/Networking/README.md | 75 +-- docs/userguide/Networking/USERGUIDE.md | 616 +---------------- docs/userguide/ObjectStore/Access.md | 65 +- docs/userguide/ObjectStore/Account.md | 27 +- docs/userguide/ObjectStore/CDN/Container.md | 77 +-- docs/userguide/ObjectStore/CDN/Object.md | 19 +- docs/userguide/ObjectStore/README.md | 64 +- .../ObjectStore/Storage/Container.md | 181 +---- .../ObjectStore/Storage/Migrating.md | 82 +-- docs/userguide/ObjectStore/Storage/Object.md | 274 +------- docs/userguide/ObjectStore/USERGUIDE.md | 624 +---------------- docs/userguide/Orchestration/README.md | 80 +-- docs/userguide/Orchestration/USERGUIDE.md | 505 +------------- docs/userguide/Queues/Claim.md | 122 +--- docs/userguide/Queues/Message.md | 206 +----- docs/userguide/Queues/Queue.md | 155 +---- docs/userguide/README.md | 73 +- docs/userguide/Services.md | 46 +- docs/userguide/accessip.md | 49 +- docs/userguide/caching-credentials.md | 38 +- docs/userguide/dbaas.md | 317 +-------- docs/userguide/flavors.md | 58 +- docs/userguide/networks.md | 101 +-- docs/userguide/servers.md | 186 +----- docs/userguide/volumes.md | 171 +---- 66 files changed, 166 insertions(+), 8169 deletions(-) diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 8a55ce57f..0709bda9a 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -204,6 +204,42 @@ Updatable attributes | accessIPv6 | The IP version 6 address. | +--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Updating the access IP address(es) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example, you may have a private cloud with internal addresses in the +10.1.x range. However, you can access a server via a firewall device at +address 50.57.94.244. In this case, you can change the ``accessIPv4`` +attribute to point to the firewall: + +.. code-block:: php + + $server->update(array('accessIPv4' => '50.57.94.244')); + +When a client application retrieves the server’s information, it will +know that it needs to use the ``accessIPv4`` address to connect to the +server, and *not* the IP address assigned to one of the network +interfaces. + + +Retrieving the server’s IP address +---------------------------------- + +The ``Server::ip()`` method is used to retrieve the server’s IP address. +It has one optional parameter: the format (either IPv4 or IPv6) of the +address to return (by default, it returns the IPv4 address): + +.. code-block:: php + + // IPv4 + echo $server->ip(); + echo $server->ip(4); + + // IPv6 + echo $server->ip(6); + + Delete server ------------- diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index 6bb1ae260..24eda2779 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -1,201 +1,5 @@ # Installing the SDK -You must install through Composer, because this library has a few dependencies: +Our docs have moved! Please visit the below link: -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master -``` - -Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): - -```php -require 'vendor/autoload.php'; -``` - -And you're good to go! - -# Quick deep-dive: building some Nova instances - -In this example, you will write code that will create a Nova instance running Ubuntu. - -### 1. Setup the client and pass in your credentials - -To authenticate against Keystone: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( - 'username' => 'foo', - 'password' => 'bar', - 'tenantName' => 'baz' -)); -``` - -You will need to substitute in the public URL endpoint for your Keystone service, as well as your `username`, `password` -and `tenantName`. You can also specify your `tenantId` instead of `tenantName` if you prefer. - -### 2. Pick what service you want to use - -In this case, we want to use the Nova service: - -```php -$compute = $client->computeService('nova', 'regionOne'); -``` - -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. For OpenStack users, this must be retrieved and entered in your code. If you are unsure how to retrieve the service name, follow these steps: - -1. Setup the `$client` object, as above -2. Copy and run this code: - - ```php - $client->authenticate(); - - print_r($client->getCatalog()->getItems()); - ``` - -3. This will output all the items in your service catalog. Go through the outputted list and find your service, making note of the "name" field. This is the name you will need to enter as the first argument. You will also be able to see the available regions. - -The second argument is the region. The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. - -### 3. Select your server image - -Instances are based on "images", which are effectively just the type of operating system you want. Let's go through the -list and find an Ubuntu one: - -```php -$images = $compute->imageList(); -foreach ($images as $image) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntu = $image; - break; - } -} -``` - -Alternatively, if you already know the image ID, you can do this much easier: - -```php -$ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -``` - -## 4. Select your flavor - -There are different server specs - some which offer 1GB RAM, others which offer a much higher spec. The 'flavor' of an -instance is its hardware configuration. So if you want a 2GB instance but don't know the ID, you have to traverse the list: - -```php -$flavors = $compute->flavorList(); -foreach ($flavors as $flavor) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} -``` - -Again, it's much easier if you know the ID: - -```php -$twoGbFlavor = $compute->flavor('4'); -``` - -## 5. Thunderbirds are go! - -Okay, you're ready to spin up a server: - -```php -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntu, - 'flavor' => $twoGbFlavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf("Status: %s\nBody: %s\nHeaders: %s", $statusCode, $responseBody, implode(', ', $headers)); -} -``` - -As you can see, you're creating a server called "My lovely server" - this will take a few minutes for the build to -complete. You can always check the progress by logging into your Controller node and running: - -`nova list` - -You can also execute a polling function immediately after the `create` method that checks the build process: - -```php -use OpenCloud\Compute\Constants\ServerState; - -$callback = function($server) { - if (!empty($server->error)) { - var_dump($server->error); - exit; - } else { - echo sprintf( - "Waiting on %s/%-12s %4s%%", - $server->name(), - $server->status(), - isset($server->progress) ? $server->progress : 0 - ); - } -}; - -$server->waitFor(ServerState::ACTIVE, 600, $callback); -``` -So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. - -# More fun with Nova - -Once you've booted up your instance, you can use other API operations to monitor your Compute nodes. To list every -node on record, you can execute: - -```php -$servers = $compute->serverList(); - -foreach ($servers as $server) { - // do something with each server... - echo $server->name, PHP_EOL; -} -``` - -or, if you know a particular instance ID you can retrieve its details: - -```php -$server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); -``` - -allowing you to update its properties: - -```php -$server->update(array( - 'name' => 'New server name' -)); -``` - -or delete it entirely: - -```php -$server->delete(); -``` - -# Next steps - -Consult our [documentation](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide) about other services -you can use, like [Keystone](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/Identity) or -[Swift](https://github.com/rackspace/php-opencloud/tree/master/docs/userguide/ObjectStore). If you have any questions or -troubles, feel free to contact us at https://developer.rackspace.com/support/ or open a Github issue with details. +https://doc.php-opencloud.com/en/latest/getting-started-with-openstack.html diff --git a/docs/getting-started.md b/docs/getting-started.md index e661e0ce8..af65271fe 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,183 +1,5 @@ # Installing the SDK -You must install through Composer, because this library has a few dependencies: +Our docs have moved! Please visit the below link: -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Require php-opencloud as a dependency -php composer.phar require rackspace/php-opencloud:dev-master -``` - -Once you have installed the library, you will need to load Composer's autoloader (which registers all the required -namespaces): - -```php -require 'vendor/autoload.php'; -``` - -And you're good to go! - -# Quick deep-dive: building some cloud servers - -In this example, you will write code that will create a Cloud Server running Ubuntu. - -### 1. Setup the client and pass in your credentials - -To authenticate against the Rackspace API and use its services: - -```php - 'foo', - 'apiKey' => 'bar' -)); -``` - -Alternatively, if you would like to validate against your own API, or just want to access OpenStack services: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://identity.my-openstack.com/v2.0/', array( - 'username' => 'foo', - 'password' => 'bar' -)); -``` - -You can see in the first example that the constant `Rackspace::US_IDENTITY_ENDPOINT` is just a string representation of -Rackspace's identity endpoint (`https://identity.api.rackspacecloud.com/v2.0/`). Another difference is that Rackspace -uses API key for authentication, whereas OpenStack uses a generic password. - -#### 1.2 Logger injection -As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. - -Prerequisities and usage example can be found in [the Clients userguide](/docs/userguide/Clients.md#12-logger-injection) - -### 2. Pick what service you want to use - -In this case, we want to use the Compute (Nova) service: - -```php -$compute = $client->computeService('cloudServersOpenStack', 'ORD'); -``` - -The first argument is the __name__ of the service as it appears in the OpenStack service catalog. If in doubt, you can -leave blank and it will revert to the default name for the service. The second argument is the region; you may use: - -- __DFW__ (Dallas) -- __ORD__ (Chicago) -- __IAD__ (Virginia) -- __LON__ (London) -- __HKG__ (Hong Kong) -- __SYD__ (Sydney) - -The third and last argument is the type of URL; you may use either `publicURL` or `internalURL`. If you select `internalUrl` -all API traffic will use ServiceNet (internal IPs) and will receive a performance boost. - -### 3. Select your server image - -Servers are based on "images", which are effectively just the type of operating system you want. Let's go through the list -and find an Ubuntu one: - -```php -$images = $compute->imageList(); -while ($image = $images->next()) { - if (strpos($image->name, 'Ubuntu') !== false) { - $ubuntu = $image; - break; - } -} -``` - -Alternatively, if you already know the image ID, you can do this much easier: - -```php -$ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -``` - -## 4. Select your flavor - -There are different server specs - some which offer 1GB RAM, others which offer a much higher spec. The 'flavor' of a -server is its hardware configuration. So if you want a 2GB instance but don't know the ID, you have to traverse the list: - -```php -$flavors = $compute->flavorList(); -while ($flavor = $flavors->next()) { - if (strpos($flavor->name, '2GB') !== false) { - $twoGbFlavor = $flavor; - break; - } -} -``` - -Again, it's much easier if you know the ID: - -```php -$twoGbFlavor = $compute->flavor('4'); -``` - -## 5. Thunderbirds are go! - -Okay, you're ready to spin up a server: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntu, - 'flavor' => $twoGbFlavor, - 'networks' => array( - $compute->network(Network::RAX_PUBLIC), - $compute->network(Network::RAX_PRIVATE) - ) - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf("Status: %s\nBody: %s\nHeaders: %s", $statusCode, $responseBody, implode(', ', $headers)); -} -``` - -As you can see, you're creating a server called "My lovely server", and you've inserted it in two networks: the Rackspace -private network (ServiceNet), and the Rackspace public network (for Internet connectivity). This will take a few -minutes for the build to complete. - -You can also call a polling function that checks on the build process: - -```php -use OpenCloud\Compute\Constants\ServerState; - -$callback = function($server) { - if (!empty($server->error)) { - var_dump($server->error); - exit; - } else { - echo sprintf( - "Waiting on %s/%-12s %4s%%", - $server->name(), - $server->status(), - isset($server->progress) ? $server->progress : 0 - ); - } -}; - -$server->waitFor(ServerState::ACTIVE, 600, $callback); -``` -So, the server will be polled until it is in an `ACTIVE` state, with a timeout of 600 seconds. When the poll happens, the -callback function is executed - which in this case just logs some output. +https://doc.php-opencloud.com/en/latest/getting-started-with-rackspace.html diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md index a619bf277..02f2e891d 100644 --- a/docs/userguide/Autoscale/Config.md +++ b/docs/userguide/Autoscale/Config.md @@ -1,47 +1,5 @@ # Group configurations -##Intro +Our docs have moved! Please visit the below link: -There are two types of configuration associated with an Auto Scale group: - -- **Group configuration**. Outlines the basic elements of the Auto Scale configuration. The group configuration manages how many servers can participate in the scaling group. It sets a minimum and maximum limit for the number of entities that can be used in the scaling process. It also specifies information related to load balancers. - -- **Launch configuration**. Creates a blueprint for how new servers will be created. The launch configuration specifies what type of server image will be started on launch, what flavor the new server is, and which load balancer the new server connects to. - -## Setup - -To interact with the configuration of a scaling group, you will need to setup the group object beforehand: - -```php -$groupId = 'e41380ae-173c-4b40-848a-25c16d7fa83d'; -$group = $service->getGroup($groupId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [groups](). - -## Get group/launch configuration - -```php -$groupConfig = $group->getGroupConfig(); -$launchConfig = $group->getLaunchConfig(); -``` - -## Edit group/launch configuration - -```php -$groupConfig->update(array( - 'name' => 'New name!' -)); - -$launchConfig = $group->getLaunchConfig(); - -$server = $launchConfig->args->server; -$server->name = "BRAND NEW SERVER NAME"; - -$launchConfig->update(array - 'args' => array( - 'server' => $server, - 'loadBalancers' => $launchConfig->args->loadBalancers - ) -)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/group-config.html diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md index 98dfb43dc..7ec7aa269 100644 --- a/docs/userguide/Autoscale/Groups.md +++ b/docs/userguide/Autoscale/Groups.md @@ -1,96 +1,5 @@ #Auto Scale groups -## Intro +Our docs have moved! Please visit the below link: -The scaling group is at the heart of an Auto Scale deployment. The scaling group specifies the basic elements of the Auto Scale configuration. It manages how many servers can participate in the scaling group. It also specifies information related to load balancers if your configuration uses a load balancer. - -## Service setup - -Nothing special here; you setup your client and service objects in the same way as every other resource: - -```php -$service = $client->autoscaleService(); -``` - -Please consult the [client doc](docs/userguide/Client.md) for more information about creating clients. - -## List all groups - -```php -$groups = $service->groupList(); -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -## Retrieve one group by ID - -```php -$group = $service->group('605e13f6-1452-4588-b5da-ac6bb468c5bf'); -``` - -## Create a new group - -You can create a new scaling group in two ways: either pass in a JSON string (easier option); or instantiate a new object and manually set its properties (a lot more verbose) - -```php - -// Easy way: - -$jsonString = <<create($jsonString); - -// More granular (and verbose) way: - -$object = new \stdClass; - -// Set the config object for this autoscale group; contains all of properties -// which determine its behaviour -$object->groupConfiguration = new \stdClass; -$object->groupConfiguration->name = 'New autoscale group'; -$object->groupConfiguration->minEntities = 5; -$object->groupConfiguration->maxEntities = 25; -$object->groupConfiguration->cooldown = 60; - -// We need specify what is going to be launched. For now, we'll launch a new server -$object->launchConfiguration = new \stdClass; -$object->launchConfiguration->type = 'launch_server'; - -// To launch a new server, we need two `args` properties: `server` and `loadBalancer` -$server = new \stdClass; -$server->flavorRef = 3; -$server->name = 'webhead'; -$server->imageRef = "0d589460-f177-4b0f-81c1-8ab8903ac7d8"; - -$loadBalancer = new \stdClass; -$loadBalancer->loadBalancerId = 2200; -$loadBalancer->port = 8081; - -$object->launchConfiguration->args = new \stdClass; -$object->launchConfiguration->args->server = $server; -$object->launchConfiguration->args->loadBalancers = array($loadBalancer); - -// Do we want particular scaling policies? -$policy = new \stdClass; -$policy->name = "scale up by 10"; -$policy->change = 10; -$policy->cooldown = 5; -$policy->type = "webhook"; - -$object->scalingPolicies = array($policy); - -$group->create($object); -``` - -## Delete an autoscale group -```php -$group->delete(); -``` - -## Get the current state of the scaling group - -```php -$group->getState(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/groups.html diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md index 6f3d1b282..7a312af50 100644 --- a/docs/userguide/Autoscale/Policies.md +++ b/docs/userguide/Autoscale/Policies.md @@ -1,68 +1,5 @@ #Policies -## Setup +Our docs have moved! Please visit the below link: -To interact with the policies of a scaling group, you will need to setup the group object beforehand. - -```php -$groupId = 'foobar'; -$group = $service->getGroup($groupId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [Autoscale groups](). - -## Get all policies - -```php -$policies = $group->getPolicies(); - -while ($policy = $policies->next()) { - // do something - echo "{$policy->name} {($policy->type)}" . PHP_EOL; -} -``` - -## Create a new scaling policy - -Creating policies is achieved through passing an array to the `create` method. - -```php -$policy = new \stdClass; -$policy->name = "NEW NAME"; -$policy->change = 1; -$policy->cooldown = 150; -$policy->type = "webhook"; - -$group->getPolicy()->create(array($policy)); - -// or even: - -$group->getPolicy()->create(array( - (object) array( - 'name' => 'NEW NAME', - 'change' => 1, - 'cooldown' => 150, - 'type' => 'webhook' - ), - // etc. -)); -``` - -## Get, update and delete a scaling policy - -```php -$policyId = 'foobar'; -$policy = $group->getPolicy($policyId); - -$policy->update(array( - 'name' => 'More relevant name' -)); - -$policy->delete(); -``` - -## Execute a scaling policy - -```php -$policy->execute(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/policies.html diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md index e634cc073..c5e6453ed 100644 --- a/docs/userguide/Autoscale/Webhooks.md +++ b/docs/userguide/Autoscale/Webhooks.md @@ -1,52 +1,5 @@ # Webhooks -## Setup +Our docs have moved! Please visit the below link: -To interact with the webhooks of a group's scaling policy, you will need to setup the group and policy objects beforehand. - -```php -$groupId = 'foo'; -$policyId = 'bar'; - -$group = $service->getGroup($groupId); -$policy = $group->getPolicy($policyId); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [Autoscale groups](). - -## Get all webhooks - -```php -$webhooks = $policy->getWebookList(); -``` - -## Create a new webhook - -```php -$policy->getWebhook()->create(array( - (object) array( - 'name' => 'Alice', - 'metadata' => array( - 'firstKey' => 'foo', - 'secondKey' => 'bar' - ) - ) -)); -``` - -## Get, update and delete an individual webhook - -```php -$webhookId = 'baz'; -$webhook = $policy->getWebhook($webhookId); - -// Update the metadata -$metadata = $webhook->metadata; -$metadata->thirdKey = 'blah'; -$webhook->update(array( - 'metadata' => $metadata -)); - -// Delete it -$webhook->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/autoscale/webhooks.html diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index c7270d69e..184b601f4 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -1,149 +1,5 @@ # Clients -## Overview +Our docs have moved! Please visit the below link: -A Client is the object responsible for issuing HTTP requests and receiving responses from the API. In short, it forms the core of the SDK because it controls how functionality is executed. All services depend on the client to work. - -Users have access to two types of client: `OpenCloud\OpenStack` and `OpenCloud\Rackspace`. The latter extends the former, meaning that much of the functionality is shared between them. The OpenStack client extends functionality from other base classes, that trace all the way back to Guzzle's root class: - -1. `Guzzle\Http\Client` -2. `OpenCloud\Common\Http\Client` -3. `OpenCloud\OpenStack` -4. `OpenCloud\Rackspace` - -## 1. Initializing a client - -### Rackspace - -First, you need to select the Identity endpoint you want to authenticate against. If you're using Rackspace, you can either use the UK or US endpoints. There are class constants defined for your convenience: - -- `OpenCloud\Rackspace::US_IDENTITY_ENDPOINT` (https://identity.api.rackspacecloud.com/v2.0) -- `OpenCloud\Rackspace::UK_IDENTITY_ENDPOINT` (https://lon.identity.api.rackspacecloud.com/v2.0) - -Then you need to find your username and apiKey. Your username will be visible at the top right of the Rackspace Control panel; and your API key can be retrieved by going to Account Settings. Once this is done: - -```php -use OpenCloud\OpenStack; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' -)); -``` - -### OpenStack - -To initialize an OpenStack client, the process is the same: - -```php -use OpenCloud\OpenStack; - -$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( - 'username' => 'foo', - 'password' => 'bar' -)); -``` - -#### 1.2 Logger injection -As the `Rackspace` client extends the `OpenStack` client, they both support passing `$options` as an array via the constructor's third parameter. The options are passed as a config to the `Guzzle` client, but also allow to inject your own `Logger`. - -Your logger should implement the `Psr\Log\LoggerInterface` [as defined in PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). Example of a compatible logger is [`Monolog`](https://github.com/Seldaek/monolog). When the client does create a service, it will inject the logger if one is available. - -To inject a `LoggerInterface` compatible logger into a new `Client`: - -```php -use Monolog\Logger; -use OpenCloud\OpenStack; - -// create a log channel -$log = new Logger('name'); - -$client = new OpenStack('http://identity.my-openstack.com/v2.0', array( - 'username' => 'foo', - 'password' => 'bar' -), array( - 'logger' => $log, -)); -``` - -## 2. Authentication - -The Client does not automatically authenticate against the API on object creation - it waits for an API call. When this happens, it checks whether the current "token" has expired, and (re-)authenticates if necessary. - -You can force authentication, by calling: - -```php -$client->authenticate(); -``` - -If the credentials are incorrect, a `401` error will be returned. If credentials are correct, a `200` status is returned with your Service Catalog. - -## 3. Service Catalog - -The Service Catalog is returned on successful authentication, and is composed of all the different API services available to the current tenant. All of this functionality is encapsulated in the `Catalog` object, which allows you greater control and interactivity. - -```php -/** @var OpenCloud\Common\Service\Catalog */ -$catalog = $client->getCatalog(); - -// Return a list of OpenCloud\Common\Service\CatalogItem objects -foreach ($catalog->getItems() as $catalogItem) { - - $name = $catalogItem->getName(); - $type = $catalogItem->getType(); - - if ($name == 'cloudServersOpenStack' && $type == 'compute') { - break; - } - - // Array of OpenCloud\Common\Service\Endpoint objects - $endpoints = $catalogItem->getEndpoints(); - foreach ($endpoints as $endpoint) { - if ($endpoint->getRegion() == 'DFW') { - echo $endpoint->getPublicUrl(); - } - } -} -``` - -As you can see, you have access to each Service's name, type and list of endpoints. Each endpoint provides access to the specific region, along with its public and private endpoint URLs. - -## 4. Default HTTP headers - -To set default HTTP headers: - -```php -$client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); -``` - -## User agents - -php-opencloud will send a default `User-Agent` header for every HTTP request, unless a custom value is provided by the end-user. The default header will be in this format: - -> User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz - -where `xxx` is the current version of the SDK, `yyy` is the current version of cURL, and `zzz` is the current PHP version. To override this default, you must run: - -```php -$client->setUserAgent('MyCustomUserAgent'); -``` - -which will result in: - -> User-Agent: MyCustomUserAgent - -If you want to set a _prefix_ for the user agent, but retain the default `User-Agent` as a suffix, you must run: - -```php -$client->setUserAgent('MyPrefix', true); -``` - -which will result in: - -> User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz - -where `$client` is an instance of `OpenCloud\OpenStack` or `OpenCloud\Rackspace`. - -## 5. Other functionality - -For a full list of functionality provided by Guzzle, please consult the [official documentation](http://docs.guzzlephp.org/en/latest/http-client/client.html). +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md index 7e67e7d0b..b316d004c 100644 --- a/docs/userguide/CloudMonitoring/Agents.md +++ b/docs/userguide/CloudMonitoring/Agents.md @@ -1,149 +1,5 @@ # Agents -## Intro +Our docs have moved! Please visit the below link: -The Monitoring Agent resides on the host server being monitored. The agent allows you to gather on-host metrics based on agent checks and push them to Cloud Monitoring where you can analyze them, use them with the Cloud Monitoring infrastructure (such as alarms), and archive them. - -For more information about this feature, including a brief overview of its core design principles and security layers, see the [official API documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/service-agent.html). - -## Setup - -```php -$agentId = '00-agent.example.com'; -$agent = $service->getAgent($agentId); -``` - -You can view the [service page](Service.md) for more information about setting up the Cloud Monitoring service. - -## List agents - -```php -$agents = $service->getAgents(); - -foreach ($agents as $agent) { - echo $agent->getLastConnected(); -} -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## List connections - -```php -$connections = $agent->getConnections(); -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Get connection -```php -/** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ -$connection = $agent->getConnection('cntl4qsIbA'); -``` - -### Agent Connection properties - -Name|Description|Type|Method -----|-----------|----|------ -id|-|-|`getId()` -guid|-|-|`getGuid()` -agent_id|-|-|`getAgentId()` -endpoint|-|-|`getEndpoint()` -process_version|-|-|`getProcessVersion()` -bundle_version|-|-|`getBundleVersion()` -agent_ip|-|-|`getAgentIp()` - - -# Agent tokens - -## Intro - -Agent tokens are used to authenticate Monitoring agents to the Monitoring Service. Multiple agents can share a single token. - -## Setup -```php -$tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; -$agentToken = $service->getAgentToken($tokenId); -``` - -## Create agent token -```php -$newToken = $service->getAgentToken(); -$newToken->create(array('label' => 'Foobar')); -``` - -## List agent tokens -```php -$agentTokens = $service->getAgentTokens(); - -foreach ($agentTokens as $token) { - echo $token->getLabel(); -} -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Update and delete Agent Token -```php -// Update -$token->update(array( - 'label' => 'New label' -)); - -// Delete -$token->delete(); -``` - -# Agent Host Information - -## Info - -An agent can gather host information, such as process lists, network configuration, and memory usage, on demand. You can use the host-information API requests to gather this information for use in dashboards or other utilities. - -## Setup -```php -$host = $monitoringService->getAgentHost(); -``` - -## Get some metrics -```php -$cpuInfo = $host->info('cpus'); -$diskInfo = $host->info('disks'); -$filesystemInfo = $host->info('filesystems'); -$memoryInfo = $host->info('memory'); -$networkIntInfo = $host->info('network_interfaces'); -$processesInfo = $host->info('processes'); -$systemInfo = $host->info('system'); -$userInfo = $host->info('who'); - -// What CPU models do we have? -foreach ($cpuInfo as $cpuMetric) { - echo $cpuMetric->model, PHP_EOL; -} - -// How many disks do we have? -echo $diskInfo->count(); - -// What's the available space on our ext4 filesystem? -foreach ($filesystemInfo as $filesystemMetric) { - if ($filesystemMetric->sys_type_name == 'ext4') { - echo $filesystemMetric->avail; - } -} -``` - -# Agent targets - -## Info - -Each agent check type gathers data for a related set of target devices on the server where the agent is installed. For example, `agent.network` gathers data for network devices. The actual list of target devices is specific to the configuration of the host server. By focusing on specific targets, you can efficiently narrow the metric data that the agent gathers. - -### List agent targets -```php -$targets = $service->getAgentTargets(); - -foreach ($targets as $target) { - echo $target->getType(); -} - -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/agents.html diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md index bf4a75652..b08caf4cd 100644 --- a/docs/userguide/CloudMonitoring/Alarms.md +++ b/docs/userguide/CloudMonitoring/Alarms.md @@ -1,56 +1,5 @@ # Alarms -## Info +Our docs have moved! Please visit the below link: -Alarms bind alerting rules, entities, and notification plans into a logical unit. Alarms are responsible for determining a state (```OK```, ```WARNING``` or ```CRITICAL```) based on the result of a Check, and executing a notification plan whenever that state changes. You create alerting rules by using the alarm DSL. For information about using the alarm language, refer to the [reference documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/alerts-language.html). - -## Setup - -Alarms are sub-resources of Entities: - -```php -$alarmId = 'alAAAA'; -$alarm = $check->getAlarm(); -``` - -For more information about working with Checks, please see the [appropriate documentation](Checks.md). - -## Attributes - -Name|Description|Required?|Method ----|---|---|--- -check_id|The ID of the check to alert on.|Required|`getCheckId()` -notification_plan_id|The ID of the notification plan to execute when the state changes.|Optional|`getNotificationPlanId()` -criteria|The alarm DSL for describing alerting conditions and their output states.|Optional|`getCriteria()` -disabled|Disable processing and alerts on this alarm|Optional|`isDisabled()` <`bool`> -label|A friendly label for an alarm.|Optional|`getLabel()` -metadata|Arbitrary key/value pairs.|Optional|`getMetadata()` - -## Create Alarm -```php -$alarm->create(array( - 'check_id' => 'chAAAA', - 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', - 'notification_plan_id' => 'npAAAAA' -)); -``` - -## List Alarms -```php -$alarms = $entity->getAlarms(); - -foreach ($alarms as $alarm) { - echo $alarm->getId(); -} -``` - -## Update and delete Alarm -```php -// Update -$alarm->update(array( - 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' -)); - -// Delete -$alarm->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/alarms.html diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md index 0f8fe48d0..ff943e592 100644 --- a/docs/userguide/CloudMonitoring/Changelogs.md +++ b/docs/userguide/CloudMonitoring/Changelogs.md @@ -1,14 +1,5 @@ # Changelogs -## Info +Our docs have moved! Please visit the below link: -The monitoring service records changelogs for alarm statuses. Changelogs are accessible as a Time Series Collection. By default the API queries the last 7 days of changelog information. - -## Working with Changelogs -```php -$changelog = $service->getChangelog(); - -foreach ($changelog as $item) { - $entity = $item->getEntityId(); -} -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/changelogs.html diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md index 265c0675d..5710aa465 100644 --- a/docs/userguide/CloudMonitoring/Checks.md +++ b/docs/userguide/CloudMonitoring/Checks.md @@ -1,141 +1,5 @@ # Checks -## Info +Our docs have moved! Please visit the below link: -A check is one of the foundational building blocks of the monitoring system. The check determines the parts or pieces of the entity that you want to monitor, the monitoring frequency, how many monitoring zones are originating the check, and so on. When you create a new check in the monitoring system, you specify the following information: - -- A name for the check -- The check's parent entity -- The type of check you're creating -- Details of the check -- The monitoring zones that will launch the check - -The check, as created, will not trigger alert messages until you create an alarm to generate notifications, to enable the creation of a single alarm that acts upon multiple checks (e.g. alert if any of ten different servers stops responding) or multiple alarms off of a single check. (e.g. ensure both that a HTTPS server is responding and that it has a valid certificate). - -## Setup - -Checks are sub-resources of Entities: -```php -$checkId = 'chAAAA'; -$check = $entity->getCheck($checkId); -``` - -## Attributes - -Name|Description|Required?|Data type ----|---|---|--- -type|The type of check.|Required|Valid check type. String (1..25 chars) -details|Details specific to the check type.|Optional|Array -disabled|Disables the check.|Optional|Boolean -label|A friendly label for a check.|Optional|String (1..255 chars) -metadata|Arbitrary key/value pairs.|Optional|Array -period|The period in seconds for a check. The value must be greater than the minimum period set on your account.|Optional|Integer (30..1800) -timeout|The timeout in seconds for a check. This has to be less than the period.|Optional|Integer (2..1800) - -###Attributes used for remote Checks - -Name|Description|Required?|Data type ----|---|---|--- -monitoring_zones_poll|List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks|Optional|Array -target_alias|A key in the entity's `ip_addresses` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target_hostname.|Optional|String (1..64 chars) -target_hostname|The hostname this check should target. This parameter is mutually exclusive with `target_alias`.|Optional|Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). -target_resolver|Determines how to resolve the check target.|Optional|`IPv4` or `IPv6` - -## Test parameters (before create) -```php -$params = array( - 'type' => 'remote.http', - 'details' => array( - 'url' => 'http://example.com', - 'method' => 'GET' - ), - 'monitoring_zones_poll' => array('mzlon'), - 'period' => '100', - 'timeout' => '30', - 'target_alias' => 'default', - 'label' => 'Website check 1' -); - -// You can do a test to see what would happen -// if a Check is launched with these params -$response = $entity->testNewCheckParams($params); - -echo $response->timestamp; // When was it executed? -echo $response->available; // Was it available? -echo $response->status; // Status code -``` -## Create a Check -```php -$entity->createCheck($params); -``` - -## Test existing Check -```php -// Set arg to TRUE for debug information -$response = $check->test(true); - -echo $response->debug_info; -``` - -## List Checks -```php -$checks = $entity->getChecks(); - -foreach ($checks as $check) { - echo $check->getId(); -} -``` - -### Update and delete Check -```php -// Update -$check->update(array('period' => 500)); - -// Delete -$check->delete(); -``` - -# Check types - -## Info - -Each check within the Rackspace Cloud Monitoring has a designated check type. The check type instructs the monitoring system how to check the monitored resource. **Note:** Users cannot create, update or delete check types. - -Check types for commonly encountered web protocols, such as HTTP (```remote.http```), IMAP (```remote.imap-banner```) , SMTP (```remote.stmp```), and DNS (```remote.dns```) are provided. Monitoring commonly encountered infrastructure servers like MySQL (```remote.mysql-banner```) and PostgreSQL (```remote.postgresql-banner```) are also available. Monitoring custom server uptime can be accomplished with the remote.tcp banner check to check for a protocol-defined banner at the beginning of a connection. Gathering metrics from server software to create alerts against can be accomplished using the remote.http check type and the 'extract' attribute to define the format. - -In addition to the standard Cloud Monitoring check types, you can also use agent check types if the Monitoring Agent is installed on the server you are monitoring. For a list of available check types, see the [official API documentation](http://docs.rackspace.com/cm/api/v1.0/cm-devguide/content/appendix-check-types.html). - -Checks generate metrics that alarms will alert based upon. The metrics generated often times depend on the check's parameters. For example, using the 'extract' attribute on the remote.http check, however the default metrics will always be present. To determine the exact metrics available, the Test Check API is provided. - -## Setup - -If you want to see the type for an existing Check: - -```php -/** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ -$checkType = $check->getCheckType(); -``` - -Alternatively, you can retrieve a specific type based on its ID: - -```php -$checkTypeId = 'remote.dns'; -$checkType = $service->getCheckType($checkTypeId); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|--- -type|The name of the supported check type.|String|`getType()` -fields|Check type fields.|Array|`getFields()` -supported_platforms|Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime|Array|`getSupportedPlatforms()` - -## List all possible check types -```php -$checkTypes = $service->getCheckTypes(); - -foreach ($checkTypes as $checkType) { - echo $checkType->getId(); -} -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/checks.html diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md index c9885604b..beed807a2 100644 --- a/docs/userguide/CloudMonitoring/Entities.md +++ b/docs/userguide/CloudMonitoring/Entities.md @@ -1,55 +1,5 @@ # Entities -## Info +Our docs have moved! Please visit the below link: -An entity is the target of what you are monitoring. For example, you can create an entity to monitor your website, a particular web service, or your Rackspace server. Note that an entity represents only one item in the monitoring system -- if you wanted to monitor each server in a cluster, you would create an entity for each of the servers. You would not create a single entity to represent the entire cluster. - -An entity can have multiple checks associated with it. This allows you to check multiple services on the same host by creating multiple checks on the same entity, instead of multiple entities each with a single check. - -## Setup - -```php -$entity = $service->getEntity(); -``` - -For more information about setting up the `$service` object, please see the userguide tutorial for [services](Service.md). - -## Attributes - -Name|Description|Required?|Data type|Method ----|---|---|---|--- -label|Defines a name for the entity.|Required|String (1..255 chars)|`getLabel()` -agent_id|Agent to which this entity is bound to.|Optional|String matching the regex: `/^[-\.\w]{1,255}$/`|`getAgentId()` -ip_addresses|Hash of IP addresses that can be referenced by checks on this entity.|Optional|Array|`getIpAddresses()` -metadata|Arbitrary key/value pairs that are passed during the alerting phase.|Optional|`OpenCloud\Common\Metadata`|`getMetadata()` - -## Create Entity -```php -$service->createEntity(array( - 'label' => 'Brand New Entity', - 'ip_addresses' => array( - 'default' => '127.0.0.4', - 'b' => '127.0.0.5', - 'c' => '127.0.0.6', - 'test' => '127.0.0.7' - ), - 'metadata' => array( - 'all' => 'kinds', - 'of' => 'stuff', - 'can' => 'go', - 'here' => 'null is not a valid value' - ) -)); -``` - - -## Update and delete Entity -```php -// Update -$entity->update(array( - 'label' => 'New label for my entity' -)); - -// Delete -$entity->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/entities.html diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md index 6799f8881..0b275c97e 100644 --- a/docs/userguide/CloudMonitoring/Metrics.md +++ b/docs/userguide/CloudMonitoring/Metrics.md @@ -1,79 +1,5 @@ # Metrics -## Info +Our docs have moved! Please visit the below link: -When Monitoring checks run, they generate metrics. These metrics are stored as full resolution data points in the Cloud Monitoring system. Full resolution data points are periodically rolled up (condensed) into coarser data points. - -Depending on your needs, you can use the metrics API to fetch individual data points (fine-grained) or rolled up data points (coarse-grained) over a period of time. - -## Data Granularity - -Cloud Monitoring supports several granularities of data: full resolution data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. - -When you fetch metrics data points, you specify several parameters to control the granularity of data returned: - -- A time range for the points -- Either the number of points you want returned OR the resolution of the data you want returned - -When you query by points, the API selects the resolution that will return you the number of points you requested. The API makes the assumption of a 30 second frequency, performs the calculation, and selects the appropriate resolution. - -**Note:** Because the API performs calculations to determine the points returned for a particular resolution, the number of points returned may differ from the specific number of points you request. - -Consider that you want to query data for a 48-hour time range between the timestamps `from=1354647221000` and `to=1358794421000` ( **specified in Unix time, based on the number of milliseconds that have elapsed since January 1, 1970** ). The following table shows the number of points that the API returns for a given resolution. - -#### Specifying resolution to retrieve data in 48 hour period - -You specify resolution...|API returns points... ----|--- -FULL|5760 -MIN5|576 -MIN20|144 -MIN60|48 -MIN240|12 -MIN1440|2 - -#### Specifying number of points to retrieve data in 48 hour period - -You specify points in the range...|API calculates resolution ----|--- -3168-∞|FULL -360-3167|MIN5 -96-359|MIN20 -30-95|MIN60 -7-29|MIN240 -0-6|MIN1440 - -#### Data Point Expiration - -Cloud Monitoring expires data points according to the following schedule: - -Resolution|Expiration ----|--- -FULL|2 days -MIN5|7 days -MIN20|15 days -MIN60|30 days -MIN240|60 days -MIN1440|365 days - -## Setup - -Metrics are sub-resources of Checks. For more information about working with Checks, please see the [relevant documentation](Checks.md). - -## List all metrics -```php -$metrics = $check->getMetrics(); - -foreach ($metrics as $metric) { - echo $metric->getName(); -} -``` - -## Fetch data points -```php -$data = $check->fetchDataPoints('mzdfw.available', array( - 'resolution' => 'FULL', - 'from' => 1369756378450, - 'to' => 1369760279018 -)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/metrics.html diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md index 81a890f56..129881fae 100644 --- a/docs/userguide/CloudMonitoring/Notifications.md +++ b/docs/userguide/CloudMonitoring/Notifications.md @@ -1,203 +1,5 @@ # Notifications -## Info +Our docs have moved! Please visit the below link: -A notification is a destination to send an alarm; it can be a variety of different types, and will evolve over time. - -For instance, with a webhook type notification, Cloud Monitoring posts JSON formatted data to a user-specified URL on an alert condition (Check goes from `OK` -> `CRITICAL` and so on). - -## Setup - -```php -$id = 'ntAAAA'; -$notification = $service->getNotification($id); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|---|--- -details|A hash of notification specific details based on the notification type.|Array|`getDetails()` -label|Friendly name for the notification.|String (1..255 chars)|`getLabel()` -type|The notification type to send.|String. Either `webhook`, `email`, or `pagerduty`|`getType()` - -## Test parameters -```php -$params = array( - 'label' => 'My webhook #1', - 'type' => 'webhook', - 'details' => array( - 'url' => 'http://example.com' - ) -); - -// Test it -$response = $notification->testParams($params); - -if ($response->status == 'Success') { - echo $response->message; -} -``` - -## Create Notification - -```php -$notification->create($params); -``` - -## Test existing notification -```php -$response = $notification->testExisting(true); -echo $response->debug_info; -``` - -## List Notifications -```php -$notifications = $service->getNotifications(); - -foreach ($notifications as $notification) { - echo $notification->getId(); -} -``` - -## Update and delete Notifications -```php -// Update -$notification->update(array( - 'label' => 'New notification label' -)); - -// Delete -$notification->delete(); -``` - -# Notification types - -## Info - -Pretty self-explanatory. Rackspace Cloud Monitoring currently supports the following notification types: - -#### Webhook - -Industry-standard web hooks, where JSON is posted to a configurable URL. It has these attributes: - -Name|Description|Data type ----|---|--- -address|Email address to send notifications to|Valid email - -#### Email - -Email alerts where the message is delivered to a specified address. It has these attributes: - -Name|Description|Data type ----|---|--- -url|An HTTP or HTTPS URL to POST to|Valid URL - -## Setup - -If you've already set up a main Notification object, and want to access functionality for this Notification's particular Notification Type, you can access its property: - -```php -$type = $notification->getNotificationType(); -``` - -Alternatively, you can retrieve an independent resource using the ID: - -```php -$typeId = 'pagerduty'; -$type = $service->getNotificationType($typeId); -``` - -## List all possible notification types -```php -$types = $service->getNotificationTypes(); - -foreach ($types as $type) { - echo sprintf('%s %s', $type->getName(), $type->getDescription()); -} -``` - -# Notification plans - -## Info - -A notification plan contains a set of notification actions that Rackspace Cloud Monitoring executes when triggered by an alarm. Rackspace Cloud Monitoring currently supports webhook and email notifications. - -Each notification state can contain multiple notification actions. For example, you can create a notification plan that hits a webhook/email to notify your operations team if a warning occurs. However, if the warning escalates to an Error, the notification plan could be configured to hit a different webhook/email that triggers both email and SMS messages to the operations team. The notification plan supports the following states: - -- Critical -- Warning -- OK - -A notification plan, `npTechnicalContactsEmail`, is provided by default which will email all of the technical contacts on file for an account whenever there is a state change. - -## Setup - -```php -$planId = 'npAAAA'; -$plan = $service->getNotificationPlan(); -``` - -## Attributes - -Name|Description|Required?|Data type|Method ----|---|---|---|--- -label|Friendly name for the notification plan.|Required|String (1..255 chars)|`getLabel()` -critical_state|The notification list to send to when the state is `CRITICAL`.|Optional|Array|`getCriticalState()` -ok_state|The notification list to send to when the state is `OK`.|Optional|Array|`getOkState()` -warning_state|The notification list to send to when the state is `WARNING`.|Optional|Array|`getWarningState()` - -## Create Notification Plan -```php -$plan->create(array( - 'label' => 'New Notification Plan', - 'critical_state' => array('ntAAAA'), - 'ok_state' => array('ntBBBB'), - 'warning_state' => array('ntCCCC') -)); -``` - -## Update and delete Notification Plan - -```php -// Update -$plan->update(array( - 'label' => 'New label for my plan' -)); - -// Delete -$plan->delete(); -``` - -# Alarm Notification History - -## Info - -The monitoring service keeps a record of notifications sent for each alarm. This history is further subdivided by the check on which the notification occurred. Every attempt to send a notification is recorded, making this history a valuable tool in diagnosing issues with unreceived notifications, in addition to offering a means of viewing the history of an alarm's statuses. - -Alarm notification history is accessible as a Time Series Collection. By default alarm notification history is stored for 30 days and the API queries the last 7 days of information. - -## Setup - -Notification History is a sub-resource of an Alarm. For more information about working with Alarms, please consult the relevant [documentation](Alarms.md). - -## Discover which Checks have a Notification History - -This operation list checks for which alarm notification history is available: - -```php -$checks = $alarm->getRecordedChecks(); -``` - -## List Alarm Notification History for a particular Check -```php -$checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); -``` - -## Get a particular Notification History item -```php -$checkId = 'chAAAA'; -$itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; - -$singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/notifications.html diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md index 5647eedc2..6445de039 100644 --- a/docs/userguide/CloudMonitoring/Service.md +++ b/docs/userguide/CloudMonitoring/Service.md @@ -1,16 +1,5 @@ # Cloud Monitoring Service -Initializing the Cloud Monitoring is easy - and can be done in a similar way to all other Rackspace services: +Our docs have moved! Please visit the below link: -1. Create client and pass in auth details. For more information about creating clients, please consult the [Client documentation](../Clients.md). -2. Use the factory method, specifying additional parameters where necessary: - -```php -$service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); -``` - -All three parameters are optional - if not specified, it will revert to the service's default values which are: - -- Name = `cloudMonitoring` -- Region = `DFW` -- URL type = `publicURL` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/index.html diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md index b6b505efc..ac0017db0 100644 --- a/docs/userguide/CloudMonitoring/Views.md +++ b/docs/userguide/CloudMonitoring/Views.md @@ -1,19 +1,5 @@ # Views -## Info +Our docs have moved! Please visit the below link: -Views contain a combination of data that usually includes multiple, different objects. The primary purpose of a view is to save API calls and make data retrieval more efficient. Instead of doing multiple API calls and then combining the result yourself, you can perform a single API call against the view endpoint. - -## List all Views - -```php -$views = $service->getViews(); - -foreach ($views as $view) { - $entity = $view->getEntity(); - - echo $view->getTimestamp(); -} - ``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/views.html diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md index 338bb1053..fcad4b900 100644 --- a/docs/userguide/CloudMonitoring/Zones.md +++ b/docs/userguide/CloudMonitoring/Zones.md @@ -1,46 +1,5 @@ # Zones -## Info +Our docs have moved! Please visit the below link: -A monitoring zone is a location that Rackspace Cloud Monitoring collects data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". It is an abstraction for a general location from which data is collected. - -An "endpoint," also known as a "collector," collects data from the monitoring zone. The endpoint is mapped directly to an individual machine or a virtual machine. A monitoring zone contains many endpoints, all of which will be within the IP address range listed in the response. The opposite is not true, however, as there may be unallocated IP addresses or unrelated machines within that IP address range. - -A check references a list of monitoring zones it should be run from. - -## Setup -```php -$zoneId = 'mzAAAAA'; -$zone = $monitoringService->getMonitoringZone($zoneId); -``` - -## Attributes - -Name|Description|Data type|Method ----|---|---|---|--- -country_code|Country Code|String longer than 2 characters|`getCountryCode()` -label|Label|String|`getLabel()` -source_ips|Source IP list|Array|`getSourceIps()` - -## List all zones - -```php -$zones = $service->getMonitoringZones(); -``` - -Please consult the [iterator doc](docs/userguide/Iterators.md) for more information about iterators. - -## Perform a traceroute - -```php -$traceroute = $zone->traceroute(array( - 'target' => 'http://test.com', - 'target_resolver' => 'IPv4' -)); - -// How many hops? -echo count($traceroute); - -// What was the first hop's IP? -echo $traceroute[0]->ip; -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/monitoring/zones.html diff --git a/docs/userguide/Compute/Images.md b/docs/userguide/Compute/Images.md index d09b0b0eb..6cc06d37f 100644 --- a/docs/userguide/Compute/Images.md +++ b/docs/userguide/Compute/Images.md @@ -1,65 +1,5 @@ # Compute Images -## Intro +Our docs have moved! Please visit the below link: -An image is a collection of files for a specific operating system that you use to create or rebuild a server. Rackspace provides prebuilt images. You can also create custom images from servers that you have launched. - -In addition to creating images manually, you can also schedule images of your server automatically. Please consult the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/scheduled_images.html) for more information about this extension, including enabling and disabling scheduled images and showing scheduled images. - -With standard servers, the entire disk (OS and data) is captured in the image. With Performance servers, only the system disk is captured in the image. The data disks should be backed up using Cloud Backup or Cloud Block Storage to ensure availability in case you need to rebuild or restore a server. - -## Setup - -You first need to setup a Compute service. For information, please consult the [Compute service](Service.md) documentation. - -## List images - -```php -$images = $service->imageList(); - -foreach ($images as $image) { - -} -``` - -For more information about [iterators](docs/userguide/Iterators.md), please consult the official documentation. - -### Query parameters - -You can also refine the list of images returned by providing specific URL parameters: - -|Field name|Description| -|---|---| -|server|Filters the list of images by server. Specify the server reference by ID or by full URL.| -|name|Filters the list of images by image name.| -|status|Filters the list of images by status. In-flight images have a status of `SAVING` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the `OpenCloud\Compute\Constants\ImageState` class. Images with an `ACTIVE` status are available for use.| -|changes-since|Filters the list of images to those that have changed since the changes-since time. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ChangesSince.html) for more information.| -|marker|The ID of the last item in the previous list. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Paginated_Collections-d1e664.html) for more information.| -|limit|Sets the page size. See the [official docs](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Paginated_Collections-d1e664.html) for more information.| -|type|Filters base Rackspace images or any custom server images that you have created. Can either be `BASE` or `SNAPSHOT`.| - -### Example - -You can return more information about each image by setting the `$details` argument to `true`. The second argument can be an array of query parameters: - -```php -use OpenCloud\Compute\Constants\ImageState; - -$list = $service->imageList(true, array( - 'server' => 'fooBar', - 'status' => ImageState::ACTIVE -)); -``` - -## Get an image - -```php -$imageId = '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001'; -$image = $service->image($imageId); -``` - -## Delete an image - -```php -$image->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/images.html diff --git a/docs/userguide/Compute/Keypair.md b/docs/userguide/Compute/Keypair.md index e314db32c..37e5080fe 100644 --- a/docs/userguide/Compute/Keypair.md +++ b/docs/userguide/Compute/Keypair.md @@ -1,91 +1,5 @@ # Keypairs -## Generate new keypair +Our docs have moved! Please visit the below link: -This operation creates a new keypair under a provided name; the public key value is automatically generated for you. - -```php -$keypair = $service->keypair(); - -$keypair->create(array( - 'name' => 'jamie_keypair_1' -)); - -echo $keypair->getPublicKey(); - -// Save private key to a file so you can use it to SSH into -// your server later. -$sshPrivateKeyFilename = 'jamie_keypair_1_rsa'; -$privateKey = $keypair->getPrivateKey(); -file_put_contents($sshPrivateKeyFilename, $privateKey); -chmod($sshPrivateKeyFilename, 0600); -``` - -## Upload existing keypair - -This operation creates a new keypair under a provided name using a provided public key value. This public key will probably exist on your local filesystem, and so provide easy access to your server when uploaded. - -```php -$keypair = $service->keypair(); - -$key = <<create(array( - 'name' => 'jamie_macbook', - 'publicKey' => $key -)); - -``` - -## List keypairs - -To list all existing keypairs: - -```php -$keys = $service->listKeypairs(); - -foreach ($keys as $key) { - // ... -} -``` - -For more information about iterators, please see [the docs](../Iterators.md). - -## Delete keypairs - -To delete a specific keypair: - -```php -$keypair->delete(); -``` - -## Creating a server with a keypair - -In order to spawn an instance with a saved keypair (allowing you to SSH in without passwords), you create your server -using the same operation as usual, with one extra parameter: - -```php -use Guzzle\Http\Exception\BadResponseException; -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'New server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor, - 'networks' => array( - $compute->network(Network::RAX_PUBLIC), - $compute->network(Network::RAX_PRIVATE) - ), - 'keypair' => 'jamie_macbook' - )); -} catch (BadResponseException $e) { - // error... -} -``` - -So, as you can see, you specify the **name** of an existing keypair that you previously created on the API. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/keypairs.html diff --git a/docs/userguide/Compute/Server.md b/docs/userguide/Compute/Server.md index 8cc0d17e7..e43e1cc42 100644 --- a/docs/userguide/Compute/Server.md +++ b/docs/userguide/Compute/Server.md @@ -1,180 +1,5 @@ # Servers -## Intro +Our docs have moved! Please visit the below link: -A server is a virtual machine instance in the Cloud Servers environment. - -## Setup - -Server objects are instantiated from the Compute service. For more details, see the [Service](Service.md) docs. - -## Get server - -The easiest way to retrieve a specific server is by its unique ID: - -```php -$serverId = 'ef08aa7a-b5e4-4bb8-86df-5ac56230f841'; -$server = $service->server($serverId); -``` - -## List servers - -You can list servers in two different ways: - -* return an _overview_ of each server (ID, name and links) -* return _detailed information_ for each server - -Knowing which option to use might help save unnecessary bandwidth and reduce latency. - -```php -// overview -$servers = $service->serverList(); - -// detailed -$servers = $service->serverList(true); -``` - -### URL parameters for filtering servers - -Name|Description|Type ----|---|--- -image|The image ID|string -flavor|The flavor ID|string -name|The server name|string -status|The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult `OpenCloud\Compute\Constants\ServerState`|string -changes-since|Value for checking for changes since a previous request|A valid ISO 8601 dateTime (2011-01-24T17:08Z) -RAX-SI:image_schedule|If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule.|bool - -## Create server - -### Using an image - -There are a few parameter requirements when creating a server using an image: - -* **name** - needs to be a string; -- **flavor** - a `OpenCloud\Compute\Resource\Flavor` object, that is populated with the values of a real API flavor; -* **image** - a `OpenCloud\Compute\Resource\Image` object, that is populated with the values of a real API image; - -Firstly we need to find our flavor and image using their UUIDs. For more information about these concepts, including how to find flavor/image UUIDs, please consult §§ 3-4 in the [Getting Started guide](https://github.com/rackspace/php-opencloud/blob/master/docs/getting-started.md#3-select-your-server-image). - -```php -$ubuntuImage = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); -$twoGbFlavor = $compute->flavor('4'); -``` - -Now we're ready to create our instance: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'image' => $ubuntuImage, - 'flavor' => $twoGbFlavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - - // No! Something failed. Let's find out: - $responseBody = (string) $e->getResponse()->getBody(); - $statusCode = $e->getResponse()->getStatusCode(); - $headers = $e->getResponse()->getHeaderLines(); - - echo sprintf('Status: %s\nBody: %s\nHeaders: %s', $statusCode, $responseBody, implode(', ', $headers); -} -``` - -It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. - -### Using a bootable volume - -There are a few parameter requirements when creating a server using a bootable volume: - -* **name** - needs to be a string; -* **flavor** - a `OpenCloud\Compute\Resource\Flavor` object, that is populated with the values of a real API flavor; -* **volume** - a `OpenCloud\Volume\Resource\Volume` object, that is populated with the values of a real API volume; - -Firstly we need to find our flavor and volume using their IDs. - -```php -$volumeService = $client->volumeService(); -$bootableVolume = $volumeService->volume(''); -$flavor = $compute->flavor(''); -``` - -Now we're ready to create our instance: - -```php -use OpenCloud\Compute\Constants\Network; - -$server = $compute->server(); - -try { - $response = $server->create(array( - 'name' => 'My lovely server', - 'volume' => $bootableVolume, - 'flavor' => $flavor - )); -} catch (\Guzzle\Http\Exception\BadResponseException $e) { - // No! Something failed. Let's find out: - echo $e->getRequest() . PHP_EOL . PHP_EOL; - echo $e->getResponse(); -} -``` - -It's always best to be defensive when executing functionality over HTTP; you can achieve this best by wrapping calls in a try/catch block. It allows you to debug your failed operations in a graceful and efficient manner. - -### Create parameters - -Name|Description|Type|Required ----|---|---|--- -name|The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync.|string|Yes -flavor|A populated `OpenCloud\Compute\Resource\Flavor` object representing your chosen flavor|object|Yes -image|A populated `OpenCloud\Compute\Resource\Image` object representing your chosen image|object|No, if volume is specified -volume|A populated `OpenCloud\Volume\Resource\Volume` object representing your chosen bootable volume|object|No, if image is specified -volumeDeleteOnTermination|`true` if the bootable volume should be deleted when the server is terminated; `false`, otherwise|boolean|No; default = `false` -OS-DCF:diskConfig|The disk configuration value. You can use two options: `AUTO` or `MANUAL`.

          `AUTO` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. `AUTO` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.

          `MANUAL` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration.|string|No -networks|An array of populated `OpenCloud\Compute\Resource\Network` objects that indicate which networks your instance resides in.|array|No -metadata|An array of arbitrary data (key-value pairs) that adds additional meaning to your server.|array|No -keypair|You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication.|array|No -personality|Files that you can upload to your newly created instance's filesystem.|array|No - -### Creating a server with keypairs - -Please see the [Keypair](Keypair.md) docs for more information. - -### Creating a server with personality files - -Before you execute the create operation, you can add "personality" files to your `OpenCloud\Compute\Resource\Server` object. These files are structured as a flat array. - -```php -$server->addFile('/var/test_file', 'FILE CONTENT'); -``` - -As you can see, the first parameter represents the filename, and the second is a string representation of its content. When the server is created these files will be created on its local filesystem. For more information about server personality files, please consult the [official documentation](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Server_Personality-d1e2543.html). - -## Update server - -You can update certain attributes of an existing server instance. These attributes are detailed in the next section. - -```php -$server->update(array( - 'name' => 'NEW SERVER NAME' -)); -``` - -### Updatable attributes - -name|description ----|--- -name|The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. -accessIPv4|The IP version 4 address. -accessIPv6|The IP version 6 address. - -## Delete server - -```php -$server->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/Compute/Service.md b/docs/userguide/Compute/Service.md index faff788bb..ba49ff4e4 100644 --- a/docs/userguide/Compute/Service.md +++ b/docs/userguide/Compute/Service.md @@ -1,18 +1,5 @@ # Compute service -## Setup +Our docs have moved! Please visit the below link: -To instantiate a Compute service object, you first need to setup a Rackspace/OpenStack client. To do this, or for more -information, please consult the [Clients documentation](../Clients.md). - -```php -$service = $client->computeService(); -``` - -If no arguments are provided to the above method, certain values are set to their default values: - -|Param|Default value| -|---|---| -|`$name`|cloudServersOpenStack| -|`$region`|DFW| -|`$urltype`|publicURL| \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index f0d94efee..d8d3a12d3 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -1,230 +1,5 @@ # Domains -A domain is an entity/container of all DNS-related information containing one or more records. +Our docs have moved! Please visit the below link: -## Setup - -Limit methods will be called on the DNS service, an instance of `OpenCloud\DNS\Service`. Please see the [DNS service](Service.md) documentation for setup instructions. - -## Get domain - -To retrieve a specific domain, you will need the domain's **id**, not its domain name. - -```php -$domain = $service->domain(12345); -``` - -If you are having trouble remembering or accessing the domain ID, you can do a domain list search for your domain and then access its ID. - -## List domains - -These calls provide a list of all DNS domains manageable by a given account. The resulting list is flat, and does not break the domains down hierarchically by subdomain. All representative domains are included in the list, even if a domain is conceptually a subdomain of another domain in the list. - -```php -$domains = $service->domainList(); - -# Return detailed information for each domain -$domains = $service->domainList(true); -``` - -Please consult the [iterator documentation](/docs/userguide/Iterators.md) for more information about iterators. - -### Filter parameters - -You can filter the aforementioned search by using the `name` parameter in a key/value array supplied as a method argument. For example, providing `array('name' => 'hoola.com')` will return hoola.com and similar names such as main.hoola.com and sub.hoola.com. - -```php -$hoolaDomains = $service->domainList(array( - 'name' => 'hoola.com' -)); -``` - -Filter criteria may consist of: - -- Any letter (A-Za-z) -- Numbers (0-9) -- Hyphen ("-") -- 1 to 63 characters - -Filter criteria should not include any of the following characters: - -> ' + , | ! " £ $ % & / ( ) = ? ^ * ç ° § ; : _ > ] [ @ à, é, ò - -### Finding a domain ID - -If you know a domain's name, but not its unique identifier, you can do this: - -```php -$domains = $service->domainList(array( - 'name' => 'foo.com' -)); - -foreach ($domains as $domain) { - $id = $domain->id; -} -``` - -## List domain changes - -This call shows all changes to the specified domain since the specified date/time. The since parameter is optional and defaults to midnight of the current day. - -```php -$changes = $domain->changes(); - -# Changes since last week -$since = date('c', strtotime('last week')); -$changes = $domain->changes($since); - -foreach ($changes->changes as $change) { - printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); - - foreach ($change->changeDetails as $detail) { - printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); - } -} -``` - -## Export domain - -This call provides the BIND (Berkeley Internet Name Domain) 9 formatted contents of the requested domain. This call is for a single domain only, and as such, does not traverse up or down the domain hierarchy for details (that is, no subdomain information is provided). - -```php -$asyncResponse = $domain->export(); -$body = $asyncResponse->waitFor('COMPLETED'); -echo $body['contents']; -``` - -## Create domain - -A domain is composed of DNS records (e.g. `A`, `CNAME` or `MX` records) and an optional list of sub-domains. You will need to specify these before creating the domain itself: - -```php -// get empty object -$domain = $service->domain(); - -// add A record -$aRecord = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 -)); -$domain->addRecord($aRecord); - -// add optional C record -$cRecord = $domain->record(array( - 'type' => 'CNAME', - 'name' => 'www.example.com', - 'data' => 'example.com', - 'ttl' => 3600 -)); -$domain->addRecord($cRecord); - -// add optional MX record -$mxRecord = $domain->record(array( - 'type' => 'MX', - 'data' => 'mail.example.com', - 'name' => 'example.com', - 'ttl' => 3600, - 'priority' => 5 -)); -$domain->addRecord($mxRecord); - -// add optional NS records -$nsRecord1 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns1.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 -)); -$domain->addRecord($nsRecord1); - -$nsRecord2 = $domain->record(array( - 'type' => 'NS', - 'data' => 'dns2.stabletransit.com', - 'name' => 'example.com', - 'ttl' => 5400 -)); -$domain->addRecord($nsRecord2); - -// add optional subdomains -$sub1 = $domain->subdomain(array( - 'emailAddress' => 'foo@example.com', - 'name' => 'dev.example.com', - 'comment' => 'Dev portal' -)); -$domain->addSubdomain($sub1); - -// send to API -$domain->create(array( - 'emailAddress' => 'webmaster@example.com', - 'ttl' => 3600, - 'name' => 'example.com', - 'comment' => 'Optional comment' -)); -``` - -## Clone domain - -This call will duplicate a single existing domain configuration with a new domain name for the specified Cloud account. By default, all records and, optionally, subdomain(s) are duplicated as well. - -The method signature you will need to use is: - -```php -cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) -``` - -Name|Data type|Default|Description ----|---|---|--- -`$newDomainName`|`string`|-|The new name for your cloned domain -`$subdomains`|`bool`|`true`|Set to `TRUE` to clone all the subdomains for this domain -`$comments`|`bool`|`true`|Set to `TRUE` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. -`$email`|`bool`|`true`|Set to `TRUE` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. - -For example: - -```php -$asyncResponse = $domain->cloneDomain('new-name.com', true); -``` - -## Import domain - -This call provisions a new DNS domain under the account specified by the BIND 9 formatted file configuration contents defined in the request object. - -You will need to ensure that the BIND 9 formatted file configuration contents are valid by adhering to the following rules: - -- Each record starts on a new line and on the first column. If a record will not fit on one line, use the BIND_9 line continuation convention where you put a left parenthesis and continue the one record on the next line and put a right parenthesis when the record ends. For example, - - > example2.net. 3600 IN SOA dns1.stabletransit.com. ( - sample@rackspace.com. 1308874739 3600 3600 3600 3600) - -- The attribute values of a record must be separated by a single blank or tab. No other white space characters. - -- If there are any NS records, the data field should not be dns1.stabletransit.com or dns2.stabletransit.com. They will result in "duplicate record" errors. - -For example: - -```php -$bind9Data = <<import($bind9Data); -``` - -## Modify domain - -This call modifies DNS domain(s) attributes only. Only the TTL, email address and comment attributes of a domain can be modified. Records cannot be added, modified, or removed through this API operation - you will need to use the [add records](/docs/userguide/DNS/Records.md#add-record), [modify records](/docs/userguide/DNS/Records.md#modify-record) or [remove records](/docs/userguide/DNS/Records.md#delete-record) operations respectively. - -```php -$domain->update(array( - 'ttl' => ($domain->ttl + 100), - 'emailAddress' => 'new_dev@foo.com' -)); -``` - -## Remove domain - -```php -$domain->delete(); -``` +https://doc.php-opencloud.com/en/latest/services/dns/domains.html diff --git a/docs/userguide/DNS/Limits.md b/docs/userguide/DNS/Limits.md index 6e7aeb042..70fac6504 100644 --- a/docs/userguide/DNS/Limits.md +++ b/docs/userguide/DNS/Limits.md @@ -1,58 +1,5 @@ # Limits -## Setup +Our docs have moved! Please visit the below link: -Limit methods will be called on the DNS service, an instance of `OpenCloud\DNS\Service`. Please see the [DNS service](Service.md) documentation for setup instructions. - -## List all limits - -This call provides a list of all applicable limits for the specified account. - -```php -$limits = $service->limits(); -``` - -### Absolute limits - -There are some absolute limits imposed on your account - such as how many domains you can create and how many records you can create for each domain: - -```php -$absoluteLimits = $limits->absolute; - -# Domain limit -echo $absoluteLimits->domains; - -# Record limit per domain -echo $absoluteLimits->{'records per domain'}; -``` - -## List limit types - -To find out the different limit types you can query, run: - -```php -$limitTypes = $service->limitTypes(); -``` - -will return: - -``` -array(3) { - [0] => - string(10) "RATE_LIMIT" - [1] => - string(12) "DOMAIN_LIMIT" - [2] => - string(19) "DOMAIN_RECORD_LIMIT" -} -``` - -## Query a specific limit - -```php -$limit = $service->limits('DOMAIN_LIMIT'); - -echo $limit->absolute->limits->value; - ->>> 500 -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/limits.html diff --git a/docs/userguide/DNS/Records.md b/docs/userguide/DNS/Records.md index b8f71bda2..52998b955 100644 --- a/docs/userguide/DNS/Records.md +++ b/docs/userguide/DNS/Records.md @@ -1,87 +1,5 @@ # Records -A DNS record belongs to a particular domain and is used to specify information about the domain. +Our docs have moved! Please visit the below link: -There are several types of DNS records. Examples include mail exchange (MX) records, which specify the mail server for a particular domain, and name server (NS) records, which specify the authoritative name servers for a domain. - -It is represented by the `OpenCloud\DNS\Resource\Record` class. Records belong to a [Domain](Domains.md). - -## Get record - -In order to retrieve details for a specific DNS record, you will need its **id**: - -```php -$record = $domain->record('NS-1234567'); -``` - -If you do not have this ID at your disposal, you can traverse the record collection and do a string comparison (detailed below). - -## List records - -This call lists all records configured for the specified domain. - -```php -$records = $domain->recordList(); - -foreach ($records as $record) { - printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -### Query parameters - -You can pass in an array of query parameters for greater control over your search: - -Name|Data type|Default|Description ----|---|---|--- -`type`|`string`|The record type -`name`|`string`|The record name -`data`|`string`|Data for this record - -### Find a record ID from its name - -For example: - -```php -$records = $domain->recordList(array( - 'name' => 'imap.example.com', - 'type' => 'MX' -)); - -foreach ($records as $record) { - $recordId = $record->id; -} -``` - -## Add record - -This call adds a new record to the specified domain: - -```php -$record = $domain->record(array( - 'type' => 'A', - 'name' => 'example.com', - 'data' => '192.0.2.17', - 'ttl' => 3600 -)); - -$record->create(); -``` - -Please be aware that records that are added with a different hostname than the parent domain might fail silently. - -## Modify record - -```php -$record = $domain->record(123456); -$record->ttl -= 100; -$record->update(); -``` - -## Delete record - -```php -$record->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/records.html diff --git a/docs/userguide/DNS/Reverse-DNS.md b/docs/userguide/DNS/Reverse-DNS.md index a269a0057..e930431c1 100644 --- a/docs/userguide/DNS/Reverse-DNS.md +++ b/docs/userguide/DNS/Reverse-DNS.md @@ -1,71 +1,5 @@ # Reverse DNS -DNS usually determines an IP address associated with a domain name. Reverse DNS is the opposite process: resolving a domain name from an IP address. This is usually achieved with a domain name pointer. +Our docs have moved! Please visit the below link: -## Get PTR record - -PTR records refer to a parent device: either a Cloud Server or a Cloud Load Balancer with a public virtual IP address. You must supply a fully formed resource object in order to retrieve either one's PTR record: - -```php -/** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ - -$ptr = $service->ptrRecord(array( - 'parent' => $parent -)); -``` - -So, in the above example, a `$parent` could be an instance of `OpenCloud\Compute\Resource\Server` or `OpenCloud\LoadBalancer\Resource\LoadBalancer` - because they both implement `OpenCloud\DNS\Resource\HadPtrRecordsInterface`. Please consult the [server documentation](../Compute/Server.md) and [load balancer documentation](../LoadBalancer/USERGUIDE.md) for more detailed usage instructions. - -## List PTR records - -```php -/** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ - -$ptrRecords = $service->ptrRecordList($parent); - -foreach ($ptrRecords as $ptrRecord) { - -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -## Add PTR record - -```php -$parent = $computeService->server('foo-server-id'); - -$ptr = $dnsService->ptrRecord(array( - 'parent' => $parent, - 'ttl' => 3600, - 'name' => 'example.com', - 'type' => 'PTR', - 'data' => '192.0.2.7' -)); - -$ptr->create(); -``` - -Here is a table that explains the above attributes: - -Name|Description|Required ----|---|--- -type|Specifies the record type as "PTR".|Yes -name|Specifies the name for the domain or subdomain. Must be a valid domain name.|Yes -data|The data field for PTR records must be a valid IPv4 or IPv6 IP address.|Yes -ttl|If specified, must be greater than 300. Defaults to 3600 if no TTL is specified.|No -comment|If included, its length must be less than or equal to 160 characters.|No - -## Modify PTR record - -```php -$ptr->update(array( - 'ttl' => $ptr->ttl * 2 -)); -``` - -## Delete PTR record - -```php -$ptr->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/reverse-dns.html diff --git a/docs/userguide/DNS/Service.md b/docs/userguide/DNS/Service.md index ccabb1242..3404ed7c7 100644 --- a/docs/userguide/DNS/Service.md +++ b/docs/userguide/DNS/Service.md @@ -1,11 +1,5 @@ # DNS Service -To instantiate a Compute service object, you first need to setup a -Rackspace/OpenStack client. To do this, or for more information, please consult -the [Clients documentation](../Clients.md). +Our docs have moved! Please visit the below link: -You will then need to run: - -```php -$service = $client->dnsService(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/dns/index.html diff --git a/docs/userguide/Database/README.md b/docs/userguide/Database/README.md index 9a5822e5b..199d049e4 100644 --- a/docs/userguide/Database/README.md +++ b/docs/userguide/Database/README.md @@ -1,116 +1,5 @@ # Databases -A **cloud database** is a MySQL relational database service that allows -customers to programatically provision database instances of varying virtual -resource sizes without the need to maintain and/or update MySQL. +Our docs have moved! Please visit the below link: -## Getting started - -### 1. Instantiate a Rackspace client. - -```php -use OpenCloud\Rackspace; -use OpenCloud\Common\Constants\State; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### 2. Create a database server instance. - -```php -$databaseService = $client->databaseService('cloudDatabases', 'DFW'); - -$twoGbFlavor = $databaseService->flavor(3); - -$dbInstance = $databaseService->instance(); -$dbInstance->name = 'Demo database instance'; -$dbInstance->volume = new stdClass(); -$dbInstance->volume->size = 20; // GB -$dbInstance->flavor = $twoGbFlavor; -$dbInstance->create(); - -$dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { - - printf("Database instance build status: %s\n", $dbInstance->status); - -}); -``` - -The example above creates a database server instance with 20GB of disk space and -2GB of memory, then waits for it to become ACTIVE. - -### 3. Create a database on the database server instance. - -```php -$db = $dbInstance->database(); -$db->name = 'demo_db'; - -$db->create(); -``` - -The example above creates a database named `demo_db` on the database server -instance created in the previous step. - -### 4. Create database user and give it access to database. - -```php -$user = $dbInstance->user(); -$user->name = 'demo_user'; -$user->password = 'h@X0r!'; -$user->databases = array('demo_db'); - -$user->create(); -``` - -The example above creates a database user named `demo_user`, sets its password -and gives it access to the `demo_db` database created in the previous step. - -### 5. Optional step: Create a load balancer to allow access to the database from the Internet. - -The database created in the previous step can only be accessed from the Rackspace -private network (aka `SERVICENET`). If you have a cloud server instance in the same -region as the database server instance, you will be able to connect to the database -from that cloud server instance. - -If, however, you would like to access the database from the Internet, you will -need to create a load balancer with an IP address that is routable from the -Internet and attach the database server instance as a back-end node of this load -balancer. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); - -$loadBalancer = $loadBalancerService->loadBalancer(); - -$loadBalancer->name = 'Load balancer - DB'; -$loadBalancer->addNode($dbInstance->hostname, 3306); -$loadBalancer->port = 3306; -$loadBalancer->protocol = 'MYSQL'; -$loadBalancer->addVirtualIp('PUBLIC'); - -$loadBalancer->create(); - -$loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { - printf("Load balancer build status: %s\n", $lb->status); -}); - -foreach ($loadBalancer->virtualIps as $vip) { - if ($vip->type == 'PUBLIC') { - printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); - } -} -``` - -In the example above, a load balancer is created with the database server -instance as its only back-end node. Further, this load balancer is configured -to listen for MySQL connections on port 3306. Finally a virtual IP address (VIP) -is configured in the `PUBLIC` network address space so that this load balancer -may receive connections from the Internet. - -Once the load balancer is created and becomes `ACTIVE`, it's Internet-accessible -IP addresses are printed out. If you connect to any of these IP addresses on port -3306 using the MySQL protocol, you will be connected to the database created in -step 3. +https://doc.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/Debugging.md b/docs/userguide/Debugging.md index 0fc970c0a..0c6afa4d6 100644 --- a/docs/userguide/Debugging.md +++ b/docs/userguide/Debugging.md @@ -1,89 +1,5 @@ # Debugging -There are two important debugging strategies to use when encountering problems -with HTTP transactions. +Our docs have moved! Please visit the below link: -## Strategy 1: Meaningful exception handling - -If the API returns a `4xx` or `5xx` status code, it indicates that there was an -error with the sent request, meaning that the transaction cannot be adequately -completed. - -The Guzzle HTTP component, which forms the basis of our SDK's transport layer, -utilizes [numerous exception classes](https://github.com/guzzle/guzzle/tree/master/src/Guzzle/Http/Exception) -to handle this error logic. - -The two most common exception classes are: - -* `Guzzle\Http\Exception\ClientErrorResponseException`, which is thrown when a -`4xx` response occurs - -* `Guzzle\Http\Exception\ServerErrorResponseException`, which is thrown when a -`5xx` response occurs - -Both of these classes extend the base `BadResponseException` class. - -This provides you with the granularity you need to debug and handle exceptions. - -### An example with Swift - -If you're trying to retrieve a Swift resource, such as a Data Object, and you're -not completely certain that it exists, it makes sense to wrap your call in a -try/catch block: - -```php -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - return $service->getObject('foo.jpg'); -} catch (ClientErrorResponseException $e) { - // Okay, the resource probably does not exist - return false; -} catch (\Exception $e) { - // Some other exception was thrown, probably critical - $this->logException($e); - $this->alertDevs(); -} -``` - -Both `ClientErrorResponseException` and `ServerErrorResponseException` have -two methods that allow you to access the HTTP transaction: - -```php -// Find out the faulty request -$request = $e->getRequest(); - -// Display everything by casting as string -echo (string) $request; - -// Find out the HTTP response -$response = $e->getResponse(); - -// Output that too -echo (string) $response; -``` - -## Strategy 2: Wire logging - -Guzzle provides a [Log plugin](http://docs.guzzlephp.org/en/latest/plugins/log-plugin.html) -that allows you to log everything over the wire, which is useful if you don't -know what's going on. - -Here's how you enable it: - -#### Install the plugin - -```bash -php composer.phar require guzzle/plugin-log:~3.8 -``` - -#### Add to your client - -```php -use Guzzle\Plugin\Log\LogPlugin; - -$client->addSubscriber(LogPlugin::getDebugPlugin()); -``` - -The above will add a generic logging subscriber to your client, which will be -notified every time a relevant HTTP event is fired off. +https://doc.php-opencloud.com/en/latest/debugging.html diff --git a/docs/userguide/Identity/Roles.md b/docs/userguide/Identity/Roles.md index e2d49e71d..333d53c81 100644 --- a/docs/userguide/Identity/Roles.md +++ b/docs/userguide/Identity/Roles.md @@ -1,68 +1,5 @@ # Roles -## Intro +Our docs have moved! Please visit the below link: -A role is a personality that a user assumes when performing a specific set of operations. A role includes a set of rights and privileges. A user assuming a role inherits the rights and privileges associated with the role. A token that is issued to a user includes the list of roles the user can assume. When a user calls a service, that service determines how to interpret a user's roles. A role that grants access to a list of operations or resources within one service may grant access to a completely different list when interpreted by a different service. - -## Setup - -Role objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Getter|Setter ----|---|--- -id|`getId()`|`setId()` -name|`getName()`|`setName()` -description|`getDescription()`|`setDescription()` - -## List roles - -This call lists the global roles available within a specified service. - -```php -$roles = $service->getRoles(); - -foreach ($roles as $role) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). - -## Get role - -This call lists detailed information (id, name, description) for a specified role. - -```php -$roleId = '123abc'; -$role = $service->getRole($roleId); -``` - -## Add/delete user roles - -To add/remove user roles, you must first instantiate a [user](Users.md) object: - -```php -$roleId = '123abc'; - -// add role to user -$user->addRole($roleId); - -// remove role from user -$user->removeRole($roleId); -``` - -## List user global roles - -This call returns a list of global roles associated with a user: - -```php -$roles = $user->getRoles(); - -foreach ($roles as $role) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/roles.html diff --git a/docs/userguide/Identity/Service.md b/docs/userguide/Identity/Service.md index 6cc80eb5a..579cf2a9f 100644 --- a/docs/userguide/Identity/Service.md +++ b/docs/userguide/Identity/Service.md @@ -1,24 +1,5 @@ # Identity service -## Intro +Our docs have moved! Please visit the below link: -The Identity service is regionless, so you do not need to specify a region when instantiating the service object. Although this was primarily based on Rackspace's implementation of Cloud Identity, it should also work for OpenStack Keystone. - -## A note on object creation - -Normally, when services are created the client handles authenticates automatically. But because Keystone/Identity is fundamental to the authentication process itself, it proves difficult to do this procedure as its normally done. For this reason, you have two options when creating the service object: - -1: Use the client's factory method - -```php -$identity = $client->identityService(); -``` - -2: Authenticate manually - -```php -use OpenCloud\Identity\Service as IdentityService; - -$identity = IdentityService::factory($client); -$identity->getClient()->authenticate(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/index.html diff --git a/docs/userguide/Identity/Tenants.md b/docs/userguide/Identity/Tenants.md index b30cb661d..ae9bcdcb0 100644 --- a/docs/userguide/Identity/Tenants.md +++ b/docs/userguide/Identity/Tenants.md @@ -1,21 +1,5 @@ # Tenants -## Intro +Our docs have moved! Please visit the below link: -A tenant is a container used to group or isolate resources and/or identity objects. Depending on the service operator, a tenant may map to a customer, account, organization, or project. - -## Setup - -Tenant objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## List tenants - -```php -$tenants = $service->getTenants(); - -foreach ($tenants as $tenant) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/tenants.html diff --git a/docs/userguide/Identity/Tokens.md b/docs/userguide/Identity/Tokens.md index 04a3ec327..a60c57ab5 100644 --- a/docs/userguide/Identity/Tokens.md +++ b/docs/userguide/Identity/Tokens.md @@ -1,84 +1,5 @@ # Tokens -## Intro +Our docs have moved! Please visit the below link: -A token is an opaque string that represents an authorization to access cloud resources. Tokens may be revoked at any time and are valid for a finite duration. - -## Setup - -Token objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Description|Getter|Setter ----|---|---|--- -id|The unique ID of the token|`getId()`|`setId()` -expires|Timestamp of when the token will expire|`getExpires()` or `hasExpired()`|`setExpires()` - -## Create token (authenticate) - -In order to generate a token, you must pass in the JSON template that is sent to the API. This is because Rackspace's operation expects a slightly different entity body than OpenStack Keystone. - -Request body for Rackspace's generate token operation: - -```json -{ - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "foo", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1100111" - } -} -``` - -Request body for Keystone's generate token operation: - -```json -{ - "auth": { - "passwordCredentials":{ - "username":"demoauthor", - "password":"theUsersPassword" - }, - "tenantId": "12345678" - } -} -``` - -The only real differences you'll notice is the name of the object key (`RAX-KSKEY:apiKeyCredentials`/`passwordCredentials`) and the secret (`apiKey`/`password`). The `tenantId` property in both templates are optional. You can also add `tenantName` too. - -```php -use OpenCloud\Common\Http\Message\Formatter; - -$template = sprintf( - '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', - 'my_username', - 'my_api_key' -); - -$response = $service->generateToken($template); - -$body = Formatter::decode($response); - -// service catalog -$catalog = $body->access->serviceCatalog; - -// token -$token = $body->access->token; - -// user -$user = $body->access->user; -``` - -As you will notice, these variables will be stdClass objects - for fully fledged functionality, let the client authenticate by itself because it ends up stocking the necessary models for you. - -To see the response body structure, consult the [official docs](http://docs.rackspace.com/auth/api/v2.0/auth-client-devguide/content/POST_authenticate_v2.0_tokens_Token_Calls.html). - -## Revoke token (destroy session) - -```php -$tokenId = '1234567'; -$service->revokeToken($tokenId); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/tokens.html diff --git a/docs/userguide/Identity/Users.md b/docs/userguide/Identity/Users.md index 286aa6007..e387cbee5 100644 --- a/docs/userguide/Identity/Users.md +++ b/docs/userguide/Identity/Users.md @@ -1,128 +1,5 @@ # Users -## Intro +Our docs have moved! Please visit the below link: -A user is a digital representation of a person, system, or service who consumes cloud services. Users have credentials and may be assigned tokens; based on these credentials and tokens, the authentication service validates that incoming requests are being made by the user who claims to be making the request, and that the user has the right to access the requested resources. Users may be directly assigned to a particular tenant and behave as if they are contained within that tenant. - -## Setup - -User objects are instantiated from the Identity service. For more details, see the [Service](Service.md) docs. - -## Useful object properties/methods - -Property|Description|Getter|Setter ----|---|---|--- -id|The unique ID for this user|`getId()`|`setId()` -username|Username for this user|`getUsername()`|`setUsername()` -email|User's email address|`getEmail()`|`setEmail()` -enabled|Whether or not this user can consume API functionality|`getEnabled()` or `isEnabled()`|`setEnabled()` -password|Either a user-defined string, or an automatically generated one, that provides security when authenticating.|`getPassword()` only valid on creation|`setPassword()` to set local property only. To set password on API (retention), use `updatePassword()`. -defaultRegion|Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region.|`getDefaultRegion()`|`setDefaultRegion()` -domainId|Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account.|`getDomainId()`|`setDomainId()` - -## List users - -```php -$users = $service->getUsers(); - -foreach ($users as $user) { - // ... -} -``` - -For more information about how to use iterators, see the [documentation](../Iterators.md). - -## Get user - -There are various ways to get a specific user: by name, ID and email address. - -```php -use OpenCloud\Identity\Constants\User as UserConst; - -// Get user by name -$user1 = $service->getUser('jamie'); - -// Get user by ID -$user2 = $service->getUser(123456, UserConst::MODE_ID); - -// Get user by email -$user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); -``` - -## Create user - -There are a few things to remember when creating a user: - -* This operation is available only to users who hold the `identity:user-admin` role. This admin can create a user who holds the `identity:default` user role. - -* The created user **will** have access to APIs but **will not** have access to the Cloud Control Panel. - -* Within an account, a maximum of 100 account users can be added. - -* If you attempt to add a user who already exists, an HTTP error 409 results. - -The `username` and `email` properties are required for creating a user. Providing a `password` is optional; if omitted, one will be automatically generated and provided in the response. - -```php -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - // execute operation - $user = $service->createUser(array( - 'username' => 'newUser', - 'email' => 'foo@bar.com' - )); -} catch (ClientErrorResponseException $e) { - // catch 4xx HTTP errors - echo $e->getResponse()->toString(); -} - -// show generated password -echo $user->getPassword(); -``` - -## Update user - -When updating a user, specify which attribute/property you want to update: - -```php -$user->update(array( - 'email' => 'new_email@bar.com' -)); -``` - -### Updating a user password - -Updating a user password requires calling a distinct method: -```php -$user->updatePassword('password123'); -``` - -## Delete user - -```php -$user->delete(); -``` - -## List credentials - -This operation allows you to see your non-password credential types for all authentication methods available. - -```php -$creds = $user->getOtherCredentials(); -``` - -## Get user API key - -```php -echo $user->getApiKey(); -``` - -## Reset user API key - -When resetting an API key, a new one will be automatically generated for you: - -```php -$user->resetApiKey(); -echo $user->getApiKey(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/identity/users.html diff --git a/docs/userguide/Image/Images.md b/docs/userguide/Image/Images.md index c0efee9a1..1f593f17a 100644 --- a/docs/userguide/Image/Images.md +++ b/docs/userguide/Image/Images.md @@ -1,97 +1,5 @@ # Images -A virtual machine image is a single file which contains a virtual disk that has -an installed bootable operating system. In the Cloud Images API, an image is -represented by a JSON-encoded data structure (the image schema) and its raw -binary data (the image file). +Our docs have moved! Please visit the below link: -An Image is represented by the `OpenCloud\Image\Resource\Image` class. - -## Setup - -You instantiate an Image object from its `OpenCloud\Image\Service` class, which -is available from the OpenStack/Rackspace client: - -```php -$service = $client->imageService('cloudImages', 'IAD'); -``` - -View the guides for more information about [clients](../Clients.md) or -[services](../Services.md). - -## List images - -```php -$images = $service->listImages(); - -foreach ($images as $image) { - /** @param $image OpenCloud\Image\Resource\Image */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](../Iterators.md). - -## Get image details - -```php -/** @param $image OpenCloud\Image\Resource\Image */ -$image = $service->getImage(''); -``` - -### A note on schema classes - -Both `OpenCloud\Image\Resource\Image` and `OpenCloud\Image\Resource\Member` -extend the `AbstractSchemaResource` abstract class, which offers some unique -functionality. - -Because these resources are inherently dynamic - i.e. they are modelled on -dynamic JSON schema - you need to access their state in a way different than -conventional getter/setter methods, and even class properties. For this reason, -they implement SPL's native -[`ArrayAccess`](http://www.php.net/manual/en/class.arrayaccess.php) -interface which allows you to access their state as a conventional array: - -```php -$image = $service->getImage(''); - -$id = $image['id']; -$tags = $image['tags']; -``` - -## Update image - -You can only update your own custom images - you cannot update or delete base -images. The way in which you may update your image is dictated by its -[schema](Schemas.md). - -Although you should be able to add new and replace existing properties, always -prepare yourself for a situation where it might be forbidden: - -```php -use OpenCloud\Common\Exceptions\ForbiddenOperationException; - -try { - $image->update(array( - 'name' => 'foo', - 'newProperty' => 'bar' - )); -} catch (ForbiddenOperationException $e) { - // A 403 Forbidden was returned -} -``` - -There are three operations that can take place for each Image property: - -* If a `false` or `null` value is provided, a `REMOVE` operation will occur, -removing the property from the JSON document -* If a non-false value is provided and the property does not exist, an `ADD` -operation will add it to the document -* If a non-false value is provided and the property does exist, a `REPLACE` -operation will modify the property in the document - -## Delete image - -```php -$image->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/images.html diff --git a/docs/userguide/Image/Schemas.md b/docs/userguide/Image/Schemas.md index b634b5705..b37222b84 100644 --- a/docs/userguide/Image/Schemas.md +++ b/docs/userguide/Image/Schemas.md @@ -1,159 +1,5 @@ # JSON schemas -The Cloud Images API supplies json documents describing the JSON-encoded data -structures that represent domain objects, so that a client knows exactly what -to expect in an API response. +Our docs have moved! Please visit the below link: -A JSON Schema is represented by the `OpenCloud\Image\Resource\Schema\Schema` -class. - -## Schema types - -There are currently four types of schema: Images schema, Image schema, Members -schema, and Member schema. - -## Example response from the API - -A sample response from the API, for an Images schema might be: - -```json -{ - "name": "images", - "properties": { - "images": { - "items": { - "type": "array", - "name": "image", - "properties": { - "id": {"type": "string"}, - "name": {"type": "string"}, - "visibility": {"enum": ["public", "private"]}, - "status": {"type": "string"}, - "protected": {"type": "boolean"}, - "tags": { - "type": "array", - "items": {"type": "string"} - }, - "checksum": {"type": "string"}, - "size": {"type": "integer"}, - "created_at": {"type": "string"}, - "updated_at": {"type": "string"}, - "file": {"type": "string"}, - "self": {"type": "string"}, - "schema": {"type": "string"} - }, - "additionalProperties": {"type": "string"}, - "links": [ - {"href": "{self}", "rel": "self"}, - {"href": "{file}", "rel": "enclosure"}, - {"href": "{schema}", "rel": "describedby"} - ] - } - }, - "schema": {"type": "string"}, - "next": {"type": "string"}, - "first": {"type": "string"} - }, - "links": [ - {"href": "{first}", "rel": "first"}, - {"href": "{next}", "rel": "next"}, - {"href": "{schema}", "rel": "describedby"} - ] -} -``` - -The top-level schema is called `images`, and contains an array of links and -a properties object. Inside this properties object we see the structure of this -top-level `images ` object. So we know that it will take this form: - -```json -{ - "images": [something...] -} -``` - -Within this object, we can see that it contains an array of anonymous objects, -each of which is called `image` and has its own set of nested properties: - -```json -{ - "images": [ - { - [object 1...] - }, - { - [object 2...] - }, - { - [object 3...] - } - ] -} -``` - -The structure of these nested objects are defined as another schema - i.e. a -*subschema*. We know that each object has an ID property (string), a name -property (string), a visibility property (can either be `private` or `public`), etc. - -```json -{ - "images": [ - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - }, - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - }, - { - "id": "foo", - "name": "bar", - "visibility": "private", - // etc. - } - ] -} -``` - -Each nested property of a schema is represented by the -`OpenCloud\Image\Resource\Schema\Property` class. - -If you would like to find out more about schemas, Guzzle has good documentation -about [service descriptions](http://docs.guzzlephp.org/en/latest/webservice-client/guzzle-service-descriptions.html), -which is fairly analogous. - -## JSON Patch - -The Glance API has a unique way of updating certain dynamic resources: they use -JSON Patch method, as outlined in [RFC 6902](http://tools.ietf.org/html/rfc6902). - -Requests need to use the `application/openstack-images-v2.1-json-patch` -content-type. - -In order for the operation to occur, the request entity body needs to contain a -very particular structure: - -``` -[ - {"op": "replace", "path": "/name", "value": "Fedora 17"}, - {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} -] -``` - -The `op` key refers to the type of Operation (see -`OpenCloud\Image\Enum\OperationType` for a full list). - -The `path` key is a JSON pointer to the document property you want to modify or -insert. JSON pointers are defined in [RFC 6901](http://tools.ietf.org/html/rfc6901). - -The `value` key is the value. - -Because this is all handled for you behind the scenes, we will not go into exhaustive depth -about how this operation is handled. You can browse the source code, consult -the various RFCs and the [official documentation](http://docs.rackspace.com/images/api/v2/ci-devguide/content/patch-method.html) -for additional information. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/schemas.html diff --git a/docs/userguide/Image/Sharing.md b/docs/userguide/Image/Sharing.md index e83470073..91515b9fa 100644 --- a/docs/userguide/Image/Sharing.md +++ b/docs/userguide/Image/Sharing.md @@ -1,110 +1,5 @@ # Sharing images -Images can be created and deleted by image producers, updated by image -consumers, and listed by both image producers and image consumers: +Our docs have moved! Please visit the below link: -Operation|Producer can?|Consumer can? ----|---|--- -Created|Yes|No -Deleted|Yes|No -Updated|No|Yes -Listed|Yes|Yes - -The producer shares an image with the consumer by making the consumer a *member* -of that image. The consumer then accepts or rejects the image by changing the -member status. Once accepted, the image appears in the consumer's image list. - -## Typical workflow - -1. The producer posts the availability of specific images on a public website. - -2. A potential consumer provides the producer with his/her tenant ID and email -address. - -3. The producer [creates a new Image Member]() with the consumer's details - -4. The producer notifies the consumer via email that the image has been shared -and provides the image's ID. - -5. If the consumer wishes the image to appear in his/her image list, the -consumer [updates their own Member status]() to `ACCEPTED`. - -### Additional notes - -* If the consumer subsequently wishes to hide the image, the consumer can change -their Member status to `REJECTED`. - -* If the consumer wishes to hide the image, but is open to the possibility of -being reminded by the producer that the image is available, the consumer can -change their Member status to `PENDING`. - -* Image producers add or remove image members, but may not modify the member -status of an image member. - -* Image consumers change their own member status, but may not add or remove -themselves as an image member. - -* Image consumers can boot from any image shared by the image producer, -regardless of the member status, as long as the image consumer knows the image ID. - -## Setup - -All member operations are executed against an [Image](Images.md), so you will -need to set this up first. - -## List image members - -This operation is available for both producers and consumers. - -```php -$members = $image->listMembers(); - -foreach ($members as $member) { - /** @param $member OpenCloud\Image\Resource\Member */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](../Iterators.md). - -## Create image member - -This operation is only available for producers. - -```php -$tenantId = 12345; - -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->createMember($tenantId); -``` - -## Delete image member - -This operation is only available for producers. - -```php -$tenantId = 12345; - -/** @param $member OpenCloud\Image\Resource\Member */ -$member = $image->getMember($tenantId); - -$member->delete(); -``` - -## Update image member status - -This operation is only available for consumers. - -```php -use OpenCloud\Images\Enum\MemberStatus; - -$tenantId = 12345; - -/** @param $member OpenCloud\Image\Resource\Member */ -$member = $image->getMember($tenantId); - -$member->updateStatus(MemberStatus::ACCEPTED); -``` - -The acceptable states you may pass in are made available to you through the -constants defined in the `OpenCloud\Images\Enum\MemberStatus` class. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/sharing.html diff --git a/docs/userguide/Image/Tags.md b/docs/userguide/Image/Tags.md index 019e336e5..fc8bcd8aa 100644 --- a/docs/userguide/Image/Tags.md +++ b/docs/userguide/Image/Tags.md @@ -1,23 +1,5 @@ # Image tags -An image tag is a string of characters used to identify a specific image or -images. +Our docs have moved! Please visit the below link: -## Setup - -All member operations are executed against an [Image](Images.md), so you will -need to set this up first. - -## Add image tag - -```php -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->addTag('jamie_dev'); -``` - -## Delete image tag - -```php -/** @param $response Guzzle\Http\Message\Response */ -$response = $image->deleteTag('jamie_dev'); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/image/tags.html diff --git a/docs/userguide/Iterators.md b/docs/userguide/Iterators.md index 553507937..114982668 100644 --- a/docs/userguide/Iterators.md +++ b/docs/userguide/Iterators.md @@ -1,118 +1,5 @@ # Iterators -## Intro +Our docs have moved! Please visit the below link: -Iterators allow you to traverse over collections of your resources in an efficient and easy way. Currently there are two Iterators provided by the SDK: - -- **ResourceIterator**. The standard iterator class that implements SPL's standard [Iterator](http://php.net/manual/en/class.iterator.php), [ArrayAccess](http://www.php.net/manual/en/class.arrayaccess.php) and [Countable](http://php.net/manual/en/class.countable.php) interfaces. In short, this allows you to traverse this object (using `foreach`), count its internal elements like an array (using `count` or `sizeof`), and access its internal elements like an array (using `$iterator[1]`). - - -- **PaginatedIterator**. This is a child of ResourceIterator, and as such inherits all of its functionality. The difference however is that when it reaches the end of the current collection, it attempts to construct a URL to access the API based on predictive paginated collection templates. - -## Common behaviour - -```php -$iterator = $computeService->flavorList(); -``` - -There are two ways to traverse an iterator. The first is the longer, more traditional way: - -```php -while ($iterator->valid()) { - $flavor = $iterator->current(); - - // do stuff.. - echo $flavor->id; - - $iterator->next(); -} -``` - -There is also a shorter and more intuitive version: - -```php -foreach ($iterator as $flavor) { - // do stuff... - echo $flavor->id; -} -``` - -Because the iterator implements PHP's native `Iterator` interface, it can inherit all the native functionality of traversible data structures with `foreach`. - -## Very important note - -Until now, users have been expected to do this: - -```php -while ($flavor = $iterator->next()) { - // ... -} -``` - -which is **incorrect**. The single responsibility of `next` is to move the internal pointer forward. It is the job of `current` to retrieve the current element. - -For your convenience, these two Iterator classes are fully backward compatible: they exhibit all the functionality you'd expect from a correctly implemented iterator, but they also allow previous behaviour. - -## Using paginated collections - -For large collections, such as retrieving DataObjects from CloudFiles/Swift, you need to use pagination. Each resource will have a different limit per page; so once that page is traversed, there needs to be another API call to retrieve to *next* page's resources. - -There are two key concepts: - -- **limit** is the amount of resources returned per page -- **marker** is the way you define a starting point. It is some form of identifier that allows the collection to begin from a specific resource - -### Resource classes - -When the iterator returns a current element in the internal list, it populates the relevant resource class with all the data returned to the API. In most cases, a `stdClass` object will become an instance of `OpenCloud\Common\PersistentObject`. - -In order for this instantiation to happen, the `resourceClass` option must correspond to some method in the parent class that creates the resource. For example, if we specify 'ScalingPolicy' as the `resourceClass`, the parent object (in this case `OpenCloud\Autoscale\Group`, needs to have some method will allows the iterator to instantiate the child resource class. These are all valid: - -1. `Group::scalingGroup($data);` - -2. `Group::getScalingGroup($data);` - -3. `Group::resource('ScalingGroup', $data);` - -where `$data` is the standard object. This list runs in order of precedence. - -## Setting up a PaginatedIterator - -```php -use OpenCloud\Common\Collection\PaginatedIterator; - -$service = $client->computeService(); - -$flavors = PaginatedIterator::factory($service, array( - 'resourceClass' => 'Flavor', - 'baseUrl' => $service->getUrl('flavors') - 'limit.total' => 350, - 'limit.page' => 100, - 'key.collection' => 'flavors' -)); - -foreach ($flavors as $flavor) { - echo $flavor->getId(); -} -``` - -As you can see, there are a lot of configuration parameters to pass in - and getting it right can be quite fiddly, involving a lot of API research. For this reason, using the convenience methods like `flavorList` is recommended because it hides the complexity. - -### PaginatedIterator options - -There are certain configuration options that the paginated iterator needs to work. These are: - -Name|Description|Type|Required|Default| ----|---|---|---|---| -resourceClass|The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator.|string|Yes|- -baseUrl|The base URL that is used for making new calls to the API for new pages|`Guzzle\Http\Url`|Yes|- -limit.total|The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list|int|No|10000 -limit.page|The amount of resources each page contains|int|No|100 -key.links|Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is `images_links`.|string|No|links -key.collection|The top-level key for the array of resources. For example, servers are returned with this data structure: `{"servers": [...]}`. The **key.collection** value in this case would be `servers`.|string|No|`null` -key.collectionElement|Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: `{"keypairs": [ {"keypair": {...}} ] }`. So in this case the collectionElement key would be `keypair`.|string|No|`null` -key.marker|The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is `id` or `name`.|string|No|name -request.method|The HTTP method used when making API calls for new pages|string|No|GET -request.headers|The HTTP headers to send when making API calls for new pages|array|No|`array()` -request.body|The HTTP entity body to send when making API calls for new pages|`Guzzle\Http\EntityBody`|No|`null` -request.curlOptions|Additional cURL options to use when making API calls for new pages|array|No|`array()` +https://doc.php-opencloud.com/en/latest/iterators.html diff --git a/docs/userguide/LoadBalancer/README.md b/docs/userguide/LoadBalancer/README.md index 03364f103..34008ab6c 100644 --- a/docs/userguide/LoadBalancer/README.md +++ b/docs/userguide/LoadBalancer/README.md @@ -1,80 +1,5 @@ # Load Balancers -A **load balancer** is a device that distributes incoming network traffic amongst -multiple back-end systems. These back-end systems are called the **nodes** of -the load balancer. +Our docs have moved! Please visit the below link: -## Getting started - -### 1. Instantiate a Rackspace client. - -```php - -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### 2. Retrieve the server instances you want to add as nodes of the load balancer. - -```php -$computeService = $client->computeService('cloudServersOpenStack', 'DFW'); - -$serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); -$serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); -``` - -The example above uses two server instances that have already been created. It -retrieves the server instances using their IDs. See also: [creating server instances](). - -### 3. Obtain a Load Balancer service object from the client. - -This object will be used to first define the load balancer nodes and later create the load balancer itself. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); -``` - -### 4. Define a load balancer node for each server. - -```php -$loadBalancer = $loadBalancerService->loadBalancer(); - -$serverOneNode = $loadBalancer->node(); -$serverOneNode->address = $serverOne->addresses->private[0]->addr; -$serverOneNode->port = 8080; -$serverOneNode->condition = 'ENABLED'; - -$serverTwoNode = $loadBalancer->node(); -$serverTwoNode->address = $serverTwo->addresses->private[0]->addr; -$serverTwoNode->port = 8080; -$serverTwoNode->condition = 'ENABLED'; -``` - -In the example above, each node runs a service that listens on port 8080. Further, -each node will start out as `ENABLED`, which means it will be ready to receive -network traffic from the load balancer as soon as it is created. - -### 5. Create the load balancer with the two nodes. - -```php -$loadBalancer->addVirtualIp('PUBLIC'); -$loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) -)); -``` - -In the example above, the load balancer will have a virtual IP address accessible -from the public Internet. Also notice that the port the load balancer listens -on (80) does not need to match the ports of its nodes (8080). - -## Next steps - -Once you have created a load balancer, there is a lot you can do with it. See -the [complete user guide for load balancers](USERGUIDE.md). \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 0c15f0b29..0a5c3b15d 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -1,626 +1,5 @@ # The Complete User Guide to Load Balancers -## Prerequisites +Our docs have moved! Please visit the below link: -### Client -To use the load balancers service, you must first instantiate a `Rackspace` -client object. - -```php -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' -)); -``` - -### Load Balancer Service -All operations on load balancers are done via a load balancer service object. - -```php -$loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); -``` - -### Cloud Servers -Many of the examples in this document use two cloud servers as nodes for -the load balancer. The variables `$serverOne` and `$serverTwo` refer to these -two cloud servers. - -## Load Balancers - -A **load balancer** is a device that distributes incoming network traffic amongst -multiple back-end systems. These back-end systems are called the **nodes** of -the load balancer. - -### Create Load Balancer - -```php -$loadBalancer = $loadBalancerService->loadBalancer(); - -$serverOneNode = $loadBalancer->node(); -$serverOneNode->address = $serverOne->addresses->private[0]->addr; -$serverOneNode->port = 8080; -$serverOneNode->condition = 'ENABLED'; - -$serverTwoNode = $loadBalancer->node(); -$serverTwoNode->address = $serverTwo->addresses->private[0]->addr; -$serverTwoNode->port = 8080; -$serverTwoNode->condition = 'ENABLED'; - -$loadBalancer->addVirtualIp('PUBLIC'); -$loadBalancer->create(array( - 'name' => 'My smart load balancer', - 'port' => 80, - 'protocol' => 'HTTP', - 'nodes' => array($serverOneNode, $serverTwoNode) -)); -``` - -### List Load Balancer Details - -You can retrieve a single load balancer's details by using its ID. - -```php -$loadBalancer = $loadBalancerService->loadBalancer('254889'); - -/** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ -``` - -### List Load Balancers - -You can retrieve a list of all your load balancers. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$loadBalancers = $loadBalancerService->loadBalancerList(); -foreach ($loadBalancers as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ -} -``` - -### Update Load Balancer Attributes - -You can update one or more of the following load balancer attributes: - -* `name`: The name of the load balancer -* `algorithm`: The algorithm used by the load balancer to distribute traffic amongst its nodes. See also: [Load balancing algorithms](#algorithms). -* `protocol`: The network protocol used by traffic coming in to the load balancer. See also: [Protocols](#protocols). -* `port`: The network port on which the load balancer listens for incoming traffic. -* `halfClosed`: Enable or Disable Half-Closed support for the load balancer. -* `timeout`: The timeout value for the load balancer to communicate with its nodes. -* `httpsRedirect`: Enable or disable HTTP to HTTPS redirection for the load balancer. When enabled, any HTTP request will return status code 301 (Moved Permanently), and the requestor will be redirected to the requested URL via the HTTPS protocol on port 443. For example, http://example.com/page.html would be redirected to https:// example.com/page.html. Only available for HTTPS protocol (`port` = 443), or HTTP Protocol with a properly configured SSL Termination (`secureTrafficOnly=true, securePort=443). See also: [SSL Termination](#ssl-termination). - -#### Updating a single attribute of a load balancer -```php -$loadBalancer->update(array( - 'name' => 'New name' -)); -``` - -#### Updating multiple attributes of a load balancer -```php -$loadBalancer->update(array( - 'name' => 'New name', - 'algorithm' => 'ROUND_ROBIN' -)); -``` - -### Remove Load Balancer - -When you no longer have a need for the load balancer, you can remove it. - -```php -$loadBalancer->delete(); -``` - -## Nodes - -A **node** is a backend device that provides a service on specified IP and port. An example of a load balancer node might be a web server serving HTTP traffic on port 8080. - -A load balancer typically has multiple nodes attached to it so it can distribute incoming network traffic amongst them. - -### List Nodes - -You can list the nodes attached to a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$nodes = $loadBalancer->nodeList(); -foreach ($nodes as $node) { - /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ -} -``` - -### Add Nodes - -You can attach additional nodes to a load balancer. Assume `$loadBalancer` already has two nodes attached to it - `$serverOne` and `$serverTwo` - and you want to attach a third node to it, say `$serverThree`, which provides a service on port 8080. - -**Important:** Remember to call `$loadBalancer->addNodes()` after all the calls to `$loadBalancer->addNode()` as shown below. - -```php -$address = $serverThree->addresses->private[0]->addr; -$loadBalancer->addNode($address, 8080); -$loadBalancer->addNodes(); -``` - -The `addNode` method accepts three more optional parameters, in addition to the two shown above: - -| Position | Description | Data type | Required? | Default value | -| ----------- | --------------- | --------------| -------------- | ----------------- | -| 1 | IP address of node | String | Yes | - | -| 2 | Port used by node's service | Integer | Yes | - | -| 3 | Starting condition of node:
          • `ENABLED` – Node is ready to receive traffic from the load balancer.
          • `DISABLED` – Node should not receive traffic from the load balancer.
          • `DRAINING` – Node should process any traffic it is already receiving but should not receive any further traffic from the load balancer.
          | String | No | `ENABLED` | -| 4 | Type of node to add:
          • `PRIMARY` – Nodes defined as PRIMARY are in the normal rotation to receive traffic from the load balancer.
          • `SECONDARY` – Nodes defined as SECONDARY are only in the rotation to receive traffic from the load balancer when all the primary nodes fail.
          | String | No | `PRIMARY` | -| 5 | Weight, between 1 and 100, given to node when distributing traffic using either the `WEIGHTED_ROUND_ROBIN` or the `WEIGHTED_LEAST_CONNECTIONS` load balancing algorithm. | Integer | No | 1 | - -### Modify Nodes -You can modify one or more of the following node attributes: - -* `condition`: The condition of the load balancer: - * `ENABLED` – Node is ready to receive traffic from the load balancer. - * `DISABLED` – Node should not receive traffic from the load balancer. - * `DRAINING` – Node should process any traffic it is already receiving but should not receive any further traffic from the load balancer. -* `type`: The type of the node: - * `PRIMARY` – Nodes defined as PRIMARY are in the normal rotation to receive traffic from the load balancer. - * `SECONDARY` – Nodes defined as SECONDARY are only in the rotation to receive traffic from the load balancer when all the primary nodes fail. -* `weight`: The weight, between 1 and 100, given to node when distributing traffic using either the `WEIGHTED_ROUND_ROBIN` or the `WEIGHTED_LEAST_CONNECTIONS` load balancing algorithm. - -#### Modifying a single attribute of a node -```php -use OpenCloud\LoadBalancer\Enum\NodeCondition; - -$node->update(array( - 'condition' => NodeCondition::DISABLED -)); -``` - -#### Modifying multiple attributes of a node -```php -use OpenCloud\LoadBalancer\Enum\NodeCondition; -use OpenCloud\LoadBalancer\Enum\NodeType; - -$node->update(array( - 'condition' => NodeCondition::DISABLED, - 'type' => NodeType::SECONDARY -)); -``` - -### Remove Nodes - -There are two ways to remove a node. - -#### Given an `OpenCloud\LoadBalancer\Resource\Node` instance -```php -$node->delete(); -``` - -#### Given an `OpenCloud\LoadBalancer\Resource\LoadBalancer` instance and a node ID -```php -$loadBalancer->removeNode(490639); -``` - -The `removeNode` method, as shown above, accepts the following arguments: - -|Position| Description | Data type | Required? | Default value | -|----------- | --------------- | -------------- |-------------- | ----------------- | -| 1 | ID of node | Integer | Yes | - | - -### View Node Service Events -You can view events associated with the activity between a node and a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$nodeEvents = $loadBalancer->nodeEventList(); -foreach ($nodeEvents as $nodeEvent) { - /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ -} -``` - -## Virtual IPs - -A **virtual IP (VIP)** makes a load balancer accessible by clients. The load balancing service supports either a public VIP address (`PUBLIC`), routable on the public Internet, or a ServiceNet VIP address (`SERVICENET`), routable only within the region in which the load balancer resides. - -### List Virtual IPs - -You can list the VIPs associated with a load balancer. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$vips = $loadBalancer->virtualIpList(); -foreach ($vips as $vip) { - /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ -} -``` - -### Add Virtual IPv6 - -You can add additional IPv6 VIPs to a load balancer. - -```php -use OpenCloud\LoadBalancer\Enum\IpType; - -$loadBalancer->addVirtualIp(IpType::PUBLIC, 6); -``` - -The `addVirtualIp` method, as shown above, accepts the following arguments: - -| Position | Description | Data type | Required? | Default value | -| ----------- | --------------- | -------------- |-------------- | ----------------- | -| 1 | Type of VIP:
          • `PUBLIC` – An IP address that is routable on the public Internet.
          • `SERVICENET` – An IP address that is routable only on ServiceNet.
          | String | No | `PUBLIC` | -| 2 | IP version: Must be `6` | Integer | Yes | - | - - -### Remove Virtual IPs - -You can remove a VIP from a load balancer. - -```php -$vip->remove(); -``` - -Please note that a load balancer must have at least one VIP associated with it. If you try to remove a load balancer's last VIP, a `ClientErrorResponseException` will be thrown. - -## Algorithms - -Load balancers use an **algorithm** to determine how incoming traffic is distributed amongst the back-end nodes. - -### List Load Balancing Algorithms - -You can list all supported load balancing algorithms using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$algorithms = $loadBalancerService->algorithmList(); -foreach ($algorithms as $algorithm) { - /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ -} -``` - -## Protocols - -When a load balancer is created a network protocol must be specified. This network protocol should be based on the network protocol of the back-end service being load balanced. - -### List Load Balancing Protocols - -You can list all supported network protocols using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$protocols = $loadBalancerService->protocolList(); -foreach ($protocols as $protocol) { - /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ -} -``` - -## Session Persistence - -**Session persistence** is a feature of the load balancing service that forces multiple requests, of the same protocol, from clients to be directed to the same node. This is common with many web applications that do not inherently share application state between back-end servers. - -There are two types (or modes) of session persistence: - -| Name | Description | -| -------- | --------------- | -| `HTTP_COOKIE` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | -| `SOURCE_IP` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | - -### List Session Persistence Configuration - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistenceType = $sessionPersistence->persistenceType; - -/** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ -``` - -In the example above: - -* If session persistence is enabled, the value of `$sessionPersistenceType` is the type of session persistence: either `HTTP_COOKIE` or `SOURCE_IP`. -* If session persistence is disabled, the value of `$sessionPersistenceType` is `null`. - -### Enable Session Persistence - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistence->update(array( - 'persistenceType' => 'HTTP_COOKIE' -)); -``` - -### Disable Session Persistence - -```php -$sessionPersistence = $loadBalancer->sessionPersistence(); -$sessionPersistence->delete(); -``` - -## Connection Logging - -The **connection logging** feature allows logs to be delivered to a Cloud Files account every hour. For HTTP-based protocol traffic, these are Apache-style access logs. For all other traffic, this is connection and transfer logging. - -### Check Logging Configuration - -```php -/** @var $connectionLogging bool **/ - -$connectionLogging = $loadBalancer->hasConnectionLogging(); -``` -In the example above: - -* If connection logging is enabled, the value of `$connectionLogging` is `true`. -* If connection logging is disabled, the value of `$connectionLogging` is `false`. - -### Enable Connection Logging - -```php -$loadBalancer->enableConnectionLogging(true); -``` - -### Disable Connection Logging - -```php -$loadBalancer->enableConnectionLogging(false); -``` - -## Error Page - -An **error page** is the html file that is shown to the end user when an error in the service has been thrown. By default every virtual server is provided with the default error file. It is also possible to set a custom error page for a load balancer. - -### View Error Page Content - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPageContent = $errorPage->content; - -/** @var $errorPageContent string **/ -``` - -In the example above the value of `$errorPageContent` is the HTML for that page. This could either be the HTML of the default error page or of your custom error page. - -### Set Custom Error Page - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPage->update(array( - 'content' => '' -)); -``` - -### Delete Custom Error Page - -```php -$errorPage = $loadBalancer->errorPage(); -$errorPage->delete(); -``` - -## Allowed Domains - -**Allowed domains** are a restricted set of domain names that are allowed to add load balancer nodes. - -### List Allowed Domains - -You can list all allowed domains using a load balancer service object. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$allowedDomains = $loadBalancerService->allowedDomainList(); -foreach ($allowedDomains as $allowedDomain) { - /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ -} -``` - -## Access Lists - -**Access Lists** allow fine-grained network access to a load balancer's VIP. Using access lists, network traffic to a load balancer's VIP can be allowed or denied from a single IP address, multiple IP addresses or entire network subnets. - -Note that `ALLOW` network items will take precedence over `DENY` network items in an access list. - -To reject traffic from all network items except those with the `ALLOW` type, add a `DENY` network item with the address of `0.0.0.0/0`. - -### View Access List - -You can view a load balancer's access list. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$accessList = $loadBalancer->accessList(); -foreach ($accessList as $networkItem) { - /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ -} -``` - -### Add Network Items To Access List - -You can add network items to a load balancer's access list very easily: - -```php -$loadBalancer->createAccessList(array( - (object) array( - 'type' => 'ALLOW', - 'address' => '206.160.165.1/24' - ), - (object) array( - 'type' => 'DENY', - 'address' => '0.0.0.0/0' - ) -)); -``` - -In the above example, we allowed access for 1 IP address, and used the "0.0.0.0" - wildcard to blacklist all other traffic. - -### Remove Network Item From Access List - -You an remove a network item from a load balancer's access list. - -```php -$networkItem->delete(); -``` - -## Content Caching - -When **content caching** is enabled on a load balancer, recently-accessed files are stored on the load balancer for easy retrieval by web clients. Requests to the load balancer for these files are serviced by the load balancer itself, which reduces load off its back-end nodes and improves response times as well. - -### Check Content Caching Configuration - -```php -/** @var $contentCaching bool **/ - -$contentCaching = $loadBalancer->hasContentCaching(); -``` -In the example above: - -* If content caching is enabled, the value of `$contentCaching` is `true`. -* If content caching is disabled, the value of `$contentCaching` is `false`. - -### Enable Content Caching - -```php -$loadBalancer->enableContentCaching(true); -``` - -### Disable Content Caching - -```php -$loadBalancer->enableContentCaching(false); -``` - -## SSL Termination - -The SSL Termination feature allows a load balancer user to terminate SSL traffic at the load balancer layer versus at the web server layer. A user may choose to configure SSL Termination using a key and an SSL certificate or an (Intermediate) SSL certificate. - -When SSL Termination is configured on a load balancer, a secure shadow server is created that listens only for secure traffic on a user-specified port. This shadow server is only visible to and manageable by the system. Existing or updated attributes on a load balancer with SSL Termination will also apply to its shadow server. For example, if Connection Logging is enabled on an SSL load balancer, it will also be enabled on the shadow server and Cloud Files logs will contain log files for both. - -### View current SSL termination config - -```php -/** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ - -$sslConfig = $loadBalancer->SSLTermination(); -``` - -### Update SSL termination config - -```php -$sslConfig->update(array( - 'enabled' => true, - 'securePort' => 443, - 'privateKey' => $key, - 'certificate' => $cert -)); -``` - -For a full list, with explanations, of required and optional attributes, please consult the [official documentation](http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/SSLTermination-d1e2479.html) - -### Delete SSL termination config - -```php -$sslConfig->delete(); -``` - -## Metadata - -Metadata can be associated with each load balancer and each node for the client's personal use. It is defined using key-value pairs where the key and value consist of alphanumeric characters. A key is unique per load balancer. - -### List metadata - -```php -$metadataList = $loadBalancer->metadataList(); - -foreach ($metadataList as $metadataItem) { - printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); -} -``` - -Please consult the [iterator documentation](docs/userguide/Iterators.md) for more information about iterators. - -### Add metadata - -```php -$metadataItem = $loadBalancer->metadata(); -$metadataItem->create(array( - 'key' => 'foo', - 'value' => 'bar' -)); -``` - -### Modify metadata - -```php -$metadataItem = $loadBalancer->metadata('foo'); -$metadataItem->update(array( - 'value' => 'baz' -)); -``` - -### Remove metadata - -```php -$metadataItem->delete(); -``` - -## Monitors - -The load balancing service includes a health monitoring operation which periodically checks your back-end nodes to ensure they are responding correctly. If a node is not responding, it is removed from rotation until the health monitor determines that the node is functional. In addition to being performed periodically, the health check also is performed against every node that is added to ensure that the node is operating properly before allowing it to service traffic. Only one health monitor is allowed to be enabled on a load balancer at a time. - -```php -/** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ - -$healthMonitor = $loadBalancer->healthMonitor(); - -printf( - "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", - $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout -); -``` - -For a full list, with explanations, of required and optional attributes, please consult the [official documentation](http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/Monitor_Connections-d1e3536.html) - -### Update or delete - -```php -// Update -$healthMonitor->update(array( - 'delay' => 120, - 'timeout' => 60, - 'type' => 'CONNECT' - 'attemptsBeforeDeactivation' => 3 -)); - -// Delete -$healthMonitor->delete(); -``` - -## Statistics - -You can retrieve detailed stats about your load balancer, including the following information: - -- `connectTimeOut` – Connections closed by this load balancer because the 'connect_timeout' interval was exceeded. -- `connectError` – Number of transaction or protocol errors in this load balancer. -- `connectFailure` – Number of connection failures in this load balancer. -- `dataTimedOut` – Connections closed by this load balancer because the 'timeout' interval was exceeded. -- `keepAliveTimedOut` – Connections closed by this load balancer because the 'keepalive_timeout' interval was exceeded. -- `maxConn` – Maximum number of simultaneous TCP connections this load balancer has processed at any one time. - -```php -/** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ - -$stats = $loadBalancer->stats(); -``` - -## Usage Reports - -The load balancer usage reports provide a view of all transfer activity, average number of connections, and number of virtual IPs associated with the load balancing service. Current usage represents all usage recorded within the preceding 24 hours. Values for both incomingTransfer and outgoingTransfer are expressed in bytes transferred. - -The optional startTime and endTime parameters can be used to filter all usage. If the startTime parameter is supplied but the endTime parameter is not, then all usage beginning with the startTime will be provided. Likewise, if the endTime parameter is supplied but the startTime parameter is not, then all usage will be returned up to the endTime specified. - -```php -# View billable LBs -$billable = $service->billableLoadBalancerList(); - -foreach ($billable as $loadBalancer) { - /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ - - # View usage - /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ - $usage = $loadBalancer->usage(); - - echo $usage->averageNumConnections, PHP_EOL; -} -``` +https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md index ee1de981e..1778646ad 100644 --- a/docs/userguide/Networking/README.md +++ b/docs/userguide/Networking/README.md @@ -1,76 +1,5 @@ # Networking -**Networking** is a service that you can use to create virtual networks and attach cloud devices such as servers to these networks. +Our docs have moved! Please visit the below link: -## Concepts - -## Concepts - -To use the Networking service effectively, you should understand the following key concepts: - -* **Network**: A network is an isolated virtual layer-2 broadcast domain that is typically reserved for the tenant who created it unless you configure the network to be shared. The network is the main entity in the Networking service. Ports and subnets are always associated with a network. - -* **Subnet**: A subnet represents an IP address block that can be used to assign IP addresses to virtual instances (such as servers created using the Compute service). Each subnet must have a CIDR and must be associated with a network. - -* **Port**: A port represents a virtual switch port on a logical network switch. Virtual instances (such as servers created using the Compute service) attach their interfaces into ports. The port also defines the MAC address and the IP address(es) to be assigned to the interfaces plugged into them. When IP addresses are associated to a port, this also implies the port is associated with a subet, as the IP address is taken from the allocation pool for a specific subnet. - - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -To use the Networking service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Networking service object from the client. -All Networking operations are done via an _networking service object_. To -instantiate this object, call the `networkingService` method on the `$client` -object. This method takes two arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$networkingService = $client->networkingService(null, $region); -``` - -Any networks, subnets, and ports created with this `$networkingService` instance will -be stored in the cloud region specified by `$region`. - -### 3. Create a network. -```php -$network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] - -## Next steps - -Once you have created a network, there is more you can do with it. See [complete user guide for networking](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 5a7beb07f..376daa093 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -1,617 +1,5 @@ # Complete User Guide for the Networking Service -Networking is a service that you can use to create virtual networks and attach -cloud devices such as servers to these networks. +Our docs have moved! Please visit the below link: -This user guide introduces you the entities in the Networking service — -networks, subnets, and ports — and shows you how to create and manage -these entities. - -## Table of contents - * [Concepts](#concepts) - * [Prerequisites](#prerequisites) - * [Client](#client) - * [Networking service](#networking-service) - * [Networks](#networks) - * [Create a network](#create-a-network) - * [Create multiple networks](#create-multiple-networks) - * [List networks](#list-networks) - * [Get a network](#get-a-network) - * [Update a network](#update-a-network) - * [Delete a network](#delete-a-network) - * [Subnets](#subnets) - * [Create a subnet](#create-a-subnet) - * [Create multiple subnets](#create-multiple-subnets) - * [List subnets](#list-subnets) - * [Get a subnet](#get-a-subnet) - * [Update a subnet](#update-a-subnet) - * [Delete a subnet](#delete-a-subnet) - * [Ports](#ports) - * [Create a port](#create-a-port) - * [Create multiple ports](#create-multiple-ports) - * [List ports](#list-ports) - * [Get a port](#get-a-port) - * [Update a port](#update-a-port) - * [Delete a port](#delete-a-port) - * [Security Groups](#security-groups) - * [Create a security group](#create-a-security-group) - * [List security groups](#list-security-groups) - * [Get a security group](#get-a-security-group) - * [Delete a security group](#delete-a-security-group) - * [Security group rule Rules](#security-group-rules) - * [Create a security group rule](#create-a-security-group-rule) - * [List security group rules](#list-security-group-rules) - * [Get a security group rule](#get-a-security-group-rule) - * [Delete a security group rule](#delete-a-security-group-rule) - -## Concepts - -To use the Networking service effectively, you should understand the following -key concepts: - -* **Network**: An isolated virtual layer-2 broadcast domain that is typically -reserved for the tenant who created it unless it is configured to be shared. The -network is the main entity in the Networking service. Ports and subnets are -always associated with a network. - -* **Subnet**: An IP address block that can be used to assign IP addresses to -virtual instances (such as servers created using the Compute service). Each -subnet must have a CIDR and must be associated with a network. - -* **Port**: A virtual switch port on a logical network switch. Virtual instances -(such as servers created using the Compute service) attach their interfaces into -ports. The port also defines the MAC address and the IP address or addresses to -be assigned to the interfaces plugged into them. When IP addresses are -associated with a port, this also implies the port is associated with a subnet -because the IP address is taken from the allocation pool for a specific subnet. - -* **Security Group**: A named container for security group rules. - -* **Security Group Rule**: Provide users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. - -## Prerequisites - -### Client -To use the Networking service, you must first instantiate a `OpenStack` or -`Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an -`OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate an -`OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Networking service - -All Networking operations are done via a _networking service object_. To -instantiate this object, call the `networkingService` method on the `$client` -object. This method takes the following arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudNetworks` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$networkingService = $client->networkingService(null, $region); -``` - -Any networks, subnets, and ports created with this `$networkingService` instance -are stored in the cloud region specified by `$region`. - -## Networks - -A network is an isolated virtual layer-2 broadcast domain that is typically -reserved for the tenant who created it unless it is configured to be shared. The -network is the main entity in the Networking service. Ports and subnets are -always associated with a network. - -### Create a network - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My private backend network` | -| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | -| `tenantId` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | `123456` | - -You can create a network as shown in the following example: - -```php -$network = $networkingService->createNetwork(array( - 'name' => 'My private backend network' -)); -/** @var $network OpenCloud\Networking\Resource\Network **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-network.php) ] - -### Create multiple networks - -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the preceding table](#create-a-network). - -You can create multiple networks as shown in the following example: - -```php -$networks = $networkingService->createNetworks(array( - array( - 'name' => 'My private backend network #1' - ), - array( - 'name' => 'My private backend network #2' - ) -)); - -foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-networks.php) ] - -### List networks - -You can list all the networks to which you have access as shown in the following example: - -```php -$networks = $networkingService->listNetworks(); -foreach ($networks as $network) { - /** @var $network OpenCloud\Networking\Resource\Network **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-networks.php) ] - -### Get a network - -You can retrieve a specific network by using that network's ID, as shown in the following example: - -```php -$network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); -/** @var $network OpenCloud\Networking\Resource\Network **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-network.php) ] - -### Update a network - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the network. This name might not be unique. | String | No | `null` | `My updated private backend network` | -| `adminStateUp` | The administrative state of network. If `false` (down), the network does not forward packets. | Boolean | No | `true` | `true` | -| `shared` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | `false` | `false` | - -You can update a network as shown in the following example: - -```php -$network->update(array( - 'name' => 'My updated private backend network' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-network.php) ] - -### Delete a network - -You can delete a network as shown in the following example: - -```php -$network->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-network.php) ] - -## Subnets - -A subnet represents an IP address block that can be used to assign IP addresses -to virtual instances (such as servers created using the Compute service). Each -subnet must have a CIDR and must be associated with a network. - -### Create a subnet - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `networkId` | Network this subnet is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `ipVersion` | IP version | Integer (`4` or `6`) | Yes | - | `4` | -| `cidr` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | `192.168.199.0/25` | -| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.199.128` | -| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `allocationPools` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | `array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))` | -| `hostRoutes` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | -| `tenantId` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | `123456` | - -You can create a subnet as shown in the following example: - -```php -$subnet = $networkingService->createSubnet(array( - 'name' => 'My subnet', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', - 'ipVersion' => 4, - 'cidr' => '192.168.199.0/25' -)); -/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-subnet.php) ] - -### Create multiple subnets - -This operation takes one parameter, an indexed array. Each element of this array must -be an associative array with the keys shown in [the preceding table](#create-a-subnet). - -You can create multiple subnets as shown in the following example: - -```php -$subnets = $networkingService->createSubnets(array( - array( - 'name' => 'My subnet #1' - ), - array( - 'name' => 'My subnet #2' - ) -)); - -foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-subnets.php) ] - -### List subnets - -You can list all the subnets to which you have access as shown in the following -example: - -```php -$subnets = $networkingService->listSubnets(); -foreach ($subnets as $subnet) { - /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-subnets.php) ] - -### Get a subnet - -You can retrieve a specific subnet by using that subnet's ID, as shown in the -following example: - -```php -$subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); -/** @var $subnet OpenCloud\Networking\Resource\Subnet **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-subnet.php) ] - -### Update a subnet - -This operation takes one parameter, an associative array, with the following -keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the subnet. This name might not be unique. | String | No | `null` | `My updated subnet` | -| `gatewayIp` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | `192.168.62.155` | -| `dnsNameservers` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | `array('4.4.4.4', '8.8.8.8')` | -| `hostRoutes` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | `array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))` | -| `enableDhcp` | Specifies whether DHCP is enabled for this subnet | Boolean | No | `true` | `false` | - -You can update a subnet as shown in the following example: - -```php -$subnet->update(array( - 'name' => 'My updated subnet', - 'hostRoutes' => array( - array( - 'destination' => '1.1.1.0/24', - 'nexthop' => '192.168.17.19' - ) - ), - 'gatewayIp' => '192.168.62.155' -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-subnet.php) ] - -### Delete a subnet - -You can delete a subnet as shown in the following example: - -```php -$subnet->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-subnet.php) ] - -## Ports - -A port represents a virtual switch port on a logical network switch. Virtual -instances (such as servers created using the Compute service) attach their -interfaces into ports. The port also defines the MAC address and the IP address -or addresses to be assigned to the interfaces plugged into them. When IP -addresses are associated with a port, this also implies the port is associated -with a subnet because the IP address is taken from the allocation pool for a -specific subnet. - -### Create a port - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `networkId` | Network this port is associated with | String | Yes | - | `eb60583c-57ea-41b9-8d5c-8fab2d22224c` | -| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | -| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `macAddress` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | `0F:5A:6F:70:E9:5C` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))` | -| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | -| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | -| `tenantId` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | `123456` | - -You can create a port as shown in the following example: - -```php -$port = $networkingService->createPort(array( - 'name' => 'My port', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' -)); -/** @var $port OpenCloud\Networking\Resource\Port **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-port.php) ] - -### Create multiple ports - -This operation takes one parameter, an indexed array. Each element of this -array must be an associative array with the keys shown in -[the preceding table](#create-a-port). - -You can create multiple ports as shown in the following example: - -```php -$ports = $networkingService->createPorts(array( - array( - 'name' => 'My port #1', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ), - array( - 'name' => 'My port #2', - 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' - ) -)); - -foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-ports.php) ] - -### List ports - -You can list all the ports to which you have access as shown in the following -example: - -```php -$ports = $networkingService->listPorts(); -foreach ($ports as $port) { - /** @var $port OpenCloud\Networking\Resource\Port **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-ports.php) ] - -### Get a port - -You can retrieve a specific port by using that port's ID, as shown in the -following example: - -```php -$port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); -/** @var $port OpenCloud\Networking\Resource\Port **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-port.php) ] - -### Update a port - -This operation takes one parameter, an associative array, with the following -keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the port. This name might not be unique. | String | No | `null` | `My port` | -| `adminStateUp` | The administrative state of port. If `false` (down), the port does not forward packets. | Boolean | No | `true` | `true` | -| `fixedIps` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | `array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))` | -| `deviceId` | Identifies the device (for example, virtual server) using this port | String | No | `null` | `5e3898d7-11be-483e-9732-b2f5eccd2b2e` | -| `deviceOwner` | Identifies the entity (for example, DHCP agent) using this port | String | No | `null` | `network:router_interface` | -| `securityGroups` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | `array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')` | - -You can update a port as shown in the following example: - -```php -$port->update(array( - 'fixedIps' => array( - array( - 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', - 'ipAddress' => '192.168.199.59' - ) - ) -)); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/update-port.php) ] - -### Delete a port - -You can delete a port as shown in the following example: - -```php -$port->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-port.php) ] - -## Security Groups - -A security group is a named container for [security group rules](#security-group-rules). - -### Create a security group - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | `new-webservers` | -| `description` | Description of the security group. | String | No | `null` | `security group for webservers` | - -You can create a security group as shown in the following example: - -```php -$securityGroup = $networkingService->createSecurityGroup(array( - 'name' => 'new-webservers', - 'description' => 'security group for webservers' -)); -/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-security-group.php) ] - -### List security groups - -You can list all the security groups to which you have access as shown in the following -example: - -```php -$securityGroups = $networkingService->listSecurityGroups(); -foreach ($securityGroups as $securityGroup) { - /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-security-groups.php) ] - -### Get a security group - -You can retrieve a specific security group by using that security group's ID, as shown in the -following example: - -```php -$securityGroup = $networkingService->getSecurityGroup('2076db17-a522-4506-91de-c6dd8e837028'); -/** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-security-group.php) ] - -### Delete a security group - -You can delete a security group as shown in the following example: - -```php -$securityGroup->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group.php) ] - -## Security Group Rules - -A security group rule provides users the ability to specify the types of traffic that are allowed to pass through to and from ports on a virtual server instance. - -### Create a security group rule - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `securityGroupId` | The security group ID to associate with this security group rule. | String | Yes | - | `2076db17-a522-4506-91de-c6dd8e837028` | -| `direction` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (`ingress` or `egress`) | Yes | - | `ingress` | -| `ethertype` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (`IPv4` or `IPv6`) | No | `IPv4` | `IPv6` | -| `portRangeMin` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the `portRangeMax` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | -| `portRangeMax` | The maximum port number in the range that is matched by the security group rule. The port_range_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | `null` | `80` | -| `protocol` | The protocol that is matched by the security group rule. | String (`tcp`, `udp`, `icmp`) | No | `null` | `tcp` | -| `remoteGroupId` | The remote group ID to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `85cc3048-abc3-43cc-89b3-377341426ac5` | -| `remoteIpPrefix` | The remote IP prefix to be associated with this security group rule. You can specify either `remoteGroupId` or `remoteGroupPrefix`. | String | Optional | `null` | `192.168.5.0` | - -You can create a security group rule as shown in the following example: - -```php -$securityGroupRule = $networkingService->createSecurityGroupRule(array( - 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', - 'direction' => 'egress', - 'ethertype' => 'IPv4', - 'portRangeMin' => 80, - 'portRangeMax' => 80, - 'protocol' => 'tcp', - 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' -)); -/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/create-security-group-rule.php) ] - -### List security group rules - -You can list all the security group rules to which you have access as shown in the following -example: - -```php -$securityGroupRules = $networkingService->listSecurityGroupRules(); -foreach ($securityGroupRules as $securityGroupRule) { - /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -} -``` - -[ [Get the executable PHP script for this example](/samples/Networking/list-security-group-rules.php) ] - -### Get a security group rule - -You can retrieve a specific security group rule by using that security group rule's ID, as shown in the -following example: - -```php -$securityGroupRule = $networkingService->getSecurityGroupRule(''); -/** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ -``` - -[ [Get the executable PHP script for this example](/samples/Networking/get-security-group-rule.php) ] - -### Delete a security group rule - -You can delete a security group rule as shown in the following example: - -```php -$securityGroupRule->delete(); -``` - -[ [Get the executable PHP script for this example](/samples/Networking/delete-security-group-rule.php) ] +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md index 0b15a2d19..8e224392a 100644 --- a/docs/userguide/ObjectStore/Access.md +++ b/docs/userguide/ObjectStore/Access.md @@ -1,66 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want -``` - -## Temporary URLs - -Temporary URLs allow you to create time-limited Internet addresses that allow you to grant access to your Cloud Files -account. Using Temporary URL, you may allow others to retrieve or place objects in your containers - regardless of -whether they're CDN-enabled. - -### Set "temporary URL" metadata key - -You must set this "secret" value on your account, where it can be used in a global state: - -```php -$account = $service->getAccount(); -$account->setTempUrlSecret('my_secret'); - -echo $account->getTempUrlSecret(); -``` - -The string argument of `setTempUrlSecret()` is optional - if left out, the SDK will generate a random hashed secret -for you. - -### Create a temporary URL - -Once you've set an account secret, you can create a temporary URL for your object. To allow GET access to your object -for 1 minute: - -```php -$object->getTemporaryUrl(60, 'GET'); -``` - -To allow PUT access for 1 hour: - -```php -$object->getTemporaryUrl(360, 'PUT'); -``` - -## Hosting websites on CloudFiles - -To host a static (i.e. HTML) website on CloudFiles, you must follow these steps: - -1. CDN-enable a container -2. Upload all HTML content. You can use nested directory structures. -3. Tell CloudFiles what to use for your default index page like this: - -```php -$container->setStaticIndexPage('index.html'); -``` - -4. (Optional) Tell CloudFiles which error page to use by default: - -```php -$container->setStaticErrorPage('error.html'); -``` - -Bear in mind that steps 3 & 4 do not upload content, but rather specify a reference to an existing page/CloudFiles object. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/access.html diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md index edd26c2c1..fc60751a8 100644 --- a/docs/userguide/ObjectStore/Account.md +++ b/docs/userguide/ObjectStore/Account.md @@ -1,28 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles'); -``` - -## View Account Details - -To see how many containers you have in your account (X-Account-Container-Count), how many objects are in your account -(X-Account-Object-Count), and how many total bytes your account uses (X-Account-Bytes-Used): - -```php -$account = $service->getAccount(); - -// Either return the full Metadata object -$details = $account->getDetails(); - -// or individual values -$account->getContainerCount(); -$account->getObjectCount(); -$account->getBytesUsed(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/account.html diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 642ee968d..9f3b4a5ca 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -1,78 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -$client = new Rackspace(RACKSPACE_US, array( - -)); - -$service = $client->objectStoreService('cloudFiles'); -``` - -To access the CDN functionality of a particular container: - -```php -$container = $service->getContainer('foo_bar'); - -$cdn = $container->getCdn(); -``` - -## List CDN-enabled container - -To list CDN-only containers, follow the same operation for Storage which lists all containers. The only difference is -which service object you execute the method on: - -```php -$cdnService = $service->getCdnService(); -$cdnContainers = $cdnService->listContainers(); - -foreach ($cdnContainers as $cdnContainer) { - -} -``` - -## CDN-enable and -disable a container - -Before a container can be CDN-enabled, it must exist in the storage system. When a container is CDN-enabled, any objects -stored in it are publicly accessible over the Content Delivery Network by combining the container's CDN URL with the -object name. - -Any CDN-accessed objects are cached in the CDN for the specified amount of time called the TTL. The default TTL value is -259200 seconds, or 72 hours. Each time the object is accessed after the TTL expires, the CDN refetches and caches the -object for the TTL period. - -```php -$container->enableCdn(); -$container->disableCdn(); -``` - -## Serving containers through SSL - -```php -$cdn->getCdnSslUri(); -``` - -## Streaming CDN-enabled containers - -```php -$cdn->getCdnStreamingUri(); -``` - -## iOS streaming - -The Cloud Files CDN allows you to stream video to iOS devices without needing to convert your video. Once you -CDN-enable your container, you have the tools necessary for streaming media to multiple devices. - -```php -$cdn->getIosStreamingUri(); -``` - -## CDN logging - -To enable and disable logging for your CDN: - -```php -$cdn->enableCdnLogging(); -$cdn->disableCdnLogging(); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md index 79e3d4cdc..9f3b4a5ca 100644 --- a/docs/userguide/ObjectStore/CDN/Object.md +++ b/docs/userguide/ObjectStore/CDN/Object.md @@ -1,20 +1,5 @@ ## Setup -You will need to instantiate the container object and access its CDN functionality as -[documented here](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/CDN/Container.md). +Our docs have moved! Please visit the below link: -## Purge CDN-enabled objects - -To remove a CDN object from public access: - -```php -$object->purge(); -``` - -You can also provide an optional e-mail address (or comma-delimeted list of e-mails), which the API will send a -confirmation message to once the object has been completely purged: - -```php -$object->purge('jamie.hannaford@rackspace.com'); -$object->purge('hello@example.com,hallo@example.com'); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index 33ad706dd..ea43fcdf7 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -1,65 +1,5 @@ # Object Store -**Object Store** is an object-based storage system that stores content and metadata as objects in a cloud. +Our docs have moved! Please visit the below link: -Specifically, a cloud is made up of one or more regions. Each region can have several **containers**, created by a user. Each container can container several **objects** (sometimes referred to as files), uploaded by the user. - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -Choose one of the following two options: - -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Object Store service object from the client. -```php -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); -``` - -In the example above, you are connecting to the ``DFW`` region of the cloud. Any containers and objects created with this `$objectStoreService` instance will be stored in that cloud region. - -### 3. Create a container for your objects (also referred to as files). - -```php -$container = $objectStoreService->createContainer('logos'); -``` - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### 4. Upload an object to the container. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/quickstart.php) ] - -## Next steps - -There is a lot more you can do with containers and objects. See -the [complete user guide to the Object Store service](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index 5f75de9d1..e9fd5af14 100644 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -1,182 +1,5 @@ ## Setup -```php -use OpenCloud\Rackspace; +Our docs have moved! Please visit the below link: -// Create a client object to communicate with various Rackspace Cloud services. -$client = new Rackspace(RACKSPACE_US, array( - 'username' => 'Replace this with your Rackspace Cloud user name', - 'apiKey' => 'Replace this with your Rackspace Cloud API key' -)); - -// Create a service object to use the object store service. The sample code -// creates the object store in the 'DFW' region. -$service = $client->objectStoreService('cloudFiles', 'DFW'); -``` - -## Create container - -To create a new container, you just need to define its name: - -```php -$container = $service->createContainer('my_amazing_container'); -``` - -If the response returned is `FALSE`, there was an API error - most likely due to the fact you have a naming collision. - -Container names must be valid strings between 0 and 256 characters. Forward slashes are not currently permitted. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -## List containers - -### Return a list of containers - -```php -$containerList = $service->listContainers(); - -while ($container = $containerList->next()) { - // Do stuff; some examples below - printf("Container name: %s\n", $container->name); - printf("Number of objects within container: %d\n", $container->getObjectCount()); -} -``` - -Container names are sorted based on a binary comparison, a single built-in collating sequence that compares string -data using SQLite's memcmp() function, regardless of text encoding. - -The list is limited to 10,000 containers at a time. See 1.3 for ways to limit and navigate this list. - -### Return a formatted list of containers - -Currently, the SDK only supports JSON-formatted responses. - -### Controlling a large list of containers - -You may limit and control this list of results by using the `marker` and `end_marker` parameters. The former parameter -(`marker`) tells the API where to begin the list, and the latter (`end_marker`) tells it where to end the list. You may -use either of them independently or together. You may also use the `limit` parameter to fix the number of containers -returned. - -To list a set of containers between two fixed points: - -```php -$someContainers = $service->listContainers(array( - 'marker' => 'container_55', - 'end_marker' => 'container_2001' -)); -``` - -Or to return a limited set: - -```php -$someContainers = $service->listContainers(array('limit' => 560)); -``` - -## Get container - -To retrieve a certain container, either to access its object or metadata: - -```php -$container = $service->getContainer('container_name'); - -echo $container->getObjectCount(); -echo $container->getBytesUsed(); -``` - -## Delete container - -Deleting a container is easy: -```php -$container->delete(); -``` - -Please bear mind that you must delete all objects inside a container before deleting it. This is done for you if you -set the `$deleteObjects` parameter to `TRUE` like so: - -```php -$container->delete(TRUE); -``` - -You can also do it manually: - -```php -$container->deleteAllObjects(); -$container->delete(); -``` - -## Create or update container metadata - -```php -$container->saveMetadata(array( - 'Author' => 'Virginia Woolf', - 'Published' => '1931' -)); -``` - -Please bear in mind that this action will set metadata to this array - overriding existing values and wiping those left -out. To _append_ values to the current metadata: - -```php -$metadata = $container->appendToMetadata(array( - 'Publisher' => 'Hogarth' -)); -``` - -If you only want to set the metadata to the local object, and not immediately retain these values on the API, you can -use a standard setter method - which can contribute to eventual actions like an update: - -```php -$container->setMetadata(array('Foo' => 'Bar')); -``` - -## Container quotas - -The container_quotas middleware implements simple quotas that can be imposed on Cloud Files containers by a user. -Setting container quotas can be useful for limiting the scope of containers that are delegated to non-admin users, -exposed to formpost uploads, or just as a self-imposed sanity check. - -To set quotas for a container: - -```php -use OpenCloud\Common\Constants\Size; - -$container->setCountQuota(1000); -$container->setBytesQuota(2.5 * Size::GB); -``` - -And to retrieve them: - -```php -echo $container->getCountQuota(); -echo $container->getBytesQuota(); -``` - -## Access log delivery - -To view your object access, turn on Access Log Delivery. You can use access logs to analyze the number of people who -access your objects, where they come from, how many requests for each object you receive, and time-based usage patterns -(such as monthly or seasonal usage). - -```php -$container->enableLogging(); -$container->disableLogging(); -``` - -## Syncing containers - -You can synchronize local directories with your CloudFiles/Swift containers very easily. When you do this, the container -will mirror exactly the nested file structure within your local directory: - -```php -$container->uploadDirectory('/home/Jamie/blog'); -``` - -There are four scenarios you should be aware of: - -Local|Remote|Comparison|Action ----|---|---|--- -File exists|File exists|Identical checksum|No action -File exists|File exists|Different checksum|Local file overwrites remote -File exists|File does not exist|-|Local file created in Swift -Files does not exist|File exists|-|Remote file deleted \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/containers.html diff --git a/docs/userguide/ObjectStore/Storage/Migrating.md b/docs/userguide/ObjectStore/Storage/Migrating.md index d92528cbf..8d12c382a 100644 --- a/docs/userguide/ObjectStore/Storage/Migrating.md +++ b/docs/userguide/ObjectStore/Storage/Migrating.md @@ -1,83 +1,5 @@ # Migrating containers (across regions) -## Introduction +Our docs have moved! Please visit the below link: -Currently, there exists no single API operation to copy containers across geographic endpoints. Although the API offers -a `COPY` operation for individual files, this does not work for cross-region copying. The SDK, however, does offer this -functionality. - -You **will** be charged for bandwidth between regions, so it's advisable to use ServiceNet where possible (which is -free). - -## Requirements - -* You must install the full Guzzle package, so that the process can take advantage of Guzzle's batching functionality -(it allows parallel requests to be batched for greater efficiency). You can do this by running: - -```bash -php composer.phar install --dev -``` - -* Depending on the size and number of transfer items, you will need to raise PHP's memory limit: - -```php -ini_set('memory_limit', '512M'); -``` - -* You will need to enact some kind of backoff/retry strategy for rate limits. Guzzle comes with a convenient feature -that just needs to be added as a normal subscriber: - -```php -use Guzzle\Plugin\Backoff\BackoffPlugin; - -$client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); -``` - -This tells the client to retry up to `10` times for failed requests have resulted in these HTTP status codes: `500`, -`503` or `408`. - -## Setup - -You can access all this functionality by executing: - -```php -$ordService = $client->objectStoreService('cloudFiles', 'ORD'); -$iadService = $client->objectStoreService('cloudFiles', 'IAD'); - -$oldContainer = $ordService->getContainer('old_container'); -$newContainer = $iadService->getContainer('new_container'); - -$iadService->migrateContainer($oldContainer, $newContainer); -``` - -It's advisable to do this process in a Cloud Server in one of the two regions you're migrating to/from. This allows you -to use `privateURL` as the third argument in the `objectStoreService` methods like this: - -```php -$client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); -``` - -This will ensure that traffic between your server and your new IAD container will be held over the internal Rackspace -network which is free. - -## Options - -You can pass in an array of arguments to the method: - -```php -$options = array( - 'read.batchLimit' => 100, - 'read.pageLimit' => 100, - 'write.batchLimit' => 50 -); - -$iadService->migrateContainer($oldContainer, $newContainer, $options); -``` - -### Options explained - -Name|Description|Default ----|---|--- -`read.pageLimit`|When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional `objectList` method, which calls the `PaginatedIterator`. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API)|10,000 -`read.batchLimit`|After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together.|1,000 -`write.batchLimit`|Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together.|100 \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/object-store/migrating-containers.html diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 4127b2610..0c58ca9fb 100644 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -1,275 +1,5 @@ ## Setup -Conceptually, a container contains objects (also known as files). In order to work with -objects, you will need to instantiate a container object first as [documented here](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/Storage/Container.md). +Our docs have moved! Please visit the below link: -## Note on object properties - -Please be aware that you cannot directly access the properties of DataObject anymore, you __must__ use appropriate getter/ -setter methods: - -|Property|Method| -|--------|------| -|Parent container|`getContainer`| -|Name|`getName`| -|Body of file|`getContent`| -|Size of file|`getContentLength`| -|Type of file|`getContentType`| -|ETag checksum|`getEtag`| -|Last modified date|`getLastModified`| - -## Create an object - -There are three ways to upload a new file, each of which has different business needs. - -> **Note:** Unlike previous versions, you do not need to manually specify your object's content type. The API will do this -for you. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### To upload a single/basic file: - -```php -use OpenCloud\ObjectStore\Resource\DataObject; - -$data = fopen('/path/to/sample.mp3', 'r+'); - -// alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) - -$meta = array( - 'Author' => 'Camera Obscura', - 'Origin' => 'Glasgow' -); - -$metaHeaders = DataObject::stockHeaders($meta); -$customHeaders = array(); -$allHeaders = $metaHeaders + $customHeaders; - -$container->uploadObject('sample.mp3', $data, $allHeaders); -``` - -### To upload multiple small-to-mid sized files: - -```php -$files = array( - array( - 'name' => 'apache.log', - 'path' => '/etc/httpd/logs/error_log' - ), - array( - 'name' => 'mysql.log', - 'body' => fopen('/tmp/mysql.log', 'r+') - ), - array( - 'name' => 'to_do_list.txt', - 'body' => 'PHONE HOME' - ) -); - -$container->uploadObjects($files); -``` - -As you can see, the `name` key is required for every file. You must also specify _either_ a path key (to an existing -file), or a `body`. The `body` can either be a PHP resource or a string representation of the content you want to upload. - -### To upload large files - -For files over 5GB, you will need to use the `OpenCloud\ObjectStore\Upload\TransferBuilder` factory to build your transfer, -upon which you can execute your upload functionality. For your convenience, the Container resource object contains a -simple method to do this heavy lifting for you: - -```php -$transfer = $container->setupObjectTransfer(array( - 'name' => 'video.mov', - 'path' => '/home/jamie/video.mov', - 'metadata' => array( - 'Author' => 'Jamie' - ), - 'concurrency' => 4, - 'partSize' => 1.5 * Size::GB -)); - -$transfer->upload(); -``` - -You can specify how many concurrent cURL connections are used to upload parts of your file. The file is fragmented into -chunks, each of which is uploaded individually as a separate file (the filename of each part will indicate that it's a -segment rather than the full file). After all parts are uploaded, a manifest is uploaded. When the end-user accesses -the 5GB by its true filename, it actually references the manifest file which concatenates each segment into a streaming -download. - -## List objects in a container - -To return a list of objects: - -```php -$files = $container->objectList(); - -foreach ($files as $file) { - // ... do something -} -``` - -By default, 10,000 objects are returned as a maximum. To get around this, you can construct a query which refines your -result set. For a full specification of query parameters relating to collection filtering, see the [official docs](http://docs.openstack.org/api/openstack-object-storage/1.0/content/list-objects.html). - -```php -$container->objectList(array('prefix' => 'logFile_')); -``` - -## Get object - -To retrieve a specific file from Cloud Files: - -```php -$file = $container->getObject('summer_vacation.mp4'); -``` - -### Conditional requests - -You can also perform conditional requests according to [RFC 2616 specification](http://www.ietf.org/rfc/rfc2616.txt) -(§§ 14.24-26). Supported headers are `If-Match`, `If-None-Match`, `If-Modified-Since` and `If-Unmodified-Since`. - -So, to retrieve a file's contents only if it's been recently changed - -```php -$file = $container->getObject('error_log.txt', array( - 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' -)); - -if ($file->getContentLength()) { - echo 'Has been changed since the above date'; -} else { - echo 'Has not been changed'; -} -``` - -Retrieve a file only if it has NOT been modified (and expect a 412 on failure): - -``` -use Guzzle\Http\Exception\ClientErrorResponseException; - -try { - $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( - 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' - )); -} catch (ClientErrorResponseException $e) { - echo 'This file has been modified...'; -} -``` - -Finally, you can specify a range - which will return a subset of bytes from the file specified. To return the last 20B -of a file: - -```php -$snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); -``` - -## Update an existing object - -Updating content is easy: - -```php -$file->setContent(fopen('/path/to/new/content', 'r+')); -$file->update(); -``` - -Bear in mind that updating a file name will result in a new file being generated (under the new name). You will need to -delete the old file. - -## Copy object - -To copy a file to another location, you need to specify a string-based destination path: - -```php -$object->copy('/container_2/new_object_name'); -``` - -## Delete object - -```php -$object->delete(); -``` - -## Get object metadata -You can fetch just the object metadata without fetching the full content: -```php -$container->getPartialObject('summer_vacation.mp4'); -``` - -In order to access the metadata on a partial or complete object, use: - -```php -$object->getMetadata(); -``` - -You can turn a partial object into a full object to get the content after looking at the metadata: -```php -$object->refresh(); -``` - -You can also update to get the latest metadata: - -```php -$object->retrieveMetadata(); -``` - -## Update object metadata - -Similarly, with setting metadata there are two options: you can update the metadata values of the local object (i.e. no -HTTP request) if you anticipate you'll be executing one soon (an update operation for example): - -```php -// There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation -$object->setMetadata(array( - 'Author' => 'Hemingway' -)); - -// ... code here - -$object->update(); -``` - -Alternatively, you can update the API straight away - so that everything is retained: - -```php -$object->saveMetadata(array( - 'Author' => 'Hemingway' -)); -``` - -Please be aware that these methods override and wipe existing values. If you want to append values to your metadata, use -the correct method: - -```php -$metadata = $object->appendToMetadata(array( - 'Author' => 'Hemingway' -)); - -$object->saveMetadata($metadata); -``` - -## Extract archive - -CloudFiles provides you the ability to extract uploaded archives to particular destinations. The archive will be extracted -and its contents will populate the particular area specified. To upload file (which might represent a directory structure) -into a particular container: - -```php -use OpenCloud\ObjectStore\Constants\UrlType; - -$service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); -``` - -You can also omit the container name (i.e. provide an empty string as the first argument). If you do this, the API will -create the containers necessary to house the extracted files - this is done based on the filenames inside the archive. - -## Bulk delete - -Bulk delete a set of paths: - -```php -$pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - -$service->bulkDelete($pathsToBeDeleted); -``` +https://doc.php-opencloud.com/en/latest/services/object-store/objects.html diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index fa04bdeaf..6881ee3e6 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -1,625 +1,5 @@ # The Complete User Guide to the Object Store Service -**Object Store** is an object-based storage system that stores content and metadata as objects in a cloud. +Our docs have moved! Please visit the below link: -## Prerequisites - -### Client -To use the object store service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with a vanilla OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as shown below. - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as shown below. - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Object Store Service -All operations on the object store are done via an object store service object. - -```php -$region = 'DFW'; -$objectStoreService = $client->objectStoreService(null, $region); -``` - -In the example above, you are connecting to the ``DFW`` region of the cloud. Any containers and objects created with this `$objectStoreService` instance will be stored in that cloud region. - -## Containers -A **container** defines a namespace for **objects**. An object with the same name in two different containers represents two different objects. - -For example, you may create a container called `logos` to hold all the image files for your blog. - -A container may contain zero or more objects in it. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### Create Container - -```php -$container = $objectStoreService->createContainer('logos'); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/create-container.php) ] - -### Get Container Details -You can retrieve a single container's details by using its name. An instance of `OpenCloud\ObjectStore\Resource\Container` is returned. - -```php -$container = $objectStoreService->getContainer('logos'); - -/** @var $container OpenCloud\ObjectStore\Resource\Container **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container.php) ] - -### List Containers -You can retrieve a list of all your containers. An instance of `OpenCloud\Common\Collection\PaginatedIterator` -is returned. - -```php -$containers = $objectStoreService->listContainers(); -foreach ($containers as $container) { - /** @var $container OpenCloud\ObjectStore\Resource\Container **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-containers.php) ] - -### Set or Update Container Metadata -You can set metadata on a container. - -```php -$container->saveMetadata(array( - 'author' => 'John Doe' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-metadata.php) ] - -### Get Container Metadata -You can retrieve the metadata for a container. - -```php -$containerMetadata = $container->getMetadata(); - -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-metadata.php) ] - -### Delete Container -When you no longer have a need for the container, you can remove it. - -If the container is empty (that is, it has no objects in it), you can remove it as shown below: - -```php -$container->delete(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/delete-container.php) ] - -If the container is not empty (that is, it has objects in it), you have two choices in how to remove it: - -* [Individually remove each object](#delete-object) in the container, then remove the container itself as shown above, or - -* Remove the container and all the objects within it as shown below: - - ```php - $container->delete(true); - ``` - [ [Get the executable PHP script for this example](/samples/ObjectStore/delete-container-recursive.php) ] - -### Get Object Count - -You can quickly find out how many objects are in a container. - -```php -$containerObjectCount = $container->getObjectCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-object-count.php) ] - -In the example above, `$containerObjectCount` will contain the number of objects in the container represented by `$container`. - -### Get Bytes Used - -You can quickly find out the space used by a container, in bytes. - -```php -$containerSizeInBytes = $container->getBytesUsed(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-bytes-used.php) ] - -In the example above, `$containerSizeInBytes` will contain the space used, in bytes, by the container represented by `$container`. - -### Container Quotas - -#### Set Quota for Number of Objects - -You can set a quota for the maximum number of objects that may be stored in a container. - -```php -$maximumNumberOfObjectsAllowedInContainer = 25; -$container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-count-quota.php) ] - -#### Set Quota for Total Size of Objects - -You can set a quota for the maximum total space (in bytes) used by objects in a container. - -```php -use OpenCloud\Common\Constants\Size; - -$maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; -$container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-container-bytes-quota.php) ] - -#### Get Quota for Number of Objects - -You can retrieve the quota for the maximum number of objects that may be stored in a container. - -```php -$maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-count-quota.php) ] - -#### Get Quota for Total Size of Objects - -You can retrieve the quota for the maximum total space (in bytes) used by objects in a container. - -```php -$maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-container-bytes-quota.php) ] - -## Objects - -An **object** (sometimes referred to as a file) is the unit of storage in an Object Store. An object is a combination of content (data) and metadata. - -For example, you may upload an object named `php-elephant.jpg`, a JPEG image file, to the `logos` container. Further, you may assign metadata to this object to indicate that the author of this object was someone named Jane Doe. - -> **Note:** when working with names that contain non-standard alphanumerical characters (such as spaces or non-English characters), you must ensure they are encoded with [`urlencode`](http://php.net/urlencode) before passing them in - -### Upload Object - -Once you have created a container, you can upload objects to it. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$object = $container->uploadObject($remoteFileName, $fileData); -/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object.php) ] - -In the example above, an image file from the local filesystem (`path/to/local/php-elephant.jpg`) is uploaded to a container in the Object Store. - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -It is also possible to upload an object and associate metadata with it. - -```php -use OpenCloud\ObjectStore\Resource\DataObject; - -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'php-elephant.jpg'; -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); -$metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData, $allHeaders); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-object-with-metadata.php) ] - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -#### Pseudo-hierarchical Folders -Although you cannot nest directories in an Object Store, you can simulate a hierarchical structure within a single container by adding forward slash characters (`/`) in the object name. - -```php -$localFileName = '/path/to/local/php-elephant.jpg'; -$remoteFileName = 'languages/php/elephant.jpg'; - -$fileData = fopen($localFileName, 'r'); -$container->uploadObject($remoteFileName, $fileData); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/pseudo-hierarchical-folders.php) ] - -In the example above, an image file from the local filesystem (`/path/to/local/php-elephant.jpg`) is uploaded to a container in the Object Store. Within that container, the filename is `languages/php/elephant.jpg`, where `languages/php/` is a pseudo-hierarchical folder hierarchy. - -Note that while we call `fopen` to open the file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `uploadObject` call. - -#### Upload Multiple Objects -You can upload more than one object at a time to a container. - -```php -$objects = array( - array( - 'name' => 'php-elephant.jpg', - 'path' => '/path/to/local/php-elephant.jpg' - ), - array( - 'name' => 'python-snake.jpg', - 'path' => '/path/to/local/python-snake.jpg' - ), - a -); - -$responses = $container->uploadObjects($objects); -foreach ($responses as $response) { - /** @var $response \Guzzle\Http\Message\Response **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects.php) ] - -In the above example, the contents of two files present on the local filesystem are uploaded as objects to the container referenced by `$container`. - -Instead of specifying the `path` key in an element of the `$objects` array, you can specify a `body` key whose value is a string or a stream representation. - -You can pass headers as the second parameter to the `uploadObjects` method. These headers will be applied to every object that is uploaded. - -``` -$metadata = array('author' => 'Jane Doe'); - -$customHeaders = array(); -$metadataHeaders = DataObject::stockHeaders($metadata); -$allHeaders = $customHeaders + $metadataHeaders; - -$container->uploadObjects($objects, $allHeaders); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-multiple-objects-with-metadata.php) ] - -In the example above, every object referenced within the `$objects` array will be uploaded with the same metadata. - -Finally, if you want the `uploadObjects` method to return an array of `OpenCloud\ObjectStore\Resource\DataObject` objects instead of an array of `Guzzle\Http\Message\Response` objects, you can specify a third parameter to the `uploadObjects` method: - -``` -use OpenCloud\ObjectStore\Enum\ReturnType; - -$dataObjects = $container->uploadObjects($objects, $allHeaders, ReturnType::DATA_OBJECT_ARRAY); -foreach ($dataObjects as $dataObject) { - /** @var $dataObject OpenCloud\ObjectStore\Resource\DataObject **/ -} -``` - -### Large Objects - -If you want to upload objects larger than 5GB in size, you must use a different upload process. - -```php -$options = array( - 'name' => 'san_diego_vacation_video.mp4', - 'path' => '/path/to/local/videos/san_diego_vacation.mp4' -); -$objectTransfer = $container->setupObjectTransfer($options); -$objectTransfer->upload(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/upload-large-object.php) ] - -The process shown above will automatically partition your large object into small chunks and upload them concurrently to the container represented by `$container`. - -You can tune the parameters of this process by specifying additional options in the `$options` array. Here is a complete listing of keys that can be specified in the `$options` array: - -| Key name | Description | Data Type | Required? | Default Value | Example | -| -------------- | --------------- | ------------- | -------------- | ------------------ | ----------- | -| `name` | Name of large object in container | String | Yes | - | `san_diego_vacation_video.mp4` | -| `path` | Path to file containing object data on local filesystem | String | One of `path` or `body` must be specified | - | `/path/to/local/videos/san_diego_vacation.mp4` | -| `body` | String or stream representation of object data | String \| Stream | One of `path` or `body` must be specified | - | `... lots of data ...` | -| `metadata` | Metadata for the object | Associative array of metadata key-value pairs | No | `array()` | `array( "Author" => "Jane Doe" )` | -| `partSize` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | `1073741824` (1GB) | `52428800` (50MB) | -| `concurrency` | The number of concurrent transfers to execute as part of the upload | Integer | No | `1` (no concurrency; upload chunks serially) | `10` | -| `progress` | A [callable function or method](http://us1.php.net/manual/en/language.types.callable.php) which is called to report progress of the the upload. See [`CURLOPT_PROGRESSFUNCTION` documentation](http://us2.php.net/curl_setopt) for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | `reportProgress` | - -### Auto-extract Archive Files - -You can upload a tar archive file and have the Object Store service automatically extract it into a container. - -```php -use OpenCloud\ObjectStore\Constants\UrlType; - -$localArchiveFileName = '/path/to/local/image_files.tar.gz'; -$remotePath = 'images/'; - -$fileData = fopen($localArchiveFileName, 'r'); -$objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/auto-extract-archive-files.php) ] - -In the above example, a local archive file named `image_files.tar.gz` is uploaded to an Object Store container named `images` (defined by the `$remotePath` variable). - -Note that while we call `fopen` to open a file resource, we do not call `fclose` at the end. The file resource is automatically closed inside the `bulkExtract` call. - -The third parameter to `bulkExtract` is the type of the archive file being uploaded. The acceptable values for this are: - -* `UrlType::TAR` for tar archive files, *or*, -* `UrlType:TAR_GZ` for tar archive files that are compressed with gzip, *or* -* `UrlType::TAR_BZ` for tar archive file that are compressed with bzip - -Note that the value of `$remotePath` could have been a (pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as `images/blog` as well. - -### List Objects in a Container -You can list all the objects stored in a container. An instance of `OpenCloud\Common\Collection\PaginatedIterator` is returned. - -```php -$objects = $container->objectList(); -foreach ($objects as $object) { - /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ -} -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects.php) ] - -You can list only those objects in the container whose names start with a certain prefix. - -```php -$options = array( - 'prefix' => 'php' -); - -$objects = $container->objectList($options); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/list-objects-with-prefix.php) ] - -In general, the `objectList()` method described above takes an optional parameter (`$options` in the example above). This parameter is an associative array of various options. Here is a complete listing of keys that can be specified in the `$options` array: - -| Key name | Description | Data Type | Required? | Default Value | Example | -| -------------- | --------------- | ------------- | -------------- | ------------------ | ----------- | -| `prefix` | Given a string x, limits the results to object names beginning with x. | String | No | | `php` | -| `limit` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | -| `marker` | Given a string x, returns object names greater than the specified marker. | String | No | | `php-elephant.jpg` | -| `end_marker` | Given a string x, returns object names less than the specified marker. | String | No | | `python-snakes.jpg` | - -### Retrieve Object -You can retrieve an object and its metadata, given the object's container and name. - -```php -$objectName = 'php-elephant.jpg'; -$object = $container->getObject($objectName); - -/** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ - -$objectContent = $object->getContent(); - -/** @var $objectContent Guzzle\Http\EntityBody **/ - -// Write object content to file on local filesystem. -$objectContent->rewind(); -$stream = $objectContent->getStream(); -$localFilename = tempnam("/tmp", 'php-opencloud-'); -file_put_contents($localFilename, $stream); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object.php) ] - -In the example above, `$object` is the object named `php-elephant.jpg` in the container represented by `$container`. Further, `$objectContent` represents the contents of the object. It is of type [`Guzzle\Http\EntityBody`](http://api.guzzlephp.org/class-Guzzle.Http.EntityBody.html). - -### Retrieve Object Metadata -You can retrieve just an object's metadata without retrieving its contents. - -```php -$objectName = 'php-elephant.jpg'; -$object = $container->getPartialObject($objectName); -$objectMetadata = $object->getMetadata(); - -/** @var $objectMetadata \OpenCloud\Common\Metadata **/ -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object-metadata.php) ] - -In the example above, while `$object` is an instance of `OpenCloud\ObjectStore\Resource\DataObject`, that instance is only partially populated. Specifically, only properties of the instance relating to object metadata are populated. - -### Temporary URLs - -The Temporary URL feature allows you to create limited-time Internet addresses that allow you to grant limited access to your Object Store account. Using this feature, you can allow others to retrieve or place objects in your Object Store account for a specified amount of time. Access to the temporary URL is independent of whether or not your account is [CDN-enabled](#cdn-containers). Even if you do not CDN-enable a container, you can still grant temporary public access through a temporary URL. - -First, you must set the temporary URL secret on your account. This is a one-time operation; you only need to perform it the very first time you wish to use the temporary URLs feature. - -```php -$account->setTempUrlSecret(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-account-temp-url-secret.php) ] - -Note that this operation is carried out on `$account`, which is an instance of `OpenCloud\ObjectStore\Resource\Account`, a class representing [your object store account](#accounts). - -The above operation will generate a random secret and set it on your account. Instead of a random secret, if you wish to provide a secret, you can supply it as a parameter to the `setTempUrlSecret` method. - -```php -$account->setTempUrlSecret(''); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-account-temp-url-secret-specified.php) ] - -Once a temporary URL secret has been set on your account, you can generate a temporary URL for any object in your Object Store. - -```php -$expirationTimeInSeconds = 3600; // one hour from now -$httpMethodAllowed = 'GET'; -$tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-object-temporary-url.php) ] - -In the example above, a temporary URL for the object is generated. This temporary URL will provide public access to the object for an hour (3600 seconds), as specified by the `$expirationTimeInSeconds` variable. Further, only GET HTTP methods will be allowed on this URL, as specified by the `$httpMethodAllowed` variable. The other value allowed for the `$httpMethodAllowed` variable would be `PUT`. - -You can also retrieve the temporary URL secret that has been set on your account. - -```php -$tempUrlSecret = $account->getTempUrlSecret(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-temp-url-secret.php) ] - -### Update Object - -You can update an object's contents (as opposed to [updating its metadata](#update-object-metadata)) by simply re-[uploading the object](#upload-object) to its container using the same object name as before. - -### Update Object Metadata - -You can update an object's metadata after it has been uploaded to a container. - -```php -$object->saveMetadata(array( - 'author' => 'John Doe' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/update-object-metadata.php) ] - -### Copy Object - -You can copy an object from one container to another, provided the destination container already exists. - -```php -$object->copy('logos_copy/php.jpg'); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/copy-object.php) ] - -In the example above, both the name of the destination container (`logos_copy`)and the name of the destination object (`php.jpg`) have to be specified, separated by a `/`. - -### Delete Object - -When you no longer need an object, you can delete it. - -```php -$object->delete(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/delete-object.php) ] - -### Bulk Delete - -While you can delete individual objects as shown above, you can also delete objects and empty containers in bulk. - -```php -$objectStoreService->bulkDelete(array( - 'logos/php-elephant.png', - 'logos/python-snakes.png', - 'some_empty_container' -)); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/bulk-delete.php) ] - -In the example above, two objects (`some_container/object_a.png`, `some_other_container/object_z.png`) and one empty container (`some_empty_container`) are all being deleted in bulk via a single command. - -## CDN Containers - -Note: The functionality described in this section is available only on the Rackspace cloud. It will not work as described when working with a vanilla OpenStack cloud. - -Any container can be converted to a CDN-enabled container. When this is done, the objects within the container can be accessed from anywhere on the Internet via a URL. - -### Enable CDN Container - -To take advantage of CDN capabilities for a container and its objects, you must CDN-enable that container. - -```php -$container->enableCdn(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/enable-container-cdn.php) ] - -### Public URLs - -Once you have CDN-enabled a container, you can retrieve a publicly-accessible URL for any of its objects. There are four types of publicly-accessible URLs for each object. Each type of URL is meant for a different purpose. The sections below describe each of these URL types and how to retrieve them. - -#### HTTP URL -You can use this type of URL to access the object over HTTP. - -``` -$httpUrl = $object->getPublicUrl(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-http-url.php) ] - -#### Secure HTTP URL -You can use this type of URL to access the object over HTTP + TLS/SSL. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$httpsUrl = $object->getPublicUrl(UrlType::SSL); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-https-url.php) ] - -#### Streaming URL -You can use this type of URL to stream a video or audio object using Adobe's HTTP Dynamic Streaming. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$streamingUrl = $object->getPublicUrl(UrlType::STREAMING); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-streaming-url.php) ] - -#### IOS Streaming URL - -You can use this type of URL to stream an audio or video object to an iOS device. - -``` -use OpenCloud\ObjectStore\Constants\UrlType; - -$iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-cdn-object-ios-streaming-url.php) ] - -### Update CDN Container TTL -You can update the TTL of a CDN-enabled container. - -```php -$cdnContainer = $container->getCdn(); -$cdnContainer->setTtl(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/set-cdn-container-ttl.php) ] - -### Disable CDN Container - -If you no longer need CDN capabilities for a container, you can disable them. - -```php -$container->disableCdn(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/disable-container-cdn.php) ] - -## Accounts -An **account** defines a namespace for **containers**. An account can have zero or more containers in it. - -### Retrieve Account -You must retrieve the account before performing any operations on it. - -```php -$account = $objectStoreService->getAccount(); -``` - -### Get Container Count - -You can quickly find out how many containers are in your account. - -```php -$accountContainerCount = $account->getContainerCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-container-count.php) ] - -### Get Object Count - -You can quickly find out how many objects are in your account. - -```php -$accountObjectCount = $account->getObjectCount(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-object-count.php) ] - -In the example above, `$accountObjectCount` will contain the number of objects in the account represented by `$account`. - -### Get Bytes Used - -You can quickly find out the space used by your account, in bytes. - -```php -$accountSizeInBytes = $account->getBytesUsed(); -``` -[ [Get the executable PHP script for this example](/samples/ObjectStore/get-account-bytes-used.php) ] - -In the example above, `$accountSizeInBytes` will contain the space used, in bytes, by the account represented by `$account`. +https://doc.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index cb3c6245a..862a61ed7 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -1,81 +1,5 @@ # Orchestration -**Orchestration** is a service that can be used to create and manage cloud -resources. Examples of such resources are databases, load balancers, -servers and software installed on them. +Our docs have moved! Please visit the below link: -## Concepts - -To use the Orchestration service effectively, you should understand several -key concepts: - -* **Template**: An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment. The template specifies what resources should be used, what -attributes of these resources are parameterized and what information is -output to the user when a template is instantiated. - -* **Resource**: A resource is a template artifact that represents some component of your desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load balancer, some configuration management system, and so forth). - -* **Stack**: A stack is a running instance of a template. When a stack is created, -the resources specified in the template are created. - -## Getting started - -### 1. Instantiate an OpenStack or Rackspace client. - -To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### 2. Obtain an Orchestration service object from the client. -All Orchestration operations are done via an _orchestration service object_. To -instantiate this object, call the `orchestrationService` method on the `$client` -object as shown in the following example: - -```php -$region = ''; -$orchestrationService = $client->orchestrationService(null, $region); -``` - -Any stacks and resources created with this `$orchestrationService` instance will -be stored in the cloud region specified by `$region`. - -### 3. Create a stack from a template. -```php -$stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/quickstart.php) ] - -## Next steps - -Once you have created a stack, there is more you can do with it. See [complete user guide for orchestration](USERGUIDE.md). +https://doc.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index a6d8af0f9..902d7741b 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -1,506 +1,5 @@ # Complete User Guide for the Orchestration Service -Orchestration is a service that you can use to create and manage cloud resources -such as databases, load balancers, and servers, and the software installed on -servers. +Our docs have moved! Please visit the below link: -## Table of Contents - -* [Concepts](#concepts) -* [Prerequisites](#prerequisites) - * [Client](#client) - * [Orchestration service](#orchestration-service) -* [Templates](#templates) - * [Validate template](#validate-template) - * [Validate a template from a file](#validate-a-template-from-a-file) - * [Validate Template from URL](#validate-template-from-url) -* [Stacks](#stacks) - * [Preview stack](#preview-stack) - * [Preview a stack from a template file](#preview-a-stack-from-a-template-file) - * [Preview a stack from a template URL](#preview-a-stack-from-a-template-url) - * [Create stack](#create-stack) - * [Create a stack from a template file](#create-a-stack-from-a-template-file) - * [Create a stack from a template URL](#create-a-stack-from-a-template-url) - * [List stacks](#list-stacks) - * [Get stack](#get-stack) - * [Get stack template](#get-stack-template) - * [Update stack](#update-stack) - * [Update a stack from a template file](#update-a-stack-from-a-template-file) - * [Update Stack from Template URL](#update-stack-from-template-url) - * [Delete stack](#delete-stack) - * [Abandon Stack](#abandon-stack) - * [Adopt stack](#adopt-stack) -* [Stack resources](#stack-resources) - * [List stack resources](#list-stack-resources) - * [Get stack resource](#get-stack-resource) - * [Get stack resource metadata](#get-stack-resource-metadata) -* [Stack resource events](#stack-resource-events) - * [List stack events](#list-stack-events) - * [List stack resource events](#list-stack-resource-events) - * [Get stack resource event](#get-stack-resource-event) -* [Resource types](#resource-types) - * [List resource types](#list-resource-types) - * [Get resource type](#get-resource-type) - * [Get resource type template](#get-resource-type-template) -* [Build info](#build-info) - * [Get build info](#get-build-info) - -## Concepts - -To use the Orchestration service effectively, you should understand the following -key concepts: - -* **Template**: A JSON or YAML document that describes how a set of resources should -be assembled to produce a working deployment. The template specifies the resources -to use, the attributes of these resources that are parameterized and the information -that is sent to the user when a template is instantiated. - -* **Resource**: Some component of your architecture (a cloud server, a group of scaled -cloud servers, a load balancer, some configuration management system, and so on) that -is defined in a template. - -* **Stack**: A running instance of a template. When a stack is created, the resources -specified in the template are created. - -## Prerequisites - -### Client -To use the Orchestration service, you must first instantiate a `OpenStack` or `Rackspace` client object. - -* If you are working with an OpenStack cloud, instantiate an `OpenCloud\OpenStack` client as follows: - - ```php - use OpenCloud\OpenStack; - - $client = new OpenStack('', array( - 'username' => '', - 'password' => '' - )); - ``` - -* If you are working with the Rackspace cloud, instantiate a `OpenCloud\Rackspace` client as follows: - - ```php - use OpenCloud\Rackspace; - - $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => '', - 'apiKey' => '' - )); - ``` - -### Orchestration service -All Orchestration operations are done via an _orchestration service object_. To -instantiate this object, call the `orchestrationService` method on the `$client` -object. This method takes two arguments: - -| Position | Description | Data type | Required? | Default value | Example value | -| -------- | ----------- | ----------| --------- | ------------- | ------------- | -| 1 | Name of the service, as it appears in the service catalog | String | No | `null`; automatically determined when possible | `cloudOrchestration` | -| 2 | Cloud region | String | Yes | - | `DFW` | - - -```php -$region = ''; -$orchestrationService = $client->orchestrationService(null, $region); -``` - -Any stacks and resources created with this `$orchestrationService` instance will -be stored in the cloud region specified by `$region`. - -## Templates -An Orchestration template is a JSON or YAML document that -describes how a set of resources should be assembled to produce a working -deployment (known as a [stack](#stacks)). The template specifies the resources -to use, the attributes of these resources that are parameterized and the -information that is sent to the user when a template is instantiated. - -### Validate template -Before you use a template to create a stack, you might want to validate it. - -#### Validate a template from a file -If your template is stored on your local computer as a JSON or YAML file, you -can validate it as shown in the following example: - -```php -use OpenCloud\Common\Exceptions\InvalidTemplateError; - -try { - $orchestrationService->validateTemplate(array( - 'template' => file_get_contents(__DIR__ . '/lamp.yaml') - )); -} catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid -} -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] - -#### Validate Template from URL -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can validate it as shown in the following example: - -```php -use OpenCloud\Common\Exceptions\InvalidTemplateError; - -try { - $orchestrationService->validateTemplate(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' - )); -} catch (InvalidTemplateError $e) { - // Use $e->getMessage() for explanation of why template is invalid -} -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/validate-template-from-template-url.php) ] - -## Stacks -A stack is a running instance of a template. When a stack is created, the -[resources](#stack-resources) specified in the template are created. - -### Preview stack -Before you create a stack from a template, you might want to see what that stack -will look like. This is called _previewing the stack_. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters | Yes | - | `simple-lamp-setup` | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of the template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters. For example, see the parameters in [this template section](https://github.com/rackspace-orchestration-templates/lamp/blob/master/lamp.yaml#L22) | Associative array | No | `null` | `array('flavor_id' => 'general1-1')` | - -#### Preview a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to preview a stack as shown in the following example: - -```php -$stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-file.php) ] - -#### Preview a stack from a template URL - -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to preview a stack as shown in the following -example: - -```php -$stack = $orchestrationService->previewStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ) -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` - -[ [Get the executable PHP script for this example](/samples/Orchestration/preview-stack-from-template-url.php) ] - -### Create stack -You can create a stack from a template. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `name` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, `_`, `-` or `.` characters. | Yes | - | `simple-lamp-setup` | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null` | `array('server_hostname' => 'web01')` | -| `timeoutMins` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | - -#### Create a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to create a stack as shown in the following example: - -```php -$stack = $orchestrationService->createStack(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-file.php) ] - -#### Create a stack from a template URL -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to create a stack as shown in the following -example: - -```php -$stack = $orchestrationService->stack(); -$stack->create(array( - 'name' => 'simple-lamp-setup', - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/create-stack-from-template-url.php) ] - -### List stacks -You can list all the stacks that you have created as shown in the following example: - -```php -$stacks = $orchestrationService->listStacks(); -foreach ($stacks as $stack) { - /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stacks.php) ] - -### Get stack -You can retrieve a specific stack using its name, as shown in the following example: - -```php -$stack = $orchestrationService->getStack('simple-lamp-setup'); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack.php) ] - -### Get stack template -You can retrieve the template used to create a stack. Note that a JSON string is -returned, regardless of whether a JSON or YAML template was used to create the stack. - -```php -$stackTemplate = $stack->getTemplate(); -/** @var $stackTemplate string **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-template.php) ] - -### Update stack -You can update a running stack. - -This operation takes one parameter, an associative array, with the following keys: - -| Name | Description | Data type | Required? | Default value | Example value | -| ---- | ----------- | --------- | --------- | ------------- | ------------- | -| `template` | Template contents | String. JSON or YAML | No, if `templateUrl` is specified | `null` | `heat_template_version: 2013-05-23\ndescription: LAMP server\n` | -| `templateUrl` | URL of template file | String. HTTP or HTTPS URL | No, if `template` is specified | `null` | `https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml` | -| `parameters` | Arguments to the template, based on the template's parameters | Associative array | No | `null`| `array('flavor_id' => 'general1-1')` | -| `timeoutMins` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | - -#### Update a stack from a template file - -If your template is stored on your local computer as a JSON or YAML file, you -can use it to update a stack as shown in the following example: - -```php -$stack->update(array( - 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-file.php) ] - -#### Update Stack from Template URL - -If your template is stored as a JSON or YAML file in a remote location accessible -via HTTP or HTTPS, you can use it to update a stack as shown in the following -example: - -```php -$stack->update(array( - 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', - 'parameters' => array( - 'server_hostname' => 'web01', - 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' - ), - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/update-stack-from-template-url.php) ] - -### Delete stack - -If you no longer need a stack and all its resources, you can delete the stack -_and_ the resources as shown in the following example: - -```php -$stack->delete(); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/delete-stack.php) ] - -### Abandon Stack - -If you want to delete a stack but preserve all its resources, you can abandon -the stack as shown in the following example: - -```php -$abandonStackData = $stack->abandon(); -/** @var $abandonStackData string **/ - -file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/abandon-stack.php) ] - -Note that this operation returns data about the abandoned stack as a string. You -can use this data to recreate the stack by using the [adopt stack](#adopt-stack) -operation. - -### Adopt stack - -If you have data from an abandoned stack, you can re-create the stack as shown -in the following example: - -```php -$stack = $orchestrationService->adoptStack(array( - 'name' => 'simple-lamp-setup', - 'template' => file_get_contents(__DIR__ . '/lamp.yml'), - 'adoptStackData' => $abandonStackData, - 'timeoutMins' => 5 -)); -/** @var $stack OpenCloud\Orchestration\Resource\Stack **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/adopt-stack.php) ] - -## Stack resources - -A stack is made up of zero or more resources such as databases, load balancers, -and servers, and the software installed on servers. - -### List stack resources - -You can list all the resources for a stack as shown in the following example: - -```php -$resources = $stack->listResources(); -foreach ($resources as $resource) { - /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resources.php) ] - -### Get stack resource - -You can retrieve a specific resource in a stack bt using that resource's name, -as shown in the following example: - -```php -$resource = $stack->getResource('load-balancer'); -/** @var $resource OpenCloud\Orchestration\Resource\Resource **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource.php) ] - -### Get stack resource metadata - -You can retrieve the metadata for a specific resource in a stack as shown in the -following example: - -```php -$resourceMetadata = $resource->getMetadata(); -/** @var $resourceMetadata \stdClass **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-metadata.php) ] - -## Stack resource events -Operations on resources within a stack (such as the creation of a resource) produce -events. - -### List stack events -You can list all of the events for all of the resources in a stack as shown in -the following example: - -```php -$stackEvents = $stack->listEvents(); -foreach ($stackEvents as $stackEvent) { - /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-events.php) ] - -### List stack resource events -You can list all of the events for a specific resource in a stack as shown in the -following example: - -```php -$resourceEvents = $resource->listEvents(); -foreach ($resourceEvents as $resourceEvent) { - /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-stack-resource-events.php) ] - -### Get stack resource event -You can retrieve a specific event for a specific resource in a stack, by using -the resource event's ID, as shown in the following example: - -```php -$resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); -/** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-stack-resource-event.php) ] - -## Resource types -When you define a template, you must use resource types supported by your cloud. - -### List resource types -You can list all supported resource types as shown in the following example: - -```php -$resourceTypes = $orchestrationService->listResourceTypes(); -foreach ($resourceTypes as $resourceType) { - /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/list-resource-types.php) ] - -### Get resource type -You can retrieve a specific resource type's schema as shown in the following example: - -```php -$resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); -/** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type.php) ] - -### Get resource type template -You can retrieve a specific resource type's representation as it would appear -in a template, as shown in the following example: - -```php -$resourceTypeTemplate = $resourceType->getTemplate(); -/** @var $resourceTypeTemplate string **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-resource-type-template.php) ] - -## Build info - -### Get build info -You can retrieve information about the current Orchestration service build as -shown in the following example: - -```php -$buildInfo = $orchestrationService->getBuildInfo(); -/** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ -``` -[ [Get the executable PHP script for this example](/samples/Orchestration/get-build-info.php) ] +https://doc.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index dbaa53ba9..0e338956c 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -1,123 +1,5 @@ ## 1. Introduction -A __Claim__ is the process of a worker checking out a message to perform a task. Claiming a message prevents other -workers from attempting to process the same messages. +Our docs have moved! Please visit the below link: -## 2. Setup - -A Claim is initialized on its parent object, a Queue: - -```php -// To initialize an empty object: -$claim = $queue->getClaim(); - -// or retrieve a specific claim: -$claim = $queue->getClaim('51db7067821e727dc24df754'); -``` - -## 3. Claim messages - -### 3.1 Description - -This operation claims a set of messages (up to the value of the limit parameter) from oldest to newest and skips any -messages that are already claimed. If no unclaimed messages are available, the API returns a `204 No Content` message. - -When a client (worker) finishes processing a message, it should delete the message before the claim expires to ensure -that the message is processed only once. As part of the delete operation, workers should specify the claim ID (which is -best done by simply using the provided href). If workers perform these actions, then if a claim simply expires, the -server can return an error and notify the worker of the race condition. This action gives the worker a chance to roll -back its own processing of the given message because another worker can claim the message and process it. - -The age given for a claim is relative to the server's clock. The claim's age is useful for determining how quickly -messages are getting processed and whether a given message's claim is about to expire. - -When a claim expires, it is released. If the original worker failed to process the message, another client worker can -then claim the message. - -### 3.2 Attributes - -The `ttl` attribute specifies how long the server waits before releasing the claim. The ttl value must be between 60 and -43200 seconds (12 hours). You must include a value for this attribute in your request. - -The `grace` attribute specifies the message grace period in seconds. The value of grace value must be between 60 and -43200 seconds (12 hours). You must include a value for this attribute in your request. To deal with workers that have -stopped responding (for up to 1209600 seconds or 14 days, including claim lifetime), the server extends the lifetime of -claimed messages to be at least as long as the lifetime of the claim itself, plus the specified grace period. If a -claimed message would normally live longer than the grace period, its expiration is not adjusted. - -The `limit` attribute specifies the number of messages to return, up to 20 messages. If limit is not specified, limit - defaults to 10. The limit parameter is optional. - -### 3.3 Code - -```php -use OpenCloud\Common\Constants\Datetime; - -$queue->claimMessages(array( - 'limit' => 15, - 'grace' => 5 * Datetime::MINUTE, - 'ttl' => 5 * Datetime::MINUTE -)); -``` - -## 4. Query claim - -### 4.1 Description - -This operation queries the specified claim for the specified queue. Claims with malformed IDs or claims that are not -found by ID are ignored. - -### 4.2 Attributes - -Claim ID. - -### 4.3 Code - -```php -$claim = $queue->getClaim('51db7067821e727dc24df754'); -``` - -## 5. Update claim - -### 5.1 Description - -This operation updates the specified claim for the specified queue. Claims with malformed IDs or claims that are not -found by ID are ignored. - -Clients should periodically renew claims during long-running batches of work to avoid losing a claim while processing a -message. The client can renew a claim by executing this method on a specific __Claim__ and including a new TTL. The API - will then reset the age of the claim and apply the new TTL. - -### 5.2 Attributes - -See section 4.2. - -### 5.3 Code - -```php -use OpenCloud\Common\Constants\Datetime; - -$claim->update(array( - 'ttl' => 10 * Datetime::MINUTE -)); -``` - -## 6. Release claim - -### 6.1 Description - -This operation immediately releases a claim, making any remaining undeleted messages that are associated with the -claim available to other workers. Claims with malformed IDs or claims that are not found by ID are ignored. - -This operation is useful when a worker is performing a graceful shutdown, fails to process one or more messages, or is -taking longer than expected to process messages, and wants to make the remainder of the messages available to other workers. - -### 6.2 Attributes - -See section 4.2. - -### 6.3 Code - -```php -$message->delete(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/queues/claims.html diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md index d27f2fbbf..ce0f54b07 100644 --- a/docs/userguide/Queues/Message.md +++ b/docs/userguide/Queues/Message.md @@ -1,207 +1,5 @@ ## 1. Introduction -A __Message__ is a task, a notification, or any meaningful data that a producer or publisher sends to the queue. A -message exists until it is deleted by a recipient or automatically by the system based on a TTL (time-to-live) value. +Our docs have moved! Please visit the below link: -## 2. Setup - -A message is initialized from its parent object, a Queue: - -```php -// Setup an empty object -$message = $queue->getMessage(); - -// or retrieve an existing one -$message = $queue->getMessage(''); -``` - -## 3. Post message - -### 3.1 Description - -This operation posts the specified message or messages. You can submit up to 10 messages in a single request. - -When posting new messages, you specify only the `body` and `ttl` for the message. The API will insert metadata, such as -ID and age. - -### 3.2 Parameters - -How you pass through the array structure depends on whether you are executing multiple (3.3.2) or single (3.3.3) posts, -but the keys are the same. - -The `body` attribute specifies an arbitrary document that constitutes the body of the message being sent. The size of -this body is limited to 256 KB, excluding whitespace. - -The `ttl` attribute specifies how long the server waits before marking the message as expired and removing it from the -queue. The value of ttl must be between 60 and 1209600 seconds (14 days). Note that the server might not actually -delete the message until its age has reached up to (ttl + 60) seconds, to allow for flexibility in storage implementations. - -### 3.3 Code samples - -#### 3.3.1 Posting a single message - -``` -use OpenCloud\Common\Constants\Datetime; - -$queue->createMessage(array( - 'body' => (object) array( - 'event' => 'BackupStarted', - 'deadline' => '26.12.2013 - ), - 'ttl' => 2 * Datetime::DAY -)); -``` - -#### 3.3.2 Post a batch of messages - -Please note that the list of messages will be truncated at 10. For more, please execute another method call. - -```php -use OpenCloud\Common\Constants\Datetime; - -$messages = array( - array( - 'body' => (object) array( - 'play' => 'football' - ), - 'ttl' => 2 * Datetime::DAY - ), - array( - 'body' => (object) array( - 'play' => 'tennis' - ), - 'ttl' => 50 * Datetime::HOUR - ) -); - -$queue->createMessages($messages); -``` - -## 4. Get messages - -### 4.1 Description - -This operation gets the message or messages in the specified queue. - -Message IDs and markers are opaque strings. Clients should make no assumptions about their format or length. Furthermore, -clients should assume that there is no relationship between markers and message IDs (that is, one cannot be derived -from the other). This allows for a wide variety of storage driver implementations. - -Results are ordered by age, oldest message first. - -### 4.2 Parameters - -A hash of options. - -|Name|Style|Type|Description| -|----|----|----|----| -|marker|Query|String|Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional.| -|limit|Query|Integer|When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional.| -|echo|Query|Boolean|Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. -|include_claimed|Query|​Boolean|Determines whether the API returns claimed messages and unclaimed messages. The include_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include_claimed uses the default value of false (only unclaimed messages are returned). Optional. - -### 4.3 Code sample - -```php -$messages = $queue->listMessages(array( - 'marker' => '51db6f78c508f17ddc924357', - 'limit' => 20, - 'echo' => true -)); - -while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; -} -``` - -## 5. Get a set of messages by ID - -### 5.1 Description - -This operation provides a more efficient way to query multiple messages compared to using a series of individual GET. -Note that the list of IDs cannot exceed 20. If a malformed ID or a nonexistent message ID is provided, it is ignored, -and the remaining messages are returned. - -### 5.2 Parameters - -A hash of options. - -|Name|Style|Type|Description| -|----|-----|----|-----------| -|ids|Query|String|Specifies the IDs of the messages to get. Format multiple message ID values by separating them with -commas (comma-separated). Optional.| -|claim_id|Query|​String|Specifies the claim ID with which the message is associated. Optional.| -|----|-----|----|-----------| - -### 5.3 Code sample - -```php -$ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); - -$messages = $queue->listMessages(array('ids' => $ids)); - -while ($message = $messages->next()) { - echo $message->getId() . PHP_EOL; -} -``` - -## 6. Delete a set of messages by ID - -### 6.1 Description - -This operation immediately deletes the specified messages. If any of the message IDs are malformed or non-existent, -they are ignored. The remaining valid messages IDs are deleted. - -### 6.2 Parameters - -An array of IDs. - -### 6.3 Code sample - -```php -$ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); - -$response = $queue->deleteMessages($ids); -``` - -## 7. Get a specific message - -### 7.1 Description - -This operation gets the specified message from the specified queue. - -### 7.2 Parameters - -Message ID. - -### 7.3 Object properties - -`href` is an opaque relative URI that the client can use to uniquely identify a message resource and interact with it. - -`ttl` is the TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. - -`age` is the number of seconds relative to the server's clock. - -`body` is the arbitrary document that was submitted with the original request to post the message. - -### 7.4 Code sample - -```php -$message = $queue->getMessage('51db6f78c508f17ddc924357'); -``` - -## 8. Delete message - -### 8.1 Description - -This operation immediately deletes the specified message. - -### 8.2 Parameters - -None. - -### 8.3 Code sample - -```php -$message->delete(); -``` +https://doc.php-opencloud.com/en/latest/services/queues/messages.html diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md index 16290f445..01bb95e74 100644 --- a/docs/userguide/Queues/Queue.md +++ b/docs/userguide/Queues/Queue.md @@ -1,156 +1,5 @@ ## 1. Introduction -A Queue is an entity that holds messages. Ideally, a queue is created per work type. For example, if you want to -compress files, you would create a queue dedicated to this job. Any application that reads from this queue would only -compress files. +Our docs have moved! Please visit the below link: -## 2. Setup - -```php -$service = $client->queuesService('cloudQueues', 'ORD'); -``` - -## 3. Client IDs - -With most of Marconi's operation, you must specify a __Client ID__ which will be used as a unique identifier for the -process accessing this Queue. This is basically a UUID that must be unique to each client accessing the API - it can be -an arbitrary string. - -```php -$service->setClientId(); - -echo $service->getClientId(); -``` - -If you call `setClientId` without any parameters, a UUID is automatically generated for you. - -## 4. List queues - -### 4.1 Description - -This operation lists queues for the project. The queues are sorted alphabetically by name. - -### 4.2 Parameters - -|Name|Style|Type|Description| -|----|-----|----|-----------| -|marker|Query|​String|Specifies the name of the last queue received in a previous request, or none to get the first page - of results. Optional.| -|limit|Query|Integer|Specifies the number of queues to return. The default value for the number of queues returned is -10. If you do not specify this parameter, the default number of queues is returned. Optional.| -|detailed|Query|​Boolean|Determines whether queue metadata is included in the response. The default value for this - parameter is false, which excludes the metadata. Optional.| - |----|-----|----|-----------| - -### 4.3 Code sample - -```php -$queues = $service->listQueues(); - -while ($queue = $queues->next()) { - echo $queue->getName() . PHP_EOL; -} -``` - -## 5. Create queue - -### 5.1 Description - -This operation creates a new queue. - -### 5.2 Parameters - -A string representation of the name for your new Queue. The name must not exceed 64 bytes in length, and it is limited -to US-ASCII letters, digits, underscores, and hyphens. - -### 5.3 Code sample - -```php -$queue = $service->createQueue('new_queue'); -``` - -## 6. Retrieve queue - -### 6.1 Description - -Returns a `Queue` object for use. - -### 6.2 Parameters - -Queue name. - -### 6.3 Code sample - -```php -$queue = $service->getQueue('new_queue'); -``` - -## 7. Check queue existence - -### 7.1 Description - -This operation verifies whether the specified queue exists by returning `TRUE` or `FALSE`. - -### 7.2 Parameters - -### 7.3 Code sample - -```php -if ($service->hasQueue('new_queue')) { - // do something -} -``` - -## 8. Update queue metadata (permanently to the API) - -### 4.1 Description - -This operation replaces any existing metadata document in its entirety. Ensure that you do not accidentally overwrite -existing metadata that you want to retain. If you want to _append_ metadata, ensure you merge a new array to the -existing values. - -### 4.2 Parameters - -Hash of key pairs. - -### 4.3 Code sample - -```php -$queue->saveMetadata(array( - 'foo' => 'bar' -)); -``` - -## 9. Retrieve the queue metadata (fresh from the API) - -### 4.1 Description - -This operation returns metadata, such as message TTL, for the queue. - -### 4.2 Parameters - -None. - -### 4.3 Code sample - -```php -$metadata = $queue->retrieveMetadata(); - -print_r($metadata->toArray()); -``` - -## 10. Get queue stats - -### 4.1 Description - -This operation returns queue statistics, including how many messages are in the queue, categorized by status. - -### 4.2 Parameters - -None. - -### 4.3 Code sample - -```php -$queue->getStats(); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/queues/queues.html diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 934951e24..0be239d94 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -1,74 +1,5 @@ # Table of contents -## Auto Scale -- [Groups](/docs/userguide/Autoscale/Groups.md) -- [Policies](/docs/userguide/Autoscale/Policies.md) -- [Webhooks](/docs/userguide/Autoscale/Webhooks.md) +Our docs have moved! Please visit the below link: -## Cloud Monitoring -- [Agents](/docs/userguide/CloudMonitoring/Agents.md) -- [Alarms](/docs/userguide/CloudMonitoring/Alarms.md) -- [Changelogs](/docs/userguide/CloudMonitoring/Changelogs.md) -- [Checks](/docs/userguide/CloudMonitoring/Checks.md) -- [Entities](/docs/userguide/CloudMonitoring/Entities.md) -- [Metrics](/docs/userguide/CloudMonitoring/Metrics.md) -- [Notifications](/docs/userguide/CloudMonitoring/Notifications.md) -- [Views](/docs/userguide/CloudMonitoring/Views.md) -- [Zones](/docs/userguide/CloudMonitoring/Zones.md) - -## Compute -- [Servers](/docs/userguide/Compute/Server.md) -- [Images](/docs/userguide/Compute/Images.md) -- [Flavors](/docs/userguide/flavors.md) -- [Keypairs](/docs/userguide/Compute/Keypairs.md) - -## DNS -- [Records](/docs/userguide/DNS/Records.md) -- [Domains](/docs/userguide/DNS/Domains.md) -- [Reverse DNS](/docs/userguide/DNS/Reverse-DNS.md) - -## Databases -- [Getting Started](/docs/userguide/Database/README.md) - -## Identity -- [Users](/docs/userguide/Identity/Users.md) -- [Roles](/docs/userguide/Identity/Roles.md) -- [Tokens](/docs/userguide/Identity/Tokens.md) -- [Tenants](/docs/userguide/Identity/Tenants.md) - -## Images -- [Images](/docs/userguide/Image/Images.md) -- [Sharing](/docs/userguide/Image/Sharing.md) -- [Tagging](/docs/userguide/Image/Tags.md) - -## Load Balancers -- [Getting Started](/docs/userguide/LoadBalancer/README.md) -- [Complete userguide](/docs/userguide/LoadBalancer/USERGUIDE.md) - -## Networking -- [Getting Started](/docs/userguide/Networking/README.md) -- [Complete userguide](/docs/userguide/Networking/USERGUIDE.md) - -## Object Store -- [Objects](/docs/userguide/ObjectStore/Storage/Object.md) -- [Containers](/docs/userguide/ObjectStore/Storage/Container.md) -- [Migrating containers across regions](/docs/userguide/ObjectStore/Storage/Migrating.md) -- [Access control](/docs/userguide/ObjectStore/Access.md) -- [Accounts](/docs/userguide/ObjectStore/Account.md) -- [CDN-enabled containers](/docs/userguide/ObjectStore/CDN/Container.md) -- [Purging objects from the CDN](/docs/userguide/ObjectStore/CDN/Object.md) - -## Orchestration -- [Getting Started](/docs/userguide/Orchestration/README.md) -- [Complete userguide](/docs/userguide/Orchestration/USERGUIDE.md) - -## Queues -- [Queues](/docs/userguide/Queues/Queue.md) -- [Messages](/docs/userguide/Queues/Message.md) -- [Claims](/docs/userguide/Queues/Claim.md) - -## Miscelleneous -- [Client setup](/docs/userguide/Clients.md) -- [Debugging](/docs/userguide/Debugging.md) -- [Iterators](/docs/userguide/Iterators.md) -- [Caching credentials](/docs/userguide/caching-credentials.md) +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/Services.md b/docs/userguide/Services.md index 6f67222a2..9c9946e34 100644 --- a/docs/userguide/Services.md +++ b/docs/userguide/Services.md @@ -1,47 +1,5 @@ # Services -Both OpenStack and Rackspace are comprised of different services: Compute (Nova), -Object Storage (Swift), Queues (Marconi), etc. Each of these services is -encapsulated in its own class which implements the -`OpenCloud\Common\Service\ServiceInterface` interface. +Our docs have moved! Please visit the below link: -Each service class is nested in its own namespace: - -Service|OpenStack codename|FQCN ----|---|--- -Compute|Nova|`OpenCloud\Compute\Service` -Object Storage|Swift|`OpenCloud\ObjectStore\Service` -Identity|Keystone|`OpenCloud\Identity\Service` -Queues|Marconi|`OpenCloud\Queues\Service` - -## Instantiating a service - -The easiest way to instantiate a service, is using the convenient factory -methods of the client class. For more information about clients, see the -[client documentation](Clients.md). - -To create a Compute service, for example: - -```php -$service = $client->computeService('cloudServersOpenStack', 'DFW'); -``` - -### Method arguments - -As you will notice, each factory method accepts three arguments: - -Position|Name|Required?|Type|Description ----|---|---|---|--- -1|Service name|Yes|string|The name of the service as it appears in the [Service Catalog](https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/Clients.md#service-catalog) -2|Region|Yes|string|The region you want your service to operate in -3|URL type|No|enum|Each service has two different URL types for API transactions: a public URL (i.e. over the Internet) and a private URL (through Rackspace's private network). Private URL's are beneficial because they have lower latency and incur no bandwidth charges; the only condition is that you are communicating to the API from the **same geographic region** (i.e. from a Cloud Serve in the same region).

          The accepted options you may pass in are either `OpenCloud\Common\Constants\Service::INTERNAL_URL` or `OpenCloud\Common\Constants\Service::PUBLIC_URL`. - - -## Differences between OpenStack and Rackspace - -Not all of Rackspace's services are supported by OpenStack; examples include: -Cloud Databases, Auto Scale, Load Balancers, DNS, etc. - -For this reason, if you want to use or interact with these Rackspace-only -services, you must do so by using the methods of the `OpenCloud\Rackspace` -client object and **not** the `OpenCloud\OpenStack` client. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md index 60c89b606..5002ef8de 100644 --- a/docs/userguide/accessip.md +++ b/docs/userguide/accessip.md @@ -1,51 +1,6 @@ About the Access IP Addresses ============================= -OpenStack deployments generally provide new [servers](servers.md) with one -or two network interfaces, each with its own address(es). Usually, one of -these will be a public interface, with its addresses available on the Internet. +Our docs have moved! Please visit the below link: -However, in some cases, the servers are created on an internal network -(this is especially common in a hybrid solution where physical and virtual -servers are intermixed). The servers may be behind a NAT device, firewall, -or other network device that prohibits direct access to the server itself. - -The `OpenCloud\Compute\Resource\Server` object provides two attributes that are -used to instruct applications what IP address to use. These are called the -*access IP* addresses, and they are, in essence, documentation strings used to -direct applications to the correct network address. They can be changed at will -by the server's owner, and OpenStack Nova does not perform any validation on -them: - -* `accessIPv4` holds the IPv4 access address, and -* `accessIPv6` holds the IPv6 access address. - -### Updating the access IP address(es) - -For example, you may have a private cloud with internal addresses in the -10.1.x range. However, you can access a server via a firewall device at -address 50.57.94.244. In this case, you can change the `accessIPv4` attribute -to point to the firewall: - -```php -$server->update(array('accessIPv4' => '50.57.94.244')); -``` - -When a client application retrieves the server's information, it will know -that it needs to use the `accessIPv4` address to connect to the server, and -*not* the IP address assigned to one of the network interfaces. - -### Retrieving the server's IP address - -The `Server::ip()` method is used to retrieve the server's IP address. -It has one optional parameter: the format (either IPv4 or IPv6) of the address -to return (by default, it returns the IPv4 address): - -```php -// IPv4 -echo $server->ip(); -echo $server->ip(4); - -// IPv6 -echo $server->ip(6); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/caching-credentials.md b/docs/userguide/caching-credentials.md index d38d69937..365e6997e 100644 --- a/docs/userguide/caching-credentials.md +++ b/docs/userguide/caching-credentials.md @@ -1,39 +1,5 @@ # Caching credentials -You can speed up your API operations by caching your credentials in a (semi-)permanent location, such as your -DB or local filesystem. This enable subsequent requests to access a shared resource, instead of repetitively having to -re-authenticate on every thread of execution. +Our docs have moved! Please visit the below link: -Tokens are valid for 24 hours, so you can effectively re-use the same cached value for that period. If you try to use -a cached version that has expired, an authentication request will be made. - -## Filesystem example - -In this example, credentials will be saved to a file in the local filesystem. Be sure to exclude it from your VCS. - -```php -use OpenCloud\Rackspace; - -$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( - 'username' => 'foo', - 'apiKey' => 'bar' -)); - -$cacheFile = __DIR__ . '/.opencloud_token'; - -// If the cache file exists, try importing it into the client -if (file_exists($cacheFile)) { - $data = unserialize(file_get_contents($cacheFile)); - $client->importCredentials($data); -} - -$token = $client->getTokenObject(); - -// If no token exists, or the current token is expired, re-authenticate and save the new token to disk -if (!$token || ($token && $token->hasExpired())) { - $client->authenticate(); - file_put_contents($cacheFile, serialize($client->exportCredentials())); -} -``` - -In tests, the above code shaved about 1-2s off the execution time. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/caching-creds.html diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md index 8a8a05176..48d108014 100644 --- a/docs/userguide/dbaas.md +++ b/docs/userguide/dbaas.md @@ -1,319 +1,6 @@ Working with Cloud Databases ============================ -Cloud Databases is a "database-as-a-service" product offered by Rackspace. Since it is -not an official OpenStack project, it is only available via Rackspace connections, -and *not* through an OpenStack connection. +Our docs have moved! Please visit the below link: -## Setup - -```php -$service = $client->databaseService('cloudDatabases', 'ORD'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Instances - -A database instance is an isolated MySQL instance in a single tenant environment -on a shared physical host machine. Also referred to as instance. - -### Create a new Instance - -```php -// Create an empty OpenCloud\Database\Resource\Instance object -$instance = $service->instance(); - -// Send to the API -$instance->create(array( - // Pass in a name for your database instance - 'name' => '', - // Pass in a particular flavor object - 'flavor' => $service->flavor(''), - // Specify a 4GB volume - 'volume' => array('size' => 4) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/create-instance.php) ] - -### Retrieving an instance - -```php -$instance = $service->instance(''); -``` -[ [Get the executable PHP script for this example](/samples/Database/get-instance.php) ] - -### Updating an instance -An instance can be updated to use a specific [configuration](#configurations) as shown below. - -```php -$instance->update(array( - 'configuration' => '' -)); -``` - -**Note:** If any parameters in the associated configuration require a restart, then -you will need to [restart the instance](#restarting-an-instance) after the update. - -### Deleting an instance - -```php -$instance->delete(); -``` - -### Restarting an instance - -```php -$instance->restart(); -``` - -### Resizing an instance - -There are two methods for resizing an instance. The first, `resize()`, changes the amount -of RAM allocated to the instance: - -```php -$flavor = $service->flavor(''); -$instance->resize($flavor); -``` - -You can also independently change the volume size to increase the disk space: - -```php -// Increase to 8GB disk -$instance->resizeVolume(8); -``` - -## Databases - -Instances can have multiple databases; the `OpenCloud\Database\Resource\Database` -class corresponds to a single MySQL database. - -### Creating a new database - -To create a new database, you must supply it with a name; you can optionally -specify its character set and collating sequence: - -```php -// Create a new OpenCloud\Database\Resource\Database object -$database = $instance->database(); - -// Send to API -$database->create(array( - 'name' => 'production', - 'character_set' => 'utf8', - 'collate' => 'utf8_general_ci' -)); -``` - -You can find values for `character_set` and `collate` at -[the MySQL website](http://dev.mysql.com/doc/refman/5.0/en/charset-mysql.html). - -### Deleting a database - -```php -$database->delete(); -``` - -Note that this is destructive; all your data will be wiped away and will not be -retrievable. - -### Listing databases - -```php -$databases = $service->databaseList(); - -foreach ($databases as $database) { - /** @param $database OpenCloud\Database\Resource\Database */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Creating users - -Database users exist at the `Instance` level, but can be associated with a specific -`Database`. They are represented by the `OpenCloud\Database\Resource\User` class. - -Users cannot be altered after they are created, so they must be assigned to -databases when they are created: - -```php -// New instance of OpenCloud\Database\Resource\User -$user = $instance->user(); - -// Send to API -$user->create(array( - 'name' => 'Alice', - 'password' => 'fooBar' - 'databases' => array('production') -)); -``` - -If you need to add a new database to a user after it's been created, you must -delete the user and then re-add it. - -### Deleting users - -```php -$user->delete(); -``` - -## The root user - -By default, Cloud Databases does not enable the root user. In most cases, the root -user is not needed, and having one can leave you open to security violations. However, -if you want to enable access to the root user, the `enableRootUser()` method is -available: - -```php -$rootUser = $instance->enableRootUser(); -``` - -This returns a regular `User` object with the `name` attribute set to `root` and the -`password` attribute set to an auto-generated password. - -To check if the root user is enabled, use the `isRootEnabled()` method. - -## Configurations - -Configurations are groups of settings that can be [applied to instances](#updating-an-instance). - -### Creating a configuration -You can create a new configuration as shown below: - -```php -$configuration = $service->configuration(); -$configuration->create(array( - 'name' => 'example-configuration-name', - 'description' => 'An example configuration', - 'values' => array( - 'collation_server' => 'latin1_swedish_ci', - 'connect_timeout' => 120 - ), - 'datastore' => array( - 'type' => '10000000-0000-0000-0000-000000000001', - 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' - ) -)); -/** @var $configuration OpenCloud\Database\Resource\Configuration **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/create-configuration.php) ] - -### Listing configurations -You can list out all the configurations you have created as shown below: - -```php -$configurations = $service->configurationList(); -foreach ($configurations as $configuration) { - /** @var $configuration OpenCloud\Database\Resource\Configuration **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-configurations.php) ] - -### Retrieving a configuration -You can retrieve a specific configuration, using its ID, as shown below: - -```php -$configuration = $service->configuration(getenv('OS_DB_CONFIGURATION_ID')); -/** @var OpenCloud\Database\Resource\Configuration **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/get-configuration.php) ] - -### Updating a configuration -You have two choices when updating a configuration: -* You could [patch a configuration](#patching-a-configuration) to change only some configuration parameters, _or_ -* You could [entirely replace a configuration](#replacing-a-configuration) to replace all configuration parameters with new ones. - -#### Patching a configuration -You can patch a configuration as shown below: - -```php -$configuration->patch(array( - 'values' => array( - 'connect_timeout' => 30 - ) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/patch-configuration.php) ] - -#### Replacing a configuration -You can replace a configuration as shown below: - -```php -$configuration->update(array( - 'values' => array( - 'collation_server' => 'utf8_general_ci', - 'connect_timeout' => 60 - ) -)); -``` -[ [Get the executable PHP script for this example](/samples/Database/replace-configuration.php) ] - -### Deleting a configuration -You can delete a configuration as shown below: - -```php -$configuration->delete(); -``` -[ [Get the executable PHP script for this example](/samples/Database/delete-configuration.php) ] - -**Note:** You cannot delete a configuration if it is in use by a running instance. - -### Listing instances using a configuration -You can list all instances using a specific configuration, using its ID, as shown below: - -```php -$instances = $configuration->instanceList(); -foreach ($instances as $instance) { - /** @var $instance OpenCloud\Database\Resource\Instance **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-configuration-instances.php) ] - -## Datastores - -Datastores are technologies avaialable to persist data. - -### Listing datastores -You can list out all the datastores available as shown below: - -```php -$datastores = $service->datastoreList(); -foreach ($datastores as $datastore) { - /** @var $datastore OpenCloud\Database\Resource\Datastore **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-datastores.php) ] - -### Retrieving a datastore -You can retrieve a specific datastore's information, using its ID, as shown below: - -```php -$datastore = $service->datastore(''); -/** @var OpenCloud\Database\Resource\Datastore **/ -``` -[ [Get the executable PHP script for this example](/samples/Database/get-datastore.php) ] - -### Listing datastore versions -You can list out all the versions available for a specific datastore, as shown below: - -```php -$versions = $datastore->versionList(); -foreach ($versions as $version) { - /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ -} -``` -[ [Get the executable PHP script for this example](/samples/Database/list-datastore-versions.php) ] - -### Retrieving a datastore version -You a retrieve a specific datastore version, using its ID, as shown below: - -```php -$datastoreVersion = $datastore->version(''); -/** @var OpenCloud\Database\Resource\DatastoreVersion **/ -[ [Get the executable PHP script for this example](/samples/Database/get-datastore-version.php) ] +https://doc.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md index 41e3fa8d4..dd7806be5 100644 --- a/docs/userguide/flavors.md +++ b/docs/userguide/flavors.md @@ -1,60 +1,6 @@ Working with Flavors ==================== -A *flavor* is a named definition of certain server parameters such as the -amount of RAM and disk space available. (There are other parameters set via -the flavor, such as the amount of disk space and the number of virtual CPUs, -but a discussion of those is too in-depth for a simple Getting Started Guide -like this one.) +Our docs have moved! Please visit the below link: -## Get a flavor - -A `Flavor` object is generated from the `flavor()` method on an -`OpenCloud\Compute\Service` object: - -```php -$flavor = $service->flavor(''); -``` - -## List flavors - -```php -$flavors = $service->flavorList(); - -foreach ($flavors as $flavor) { - /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Details - -By default, the `flavorList()` method returns full details on all flavors. -However, because of the overhead involved in retrieving all the details, -this function can be slower than expected. You can supply an optional -boolean parameter to the `flavorList()` method to determine whether or not -the flavor details are included: - -```php -// Name and ID only -$compute->flavorList(false); - -// All details -$compute->flavorList(true); -``` - -### Filters - -The optional second parameter to the `flavorList()` method is an -associative array of filter parameters. See the -[List Flavors operation](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Flavors-d1e4188.html) - for a list of the available parameters. - -For example, you may be only interested in flavors that have at least 4GB of -memory: - -```php -$fourGbFlavors = $compute->FlavorList(true, array('minRam' => 4096)); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/compute/flavors.html diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md index f24752fc4..d42b73964 100644 --- a/docs/userguide/networks.md +++ b/docs/userguide/networks.md @@ -1,103 +1,6 @@ Working with Cloud Networks =========================== -Rackspace Cloud Networks is a virtual "isolated network" product -based upon (though not strictly identical to) the [OpenStack -Quantum](http://quantum.openstack.org) project. With Cloud Networks, -you can create multiple isolated networks and associate them with -new Cloud Servers. (You cannot add an isolated network to an -existing Cloud Server at this time, although that feature may be -available in the future.) +Our docs have moved! Please visit the below link: -Since the network is a feature of the Cloud Servers product, you -use the `Compute::network()` method to create a new network, and -the `Compute::networkList()` method to retrieve information about -existing networks. - -### Pseudo-networks - -Rackspace has two *pseudo-networks* called `public` and `private`. -The `public` network represents the Internet, while the `private` -network represents the Rackspace internal ServiceNet (an infrastructure -network used to facilitate communication within a Rackspace data -center). These are called "pseudo-networks" because they are not -actually represented in the Quantum component, but have a special -representation that you must use if you want to associate your -server with them. - -The `public` network is represented by the special UUID -`00000000-0000-0000-0000-000000000000` and the `private` network -by `11111111-1111-1111-1111-111111111111`. php-opencloud -provides the special constants `RAX_PUBLIC` and `RAX_PRIVATE` to -make using these networks easier (see [Create a server with an -isolated network](#server) below). - - -### Create a network - -A Cloud Network is identified by a *label* and must be defined with -a network *CIDR* address range. For example, we can create a network -used by our backend servers on the 192.168.0.0 network like this: - -```php -// Create instance of OpenCloud\Compute\Resource\Network -$network = $compute->network(); - -// Send to API -$network->create(array( - 'label' => 'Backend Network', - 'cidr' => '192.168.0.0/16' -)); -``` - -### Delete a network - -```php -$network->delete(); -``` - -Note that you cannot delete a network if there are still servers attached to it. - -### Listing networks - -``` -$networks = $service->networkList(); - -foreach ($networks as $network) { - /** @param $network OpenCloud\Compute\Resource\Network */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -## Create a server with an isolated network - -When you create a new server, you can specify the networks to which -it is attached via the `networks` attribute in the `Server::create()` -method: - -use OpenCloud\Compute\Constants\Network; - -// Create instance of OpenCloud\Compute\Resource\Server -$server = $compute->server(); - -// Send to API -$server->create(array( - 'name' => 'My Server', - 'flavor' => $compute->flavor(''), - 'image' => $compute->image(''), - 'networks' => array( - $network, - $compute->network(Network::RAX_PUBLIC) - ) -)); -``` - -In this example, the server `$server` is attached to the network that we -created in the previous example. It is also attached to the Rackspace `public` -network (the Internet). However, it is *not* attached to the Rackspace `private` -network (ServiceNet). - -Note that the `networks` attribute is an array of `OpenCloud\Compute\Resource\Network` -objects. \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md index 8f58adea0..127a5b6c5 100644 --- a/docs/userguide/servers.md +++ b/docs/userguide/servers.md @@ -1,188 +1,6 @@ Working with Servers ==================== -A server is a virtual machine instance that is managed by OpenStack Nova. It is -represented by the `OpenCloud\Compute\Resource\Server` class. - -## Setup - -In order to use a `Server` object, you must create the Compute service first: - -```php -$service = $client->computeService('cloudServersOpenStack', 'ORD'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Get an existing server - -To retrieve an existing server, all you need is its unique ID: - -```php -/** @param $server OpenCloud\Compute\Resource\Server */ -$server = $server->server(''); -``` - -## Create a new server - -A server requires both a [Flavor object](flavors.md) and an -[Image object](images.md) to be created. In addition, a server requires a name: - -```php -// New instance of OpenCloud\Compute\Resource\Server -$server = $service->server(); - -/** @param $server OpenCloud\Image\Resource\ImageInterface */ -$image = $service->image(''); - -/** @param $server OpenCloud\Compute\Resource\Flavor */ -$flavor = $service->flavor(''); - -// Send to API -$server->create(array( - 'name' => 'New server', - 'flavor' => $flavor, - 'image' => $image -)); -``` - -Server builds typically take under 5 minutes to complete (depending upon the size -of the server). However, the initial response will return the server's ID as -well as the assigned root password: - -```php -echo $server->adminPass; -``` - -(Note: it is not recommended that you print out the root password because of -security risks. This is only provided as an example.) - -When you create a new server on the Rackspace public cloud, you can also -associate it with one or more isolated networks. For more information, see -[Working with Cloud Networks](networks.md). - -### Rebuilding an existing server - -"Rebuilding" a server is nearly identical to creating one; you must supply -an Image object. You can also change the server's name as part of the rebuild. -The primary difference between a create and a rebuild is that, in the rebuild, -the server's IP address(es) are retained (when the server is created, new IP -addresses are assigned). - -To rebuild a server: - -```php -$server->rebuild(array( - 'adminPass' => 'rootPassword', - 'name' => 'A Bigger Server', - 'image' => $compute->image('') -)); -``` - -### Updating a server - -The `update()` method is very similar to `create()` except that the only -attributes of a server that you are permitted to update are its name and -the [access IP addresses](accessip.md). - -```php -$server->update(array('accessIPv4' => '50.57.94.244')); -``` - -### Deleting a server - -```php -$server->delete(); -``` - -## Server actions - -You can perform various actions on a server, such as rebooting it, resizing -it, or changing the root password. - -### Setting the root password - -Use the `setPassword()` method to change the root user's password: - -```php -$server->setPassword('new password'); -``` - -Note that it may take a few second for the new password to take effect. Also, -password restrictions (such as the minimum number of characters, numbers of -punctuation characters, and so forth) are enforced by the operating system and are -not always detectable by the Compute service. This means that, even though -the `setPassword()` method succeeds, the password may not be changed, and -there may not be any feedback to that effect. - -### To reboot the server - -You can perform either a *hard* reboot (this is like pulling the power cord -and then restarting) or a *soft* reboot (initiated by the operating system -and generally less disruptive than a hard reboot). A hard reboot is -performed by default: - -```php -$server->reboot(); // hard reboot -$server->reboot(ServerState::REBOOT_STATE_HARD); // also a hard reboot -$server->reboot(ServerState::REBOOT_STATE_SOFT); // a soft reboot -``` - -If the server is "hung," or unresponsive, a hard reboot may sometimes be -the only way to access the server. - -### To resize the server - -A server can be resized by providing a new [Flavor object](flavors.md): - -```php -$server->resize($compute->flavor(5)); -``` - -Once the resize completes (check the `$server->status`), you can either -confirm it: - -```php -$server->resizeConfirm(); -``` - -or revert it back to the original size: - -```php -$server->resizeRevert(); -``` - -### To rescue/unrescue a server - -In rescue mode, a server is rebuilt to a pristine state and the existing -filesystem is mounted so that you can edit files and diagnose issues. -See [this document](http://docs.rackspace.com/servers/api/v2/cs-devguide/content/rescue_mode.html) -for more details. - -Put server into rescue mode: - -```php -$password = $server->rescue(); -``` - -The `$password` is the assigned root password of the rescue server. - -Take server out of rescue mode: - -```php -$server->unrescue(); -``` - -This restores the server to its original state (plus any changes you may have -made while it was in rescue mode). - -## What next? - -* See also [Working with Networks](networks.md). -* To learn about dynamic - volume creation and assignment, see - [Working with Volumes](volumes.md). +Our docs have moved! Please visit the below link: +https://doc.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md index bd2f13a9d..8c891d4f2 100644 --- a/docs/userguide/volumes.md +++ b/docs/userguide/volumes.md @@ -1,173 +1,6 @@ Working with Volumes ---------------------------------------------- -Cloud Block Storage (CBS) is a dynamic volume creation and management service -built upon the OpenStack Cinder project. See http://cinder.openstack.org for -complete details about the available features and functionality. -## Setup +Our docs have moved! Please visit the below link: -```php -$service = $client->volumeService('cloudBlockStorage', 'DFW'); -``` - -For more information about setting up client objects, see the -[client documentation](Clients.md). For more information about service objects, -including a full list of expected arguments, see the -[service documentation](Services.md). - -## Volume Types - -Providers may support multiple types of volumes; at Rackspace, a volume can -either be `SSD` (solid state disk: expensive, high-performance) or -`SATA` (serial attached storage: regular disks, less expensive). - -### List volume types - -```php -$volumeTypes = $service->volumeTypeList(); - -foreach ($volumeTypes as $volumeType) { - /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Describe a volume type - -If you know the ID of a volume type, use the `volumeType` method to retrieve -information on it: - -```php -$volumeType = $service->volumeType(1); -``` - -A volume type has three attributes: - -* `$id` the volume type identifier -* `$name` its name -* `$extra_specs` additional information for the provider - -## Volumes - -A volume is a detachable block storage device. You can think of it as a USB -hard drive. It can only be attached to one instance at a time. - -A volume is represented by the `OpenCloud\Volume\Resource\Volume` class. - -### To create a volume - -To create a volume, you must specify its size (in gigabytes). All other -parameters are optional: - -```php -// Create instance of OpenCloud\Volume\Resource\Volume -$volume = $service->volume(); - -$volume->create(array( - 'size' => 200, - 'volume_type' => $service->volumeType(''), - 'display_name' => 'My Volume', - 'display_description' => 'Used for large object storage' -)); -``` - -### List volumes - -```php -$volumes = $service->volumeList(); - -foreach ($volumes as $volume) { - /** @param $volumeType OpenCloud\Volume\Resource\Volume */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### Get details on a single volume - -If you specify an ID on the `volume()` method, it retrieves information on -the specified volume: - -```php -$volume = $dallas->volume(''); -echo $volume->size; -``` - -### To delete a volume - -```php -$volume->delete(); -``` - -## Snapshots - -A snapshot is a point-in-time copy of the data contained in a volume. It is -represented by the `OpenCloud\Volume\Resource\Snapshot` class. - -### Create a snapshot - -A `Snapshot` object is created from the Cloud Block Storage service. However, -it is associated with a volume, and you must specify a volume to create one: - -```php -// New instance of OpenCloud\Volume\Resource\Snapshot -$snapshot = $service->snapshot(); - -// Send to API -$snapshot->create(array( - 'display_name' => 'Name that snapshot', - 'volume_id' => $volume->id -)); -``` - -### List snapshots - -```php -$snapshots = $service->snapshotList(); - -foreach ($snapshots as $snapshot) { - /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ -} -``` - -For more information about working with iterators, please see the -[iterators documentation](Iterators.md). - -### To get details on a single snapshot - -```php -$snapshot = $dallas->snapshot(''); -``` - -#### To delete a snapshot - -```php -$snapshot->delete(); -``` - -### Volumes and Servers - -A volume by itself is useful when it is attached to a server so that the -server can use the volume. - -### To attach a volume to a server - -```php -$server->attachVolume($volume, '') -``` - -The `` is the location on the server on which to -mount the volume (usually `/dev/xvhdd` or similar). You can also supply -`'auto'` as the mount point, in which case the mount point will be -automatically selected for you. `auto` is the default value for -`{mount-point}`, so you do not actually need to supply anything for that -parameter. - -### To detach a volume from a server - -```php -$server->detachVolume($volume); -``` \ No newline at end of file +https://doc.php-opencloud.com/en/latest/services/volume/index.html From 6655b4ad0c56059719fbe21d38dab02a50d4414f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 11 Mar 2015 19:35:26 +0100 Subject: [PATCH 760/835] Add more information about configuring clients --- doc/auth.rst | 54 ++++++++++++++++++++++++++++++++++++++++++ doc/http-clients.rst | 56 ++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 3 +++ doc/logging.rst | 33 ++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 doc/auth.rst create mode 100644 doc/http-clients.rst create mode 100644 doc/logging.rst diff --git a/doc/auth.rst b/doc/auth.rst new file mode 100644 index 000000000..ce5ba7233 --- /dev/null +++ b/doc/auth.rst @@ -0,0 +1,54 @@ +Authentication +============== + +The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary. + +You can force authentication, by calling: + +.. code-block:: php + + $client->authenticate(); + +If the credentials are incorrect, a ``401`` error will be returned. If +credentials are correct, a ``200`` status is returned with your Service +Catalog. + + +Service Catalog +--------------- + +The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the ``Catalog`` +object, which allows you greater control and interactivity. + +.. code-block:: php + + /** @var OpenCloud\Common\Service\Catalog */ + $catalog = $client->getCatalog(); + + // Return a list of OpenCloud\Common\Service\CatalogItem objects + foreach ($catalog->getItems() as $catalogItem) { + + $name = $catalogItem->getName(); + $type = $catalogItem->getType(); + + if ($name == 'cloudServersOpenStack' && $type == 'compute') { + break; + } + + // Array of OpenCloud\Common\Service\Endpoint objects + $endpoints = $catalogItem->getEndpoints(); + foreach ($endpoints as $endpoint) { + if ($endpoint->getRegion() == 'DFW') { + echo $endpoint->getPublicUrl(); + } + } + } + +As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs. diff --git a/doc/http-clients.rst b/doc/http-clients.rst new file mode 100644 index 000000000..49828509b --- /dev/null +++ b/doc/http-clients.rst @@ -0,0 +1,56 @@ +HTTP Clients +============ + +Default HTTP headers +-------------------- + +To set default HTTP headers: + +.. code-block:: php + + $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); + + +User agents +----------- + +php-opencloud will send a default ``User-Agent`` header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format: + + User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz + +where ``xxx`` is the current version of the SDK, ``yyy`` is the current +version of cURL, and ``zzz`` is the current PHP version. To override +this default, you must run: + +.. code-block:: php + + $client->setUserAgent('MyCustomUserAgent'); + +which will result in: + + User-Agent: MyCustomUserAgent + +If you want to set a *prefix* for the user agent, but retain the default +``User-Agent`` as a suffix, you must run: + +.. code-block:: php + + $client->setUserAgent('MyPrefix', true); + +which will result in: + + User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + +where ``$client`` is an instance of ``OpenCloud\OpenStack`` or +``OpenCloud\Rackspace``. + + +Other functionality +------------------- + +For a full list of functionality provided by Guzzle, please consult the +`official documentation`_. + +.. _official documentation: http://docs.guzzlephp.org/en/latest/http-client/client.html diff --git a/doc/index.rst b/doc/index.rst index fc7c280c3..4994f0efb 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -50,6 +50,9 @@ Usage tips iterators regions url-types + logging + http-clients + auth Help and support ---------------- diff --git a/doc/logging.rst b/doc/logging.rst new file mode 100644 index 000000000..bb2a3de09 --- /dev/null +++ b/doc/logging.rst @@ -0,0 +1,33 @@ +Logging +======= + +Logger injection +---------------- + +As the ``Rackspace`` client extends the ``OpenStack`` client, they both support +passing ``$options`` as an array via the constructor's third parameter. The +options are passed as a config to the `Guzzle` client, but also allow to inject +your own logger. + +Your logger should implement the ``Psr\Log\LoggerInterface`` `as defined in +PSR-3 `_. +One example of a compatible logger is `Monolog `_. +When the client does create a service, it will inject the logger if one is +available. + +To inject a ``LoggerInterface`` compatible logger into a new client: + +.. code-block:: php + + use Monolog\Logger; + use OpenCloud\OpenStack; + + // create a log channel + $logger = new Logger('name'); + + $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' + ), array( + 'logger' => $logger, + )); From 9b9b3aa9b715b01c2d788a22e43dd227f6e3305d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 16 Mar 2015 14:22:04 +0100 Subject: [PATCH 761/835] Use correct CNAME record --- docs/getting-started-openstack.md | 2 +- docs/getting-started.md | 2 +- docs/userguide/Autoscale/Config.md | 2 +- docs/userguide/Autoscale/Groups.md | 2 +- docs/userguide/Autoscale/Policies.md | 2 +- docs/userguide/Autoscale/Webhooks.md | 2 +- docs/userguide/Clients.md | 2 +- docs/userguide/CloudMonitoring/Agents.md | 2 +- docs/userguide/CloudMonitoring/Alarms.md | 2 +- docs/userguide/CloudMonitoring/Changelogs.md | 2 +- docs/userguide/CloudMonitoring/Checks.md | 2 +- docs/userguide/CloudMonitoring/Entities.md | 2 +- docs/userguide/CloudMonitoring/Metrics.md | 2 +- docs/userguide/CloudMonitoring/Notifications.md | 2 +- docs/userguide/CloudMonitoring/Service.md | 2 +- docs/userguide/CloudMonitoring/Views.md | 2 +- docs/userguide/CloudMonitoring/Zones.md | 2 +- docs/userguide/Compute/Images.md | 2 +- docs/userguide/Compute/Keypair.md | 2 +- docs/userguide/Compute/Server.md | 2 +- docs/userguide/Compute/Service.md | 2 +- docs/userguide/DNS/Domains.md | 2 +- docs/userguide/DNS/Limits.md | 2 +- docs/userguide/DNS/Records.md | 2 +- docs/userguide/DNS/Reverse-DNS.md | 2 +- docs/userguide/DNS/Service.md | 2 +- docs/userguide/Database/README.md | 2 +- docs/userguide/Debugging.md | 2 +- docs/userguide/Identity/Roles.md | 2 +- docs/userguide/Identity/Service.md | 2 +- docs/userguide/Identity/Tenants.md | 2 +- docs/userguide/Identity/Tokens.md | 2 +- docs/userguide/Identity/Users.md | 2 +- docs/userguide/Image/Images.md | 2 +- docs/userguide/Image/Schemas.md | 2 +- docs/userguide/Image/Sharing.md | 2 +- docs/userguide/Image/Tags.md | 2 +- docs/userguide/Iterators.md | 2 +- docs/userguide/LoadBalancer/README.md | 2 +- docs/userguide/LoadBalancer/USERGUIDE.md | 2 +- docs/userguide/Networking/README.md | 2 +- docs/userguide/Networking/USERGUIDE.md | 2 +- docs/userguide/ObjectStore/Access.md | 2 +- docs/userguide/ObjectStore/Account.md | 2 +- docs/userguide/ObjectStore/CDN/Container.md | 2 +- docs/userguide/ObjectStore/CDN/Object.md | 2 +- docs/userguide/ObjectStore/README.md | 2 +- docs/userguide/ObjectStore/Storage/Container.md | 2 +- docs/userguide/ObjectStore/Storage/Migrating.md | 2 +- docs/userguide/ObjectStore/Storage/Object.md | 2 +- docs/userguide/ObjectStore/USERGUIDE.md | 2 +- docs/userguide/Orchestration/README.md | 2 +- docs/userguide/Orchestration/USERGUIDE.md | 2 +- docs/userguide/Queues/Claim.md | 2 +- docs/userguide/Queues/Message.md | 2 +- docs/userguide/Queues/Queue.md | 2 +- docs/userguide/README.md | 2 +- docs/userguide/Services.md | 2 +- docs/userguide/accessip.md | 2 +- docs/userguide/caching-credentials.md | 2 +- docs/userguide/dbaas.md | 2 +- docs/userguide/flavors.md | 2 +- docs/userguide/networks.md | 2 +- docs/userguide/servers.md | 2 +- docs/userguide/volumes.md | 2 +- 65 files changed, 65 insertions(+), 65 deletions(-) diff --git a/docs/getting-started-openstack.md b/docs/getting-started-openstack.md index 24eda2779..1133b948d 100644 --- a/docs/getting-started-openstack.md +++ b/docs/getting-started-openstack.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/getting-started-with-openstack.html +http://docs.php-opencloud.com/en/latest/getting-started-with-openstack.html diff --git a/docs/getting-started.md b/docs/getting-started.md index af65271fe..c7d731a4c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/getting-started-with-rackspace.html +http://docs.php-opencloud.com/en/latest/getting-started-with-rackspace.html diff --git a/docs/userguide/Autoscale/Config.md b/docs/userguide/Autoscale/Config.md index 02f2e891d..328fdcc93 100644 --- a/docs/userguide/Autoscale/Config.md +++ b/docs/userguide/Autoscale/Config.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/group-config.html +http://docs.php-opencloud.com/en/latest/services/autoscale/group-config.html diff --git a/docs/userguide/Autoscale/Groups.md b/docs/userguide/Autoscale/Groups.md index 7ec7aa269..e0d8bfcdb 100644 --- a/docs/userguide/Autoscale/Groups.md +++ b/docs/userguide/Autoscale/Groups.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/groups.html +http://docs.php-opencloud.com/en/latest/services/autoscale/groups.html diff --git a/docs/userguide/Autoscale/Policies.md b/docs/userguide/Autoscale/Policies.md index 7a312af50..1f890d7ad 100644 --- a/docs/userguide/Autoscale/Policies.md +++ b/docs/userguide/Autoscale/Policies.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/policies.html +http://docs.php-opencloud.com/en/latest/services/autoscale/policies.html diff --git a/docs/userguide/Autoscale/Webhooks.md b/docs/userguide/Autoscale/Webhooks.md index c5e6453ed..783075508 100644 --- a/docs/userguide/Autoscale/Webhooks.md +++ b/docs/userguide/Autoscale/Webhooks.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/autoscale/webhooks.html +http://docs.php-opencloud.com/en/latest/services/autoscale/webhooks.html diff --git a/docs/userguide/Clients.md b/docs/userguide/Clients.md index 184b601f4..762a7ed70 100644 --- a/docs/userguide/Clients.md +++ b/docs/userguide/Clients.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/CloudMonitoring/Agents.md b/docs/userguide/CloudMonitoring/Agents.md index b316d004c..f1b8cab5d 100644 --- a/docs/userguide/CloudMonitoring/Agents.md +++ b/docs/userguide/CloudMonitoring/Agents.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/agents.html +http://docs.php-opencloud.com/en/latest/services/monitoring/agents.html diff --git a/docs/userguide/CloudMonitoring/Alarms.md b/docs/userguide/CloudMonitoring/Alarms.md index b08caf4cd..b64c1e948 100644 --- a/docs/userguide/CloudMonitoring/Alarms.md +++ b/docs/userguide/CloudMonitoring/Alarms.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/alarms.html +http://docs.php-opencloud.com/en/latest/services/monitoring/alarms.html diff --git a/docs/userguide/CloudMonitoring/Changelogs.md b/docs/userguide/CloudMonitoring/Changelogs.md index ff943e592..a1d137c9f 100644 --- a/docs/userguide/CloudMonitoring/Changelogs.md +++ b/docs/userguide/CloudMonitoring/Changelogs.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/changelogs.html +http://docs.php-opencloud.com/en/latest/services/monitoring/changelogs.html diff --git a/docs/userguide/CloudMonitoring/Checks.md b/docs/userguide/CloudMonitoring/Checks.md index 5710aa465..37aefb644 100644 --- a/docs/userguide/CloudMonitoring/Checks.md +++ b/docs/userguide/CloudMonitoring/Checks.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/checks.html +http://docs.php-opencloud.com/en/latest/services/monitoring/checks.html diff --git a/docs/userguide/CloudMonitoring/Entities.md b/docs/userguide/CloudMonitoring/Entities.md index beed807a2..0f09a74a2 100644 --- a/docs/userguide/CloudMonitoring/Entities.md +++ b/docs/userguide/CloudMonitoring/Entities.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/entities.html +http://docs.php-opencloud.com/en/latest/services/monitoring/entities.html diff --git a/docs/userguide/CloudMonitoring/Metrics.md b/docs/userguide/CloudMonitoring/Metrics.md index 0b275c97e..86450bee9 100644 --- a/docs/userguide/CloudMonitoring/Metrics.md +++ b/docs/userguide/CloudMonitoring/Metrics.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/metrics.html +http://docs.php-opencloud.com/en/latest/services/monitoring/metrics.html diff --git a/docs/userguide/CloudMonitoring/Notifications.md b/docs/userguide/CloudMonitoring/Notifications.md index 129881fae..30a1cf97c 100644 --- a/docs/userguide/CloudMonitoring/Notifications.md +++ b/docs/userguide/CloudMonitoring/Notifications.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/notifications.html +http://docs.php-opencloud.com/en/latest/services/monitoring/notifications.html diff --git a/docs/userguide/CloudMonitoring/Service.md b/docs/userguide/CloudMonitoring/Service.md index 6445de039..be9f9435f 100644 --- a/docs/userguide/CloudMonitoring/Service.md +++ b/docs/userguide/CloudMonitoring/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/index.html +http://docs.php-opencloud.com/en/latest/services/monitoring/index.html diff --git a/docs/userguide/CloudMonitoring/Views.md b/docs/userguide/CloudMonitoring/Views.md index ac0017db0..7672c2ea8 100644 --- a/docs/userguide/CloudMonitoring/Views.md +++ b/docs/userguide/CloudMonitoring/Views.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/views.html +http://docs.php-opencloud.com/en/latest/services/monitoring/views.html diff --git a/docs/userguide/CloudMonitoring/Zones.md b/docs/userguide/CloudMonitoring/Zones.md index fcad4b900..8257efa29 100644 --- a/docs/userguide/CloudMonitoring/Zones.md +++ b/docs/userguide/CloudMonitoring/Zones.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/monitoring/zones.html +http://docs.php-opencloud.com/en/latest/services/monitoring/zones.html diff --git a/docs/userguide/Compute/Images.md b/docs/userguide/Compute/Images.md index 6cc06d37f..b7a99025d 100644 --- a/docs/userguide/Compute/Images.md +++ b/docs/userguide/Compute/Images.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/images.html +http://docs.php-opencloud.com/en/latest/services/compute/images.html diff --git a/docs/userguide/Compute/Keypair.md b/docs/userguide/Compute/Keypair.md index 37e5080fe..a3e0b4870 100644 --- a/docs/userguide/Compute/Keypair.md +++ b/docs/userguide/Compute/Keypair.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/keypairs.html +http://docs.php-opencloud.com/en/latest/services/compute/keypairs.html diff --git a/docs/userguide/Compute/Server.md b/docs/userguide/Compute/Server.md index e43e1cc42..dedc1da77 100644 --- a/docs/userguide/Compute/Server.md +++ b/docs/userguide/Compute/Server.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/servers.html +http://docs.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/Compute/Service.md b/docs/userguide/Compute/Service.md index ba49ff4e4..9bb8e996a 100644 --- a/docs/userguide/Compute/Service.md +++ b/docs/userguide/Compute/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/index.html +http://docs.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/DNS/Domains.md b/docs/userguide/DNS/Domains.md index d8d3a12d3..53aa45c3b 100644 --- a/docs/userguide/DNS/Domains.md +++ b/docs/userguide/DNS/Domains.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/domains.html +http://docs.php-opencloud.com/en/latest/services/dns/domains.html diff --git a/docs/userguide/DNS/Limits.md b/docs/userguide/DNS/Limits.md index 70fac6504..68e928594 100644 --- a/docs/userguide/DNS/Limits.md +++ b/docs/userguide/DNS/Limits.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/limits.html +http://docs.php-opencloud.com/en/latest/services/dns/limits.html diff --git a/docs/userguide/DNS/Records.md b/docs/userguide/DNS/Records.md index 52998b955..fb428c60e 100644 --- a/docs/userguide/DNS/Records.md +++ b/docs/userguide/DNS/Records.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/records.html +http://docs.php-opencloud.com/en/latest/services/dns/records.html diff --git a/docs/userguide/DNS/Reverse-DNS.md b/docs/userguide/DNS/Reverse-DNS.md index e930431c1..84a3a2d15 100644 --- a/docs/userguide/DNS/Reverse-DNS.md +++ b/docs/userguide/DNS/Reverse-DNS.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/reverse-dns.html +http://docs.php-opencloud.com/en/latest/services/dns/reverse-dns.html diff --git a/docs/userguide/DNS/Service.md b/docs/userguide/DNS/Service.md index 3404ed7c7..993648cd9 100644 --- a/docs/userguide/DNS/Service.md +++ b/docs/userguide/DNS/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/dns/index.html +http://docs.php-opencloud.com/en/latest/services/dns/index.html diff --git a/docs/userguide/Database/README.md b/docs/userguide/Database/README.md index 199d049e4..2fc712efd 100644 --- a/docs/userguide/Database/README.md +++ b/docs/userguide/Database/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/database/index.html +http://docs.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/Debugging.md b/docs/userguide/Debugging.md index 0c6afa4d6..8922df10d 100644 --- a/docs/userguide/Debugging.md +++ b/docs/userguide/Debugging.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/debugging.html +http://docs.php-opencloud.com/en/latest/debugging.html diff --git a/docs/userguide/Identity/Roles.md b/docs/userguide/Identity/Roles.md index 333d53c81..5d608bf51 100644 --- a/docs/userguide/Identity/Roles.md +++ b/docs/userguide/Identity/Roles.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/roles.html +http://docs.php-opencloud.com/en/latest/services/identity/roles.html diff --git a/docs/userguide/Identity/Service.md b/docs/userguide/Identity/Service.md index 579cf2a9f..4195d4bcb 100644 --- a/docs/userguide/Identity/Service.md +++ b/docs/userguide/Identity/Service.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/index.html +http://docs.php-opencloud.com/en/latest/services/identity/index.html diff --git a/docs/userguide/Identity/Tenants.md b/docs/userguide/Identity/Tenants.md index ae9bcdcb0..2129f9604 100644 --- a/docs/userguide/Identity/Tenants.md +++ b/docs/userguide/Identity/Tenants.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/tenants.html +http://docs.php-opencloud.com/en/latest/services/identity/tenants.html diff --git a/docs/userguide/Identity/Tokens.md b/docs/userguide/Identity/Tokens.md index a60c57ab5..f59835ef4 100644 --- a/docs/userguide/Identity/Tokens.md +++ b/docs/userguide/Identity/Tokens.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/tokens.html +http://docs.php-opencloud.com/en/latest/services/identity/tokens.html diff --git a/docs/userguide/Identity/Users.md b/docs/userguide/Identity/Users.md index e387cbee5..a69fbb7dc 100644 --- a/docs/userguide/Identity/Users.md +++ b/docs/userguide/Identity/Users.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/identity/users.html +http://docs.php-opencloud.com/en/latest/services/identity/users.html diff --git a/docs/userguide/Image/Images.md b/docs/userguide/Image/Images.md index 1f593f17a..95824b2a5 100644 --- a/docs/userguide/Image/Images.md +++ b/docs/userguide/Image/Images.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/images.html +http://docs.php-opencloud.com/en/latest/services/image/images.html diff --git a/docs/userguide/Image/Schemas.md b/docs/userguide/Image/Schemas.md index b37222b84..5efce8c59 100644 --- a/docs/userguide/Image/Schemas.md +++ b/docs/userguide/Image/Schemas.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/schemas.html +http://docs.php-opencloud.com/en/latest/services/image/schemas.html diff --git a/docs/userguide/Image/Sharing.md b/docs/userguide/Image/Sharing.md index 91515b9fa..3fbe0c1d1 100644 --- a/docs/userguide/Image/Sharing.md +++ b/docs/userguide/Image/Sharing.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/sharing.html +http://docs.php-opencloud.com/en/latest/services/image/sharing.html diff --git a/docs/userguide/Image/Tags.md b/docs/userguide/Image/Tags.md index fc8bcd8aa..2686391f4 100644 --- a/docs/userguide/Image/Tags.md +++ b/docs/userguide/Image/Tags.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/image/tags.html +http://docs.php-opencloud.com/en/latest/services/image/tags.html diff --git a/docs/userguide/Iterators.md b/docs/userguide/Iterators.md index 114982668..4c8c794ce 100644 --- a/docs/userguide/Iterators.md +++ b/docs/userguide/Iterators.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/iterators.html +http://docs.php-opencloud.com/en/latest/iterators.html diff --git a/docs/userguide/LoadBalancer/README.md b/docs/userguide/LoadBalancer/README.md index 34008ab6c..3531e3dc8 100644 --- a/docs/userguide/LoadBalancer/README.md +++ b/docs/userguide/LoadBalancer/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html +http://docs.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/LoadBalancer/USERGUIDE.md b/docs/userguide/LoadBalancer/USERGUIDE.md index 0a5c3b15d..d29925da0 100644 --- a/docs/userguide/LoadBalancer/USERGUIDE.md +++ b/docs/userguide/LoadBalancer/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/load-balancer/index.html +http://docs.php-opencloud.com/en/latest/services/load-balancer/index.html diff --git a/docs/userguide/Networking/README.md b/docs/userguide/Networking/README.md index 1778646ad..9597c33a1 100644 --- a/docs/userguide/Networking/README.md +++ b/docs/userguide/Networking/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/Networking/USERGUIDE.md b/docs/userguide/Networking/USERGUIDE.md index 376daa093..d3185c6f2 100644 --- a/docs/userguide/Networking/USERGUIDE.md +++ b/docs/userguide/Networking/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/ObjectStore/Access.md b/docs/userguide/ObjectStore/Access.md index 8e224392a..53466362e 100644 --- a/docs/userguide/ObjectStore/Access.md +++ b/docs/userguide/ObjectStore/Access.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/access.html +http://docs.php-opencloud.com/en/latest/services/object-store/access.html diff --git a/docs/userguide/ObjectStore/Account.md b/docs/userguide/ObjectStore/Account.md index fc60751a8..286bb9ca1 100644 --- a/docs/userguide/ObjectStore/Account.md +++ b/docs/userguide/ObjectStore/Account.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/account.html +http://docs.php-opencloud.com/en/latest/services/object-store/account.html diff --git a/docs/userguide/ObjectStore/CDN/Container.md b/docs/userguide/ObjectStore/CDN/Container.md index 9f3b4a5ca..c854b31e5 100644 --- a/docs/userguide/ObjectStore/CDN/Container.md +++ b/docs/userguide/ObjectStore/CDN/Container.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html +http://docs.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/CDN/Object.md b/docs/userguide/ObjectStore/CDN/Object.md index 9f3b4a5ca..c854b31e5 100644 --- a/docs/userguide/ObjectStore/CDN/Object.md +++ b/docs/userguide/ObjectStore/CDN/Object.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/cdn.html +http://docs.php-opencloud.com/en/latest/services/object-store/cdn.html diff --git a/docs/userguide/ObjectStore/README.md b/docs/userguide/ObjectStore/README.md index ea43fcdf7..c66bbabdb 100644 --- a/docs/userguide/ObjectStore/README.md +++ b/docs/userguide/ObjectStore/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/index.html +http://docs.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/ObjectStore/Storage/Container.md b/docs/userguide/ObjectStore/Storage/Container.md index e9fd5af14..ab705fa15 100644 --- a/docs/userguide/ObjectStore/Storage/Container.md +++ b/docs/userguide/ObjectStore/Storage/Container.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/containers.html +http://docs.php-opencloud.com/en/latest/services/object-store/containers.html diff --git a/docs/userguide/ObjectStore/Storage/Migrating.md b/docs/userguide/ObjectStore/Storage/Migrating.md index 8d12c382a..550268f60 100644 --- a/docs/userguide/ObjectStore/Storage/Migrating.md +++ b/docs/userguide/ObjectStore/Storage/Migrating.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/migrating-containers.html +http://docs.php-opencloud.com/en/latest/services/object-store/migrating-containers.html diff --git a/docs/userguide/ObjectStore/Storage/Object.md b/docs/userguide/ObjectStore/Storage/Object.md index 0c58ca9fb..2ecca81e1 100644 --- a/docs/userguide/ObjectStore/Storage/Object.md +++ b/docs/userguide/ObjectStore/Storage/Object.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/objects.html +http://docs.php-opencloud.com/en/latest/services/object-store/objects.html diff --git a/docs/userguide/ObjectStore/USERGUIDE.md b/docs/userguide/ObjectStore/USERGUIDE.md index 6881ee3e6..3773b0078 100644 --- a/docs/userguide/ObjectStore/USERGUIDE.md +++ b/docs/userguide/ObjectStore/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/object-store/index.html +http://docs.php-opencloud.com/en/latest/services/object-store/index.html diff --git a/docs/userguide/Orchestration/README.md b/docs/userguide/Orchestration/README.md index 862a61ed7..3647da0c9 100644 --- a/docs/userguide/Orchestration/README.md +++ b/docs/userguide/Orchestration/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/orchestration/index.html +http://docs.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Orchestration/USERGUIDE.md b/docs/userguide/Orchestration/USERGUIDE.md index 902d7741b..4a5b9a135 100644 --- a/docs/userguide/Orchestration/USERGUIDE.md +++ b/docs/userguide/Orchestration/USERGUIDE.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/orchestration/index.html +http://docs.php-opencloud.com/en/latest/services/orchestration/index.html diff --git a/docs/userguide/Queues/Claim.md b/docs/userguide/Queues/Claim.md index 0e338956c..58b57d22f 100644 --- a/docs/userguide/Queues/Claim.md +++ b/docs/userguide/Queues/Claim.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/claims.html +http://docs.php-opencloud.com/en/latest/services/queues/claims.html diff --git a/docs/userguide/Queues/Message.md b/docs/userguide/Queues/Message.md index ce0f54b07..767bb0b1d 100644 --- a/docs/userguide/Queues/Message.md +++ b/docs/userguide/Queues/Message.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/messages.html +http://docs.php-opencloud.com/en/latest/services/queues/messages.html diff --git a/docs/userguide/Queues/Queue.md b/docs/userguide/Queues/Queue.md index 01bb95e74..2299a72e2 100644 --- a/docs/userguide/Queues/Queue.md +++ b/docs/userguide/Queues/Queue.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/queues/queues.html +http://docs.php-opencloud.com/en/latest/services/queues/queues.html diff --git a/docs/userguide/README.md b/docs/userguide/README.md index 0be239d94..b49b4e67c 100644 --- a/docs/userguide/README.md +++ b/docs/userguide/README.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/Services.md b/docs/userguide/Services.md index 9c9946e34..6973130eb 100644 --- a/docs/userguide/Services.md +++ b/docs/userguide/Services.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/index.html +http://docs.php-opencloud.com/en/latest/index.html diff --git a/docs/userguide/accessip.md b/docs/userguide/accessip.md index 5002ef8de..9d343de11 100644 --- a/docs/userguide/accessip.md +++ b/docs/userguide/accessip.md @@ -3,4 +3,4 @@ About the Access IP Addresses Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/servers.html +http://docs.php-opencloud.com/en/latest/services/compute/servers.html diff --git a/docs/userguide/caching-credentials.md b/docs/userguide/caching-credentials.md index 365e6997e..949dcad53 100644 --- a/docs/userguide/caching-credentials.md +++ b/docs/userguide/caching-credentials.md @@ -2,4 +2,4 @@ Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/caching-creds.html +http://docs.php-opencloud.com/en/latest/caching-creds.html diff --git a/docs/userguide/dbaas.md b/docs/userguide/dbaas.md index 48d108014..fe0327861 100644 --- a/docs/userguide/dbaas.md +++ b/docs/userguide/dbaas.md @@ -3,4 +3,4 @@ Working with Cloud Databases Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/database/index.html +http://docs.php-opencloud.com/en/latest/services/database/index.html diff --git a/docs/userguide/flavors.md b/docs/userguide/flavors.md index dd7806be5..4bf978b52 100644 --- a/docs/userguide/flavors.md +++ b/docs/userguide/flavors.md @@ -3,4 +3,4 @@ Working with Flavors Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/flavors.html +http://docs.php-opencloud.com/en/latest/services/compute/flavors.html diff --git a/docs/userguide/networks.md b/docs/userguide/networks.md index d42b73964..f6a153508 100644 --- a/docs/userguide/networks.md +++ b/docs/userguide/networks.md @@ -3,4 +3,4 @@ Working with Cloud Networks Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/networking/index.html +http://docs.php-opencloud.com/en/latest/services/networking/index.html diff --git a/docs/userguide/servers.md b/docs/userguide/servers.md index 127a5b6c5..1ff28fd30 100644 --- a/docs/userguide/servers.md +++ b/docs/userguide/servers.md @@ -3,4 +3,4 @@ Working with Servers Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/compute/index.html +http://docs.php-opencloud.com/en/latest/services/compute/index.html diff --git a/docs/userguide/volumes.md b/docs/userguide/volumes.md index 8c891d4f2..bc161e5b3 100644 --- a/docs/userguide/volumes.md +++ b/docs/userguide/volumes.md @@ -3,4 +3,4 @@ Working with Volumes Our docs have moved! Please visit the below link: -https://doc.php-opencloud.com/en/latest/services/volume/index.html +http://docs.php-opencloud.com/en/latest/services/volume/index.html From 9cf735dd7764ad907991079f86c7e557c863a3df Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:21:50 -0700 Subject: [PATCH 762/835] Using TLSv1 cipher suite for Cloud Databases client. --- lib/OpenCloud/Database/Service.php | 22 +++++++++++++++++++ .../OpenCloud/Tests/Database/ServiceTest.php | 7 ++++++ 2 files changed, 29 insertions(+) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index dd63a60f1..50845ba24 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -17,6 +17,8 @@ namespace OpenCloud\Database; +use Guzzle\Http\ClientInterface; +use Psr\Log\LogLevel; use OpenCloud\Common\Service\NovaService; use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; @@ -104,4 +106,24 @@ public function datastoreList($params = array()) return $this->resourceList('Datastore', $url); } + + /** + * {@inheritDoc} + */ + public function setClient(ClientInterface $client) + { + // The Rackspace Cloud Databases service only supports the + // RC4 SSL cipher which is not supported by modern OpenSSL clients. + // Until the service can support additional, more modern and secure + // ciphers, this SDK has to ask curl to allow using the weaker + // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 + + $curlOptions = $client->getConfig()->get('curl.options'); + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; + $client->getConfig()->set('curl.options', $curlOptions); + + $client->getLogger()->critical('The SDK is using the TLSv1 cipher suite when connecting to the Rackspace Cloud Databases service. This suite contains a weak cipher (RC4) so please use at your own risk. See https://github.com/rackspace/php-opencloud/issues/560 for details.'); + + $this->client = $client; + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 8212fb61a..4c690ae81 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -71,4 +71,11 @@ public function testDatastoreList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } + + public function testClientUsesTLSv1CipherSuite() + { + $client = $this->service->getClient(); + $curlOptions = $client->getConfig('curl.options'); + $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + } } From ffd884be7bfefc521ec6b5103a9fd77c967d2abb Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:40:04 -0700 Subject: [PATCH 763/835] Check that critical message is being logged + suppress log output. --- .../Tests/Database/DatabaseTestCase.php | 25 ++++++++++++++++++- .../OpenCloud/Tests/Database/ServiceTest.php | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index 0e8ad75cb..c0a0b38e0 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -18,6 +18,22 @@ namespace OpenCloud\Tests\Database; use OpenCloud\Tests\OpenCloudTestCase; +use OpenCloud\Common\Log\Logger; + +class UnitTestLogger extends Logger +{ + protected $criticalLogMessage; + + public function critical($message, array $context = array()) + { + ++$this->criticalLogMessage; + } + + public function getCriticalLogMessage() + { + return $this->criticalLogMessage; + } +} class DatabaseTestCase extends OpenCloudTestCase { @@ -28,7 +44,9 @@ class DatabaseTestCase extends OpenCloudTestCase public function setupObjects() { - $this->service = $this->getClient()->databaseService(); + $client = $this->getClient(); + $client->setLogger(new UnitTestLogger()); + $this->service = $client->databaseService(); $this->addMockSubscriber($this->getTestFilePath('Instance')); $this->instance = $this->service->instance('foo'); @@ -37,4 +55,9 @@ public function setupObjects() $this->datastore = $this->service->datastore('10000000-0000-0000-0000-000000000001'); $this->datastoreVersion = $this->datastore->version('b00000b0-00b0-0b00-00b0-000b000000bb'); } + + protected function assertCriticalMessageWasLogged() + { + $this->assertNotEmpty($this->getClient()->getLogger()->getCriticalLogMessage()); + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 4c690ae81..3dc5a11f1 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -77,5 +77,6 @@ public function testClientUsesTLSv1CipherSuite() $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertCriticalMessageWasLogged(); } } From b212e21f04eea6c6e55c961024c628bf77c60fd9 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:49:39 -0700 Subject: [PATCH 764/835] Removing unnecessary import. --- lib/OpenCloud/Database/Service.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 50845ba24..2e9d56093 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -18,7 +18,6 @@ namespace OpenCloud\Database; use Guzzle\Http\ClientInterface; -use Psr\Log\LogLevel; use OpenCloud\Common\Service\NovaService; use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; From 925d5a77fa89471706694523030b7e1ecb2b3158 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:49:46 -0700 Subject: [PATCH 765/835] Breaking up log message over several lines. --- lib/OpenCloud/Database/Service.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 2e9d56093..00498ed28 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -121,7 +121,11 @@ public function setClient(ClientInterface $client) $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; $client->getConfig()->set('curl.options', $curlOptions); - $client->getLogger()->critical('The SDK is using the TLSv1 cipher suite when connecting to the Rackspace Cloud Databases service. This suite contains a weak cipher (RC4) so please use at your own risk. See https://github.com/rackspace/php-opencloud/issues/560 for details.'); + $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' + . 'to the Rackspace Cloud Databases service. This suite contains ' + . 'a weak cipher (RC4) so please use at your own risk. See ' + . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; + $client->getLogger()->critical($logMessage); $this->client = $client; } From 8d2d1ac3678fa8f6fc950e7ea14af13077cd1b5d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 08:54:37 -0700 Subject: [PATCH 766/835] Fixing option value - should be cipher name not integer (constant). --- lib/OpenCloud/Database/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 00498ed28..b73ee2f7c 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -118,7 +118,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = CURL_SSLVERSION_TLSv1; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'TLSv1'; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' From 75a492808c8277b1d61b865e68bcc3e6c612ad70 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 09:50:56 -0700 Subject: [PATCH 767/835] Use custom cipher suite stronger than TLSv1 (but still including RC4 for Cloud Databases). --- lib/OpenCloud/Database/Service.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index b73ee2f7c..065191095 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -118,7 +118,10 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'TLSv1'; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' From 528b7852fb410445f37fdcf6832a36a84f5d6dac Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:08:35 -0700 Subject: [PATCH 768/835] Refactoring SSL cipher list into constant; fixing test. --- lib/OpenCloud/Database/Service.php | 10 ++++++---- tests/OpenCloud/Tests/Database/ServiceTest.php | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 065191095..0828d47f1 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -31,6 +31,11 @@ class Service extends NovaService const DEFAULT_TYPE = 'rax:database'; const DEFAULT_NAME = 'cloudDatabases'; + const SSL_CIPHER_LIST = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + /** * Returns an Instance * @@ -118,10 +123,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = self::SSL_CIPHER_LIST; $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 3dc5a11f1..28636a363 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -27,6 +27,8 @@ namespace OpenCloud\Tests\Database; +use OpenCloud\Database\Service; + class ServiceTest extends DatabaseTestCase { public function test__construct() @@ -76,7 +78,7 @@ public function testClientUsesTLSv1CipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(CURL_SSLVERSION_TLSv1, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertEquals(Service::SSL_CIPHER_LIST, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); $this->assertCriticalMessageWasLogged(); } } From d559ffa12022966fd75025ca837c4b8df4c476b2 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:16:20 -0700 Subject: [PATCH 769/835] PHP < 5.6 does not like multi-line consts :| --- lib/OpenCloud/Database/Service.php | 15 +++++++++------ tests/OpenCloud/Tests/Database/ServiceTest.php | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 0828d47f1..1098e6add 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -31,11 +31,6 @@ class Service extends NovaService const DEFAULT_TYPE = 'rax:database'; const DEFAULT_NAME = 'cloudDatabases'; - const SSL_CIPHER_LIST = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; - /** * Returns an Instance * @@ -123,7 +118,7 @@ public function setClient(ClientInterface $client) // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = self::SSL_CIPHER_LIST; + $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); $client->getConfig()->set('curl.options', $curlOptions); $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' @@ -134,4 +129,12 @@ public function setClient(ClientInterface $client) $this->client = $client; } + + public static function getSslCipherList() + { + return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' + . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' + . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' + . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 28636a363..a3f5f4401 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -78,7 +78,7 @@ public function testClientUsesTLSv1CipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(Service::SSL_CIPHER_LIST, $curlOptions['CURLOPT_SSL_CIPHER_LIST']); + $this->assertEquals(Service::getSslCipherList(), $curlOptions['CURLOPT_SSL_CIPHER_LIST']); $this->assertCriticalMessageWasLogged(); } } From befe7ea516cc869d802bca870414ac6110e96e6d Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:18:45 -0700 Subject: [PATCH 770/835] Fixing log message to match reality. --- lib/OpenCloud/Database/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 1098e6add..58c5ba260 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -121,7 +121,7 @@ public function setClient(ClientInterface $client) $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); $client->getConfig()->set('curl.options', $curlOptions); - $logMessage = 'The SDK is using the TLSv1 cipher suite when connecting ' + $logMessage = 'The SDK is using a custom cipher suite when connecting ' . 'to the Rackspace Cloud Databases service. This suite contains ' . 'a weak cipher (RC4) so please use at your own risk. See ' . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; From 20e05f60afa80d727ec2d997a1fe26f00962ec3a Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:20:01 -0700 Subject: [PATCH 771/835] Fixing unit test method name to match reality. --- tests/OpenCloud/Tests/Database/ServiceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index a3f5f4401..96a6be1c6 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -74,7 +74,7 @@ public function testDatastoreList() $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } - public function testClientUsesTLSv1CipherSuite() + public function testClientUsesCustomCipherSuite() { $client = $this->service->getClient(); $curlOptions = $client->getConfig('curl.options'); From 806002d66094a6312adf436d6473c8c83c41f963 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:29:50 -0700 Subject: [PATCH 772/835] Refactoring logger into common class to enable reuse. --- .../Tests/Database/DatabaseTestCase.php | 19 ++-------- tests/OpenCloud/Tests/MockLogger.php | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 tests/OpenCloud/Tests/MockLogger.php diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index c0a0b38e0..403f6955f 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -18,22 +18,7 @@ namespace OpenCloud\Tests\Database; use OpenCloud\Tests\OpenCloudTestCase; -use OpenCloud\Common\Log\Logger; - -class UnitTestLogger extends Logger -{ - protected $criticalLogMessage; - - public function critical($message, array $context = array()) - { - ++$this->criticalLogMessage; - } - - public function getCriticalLogMessage() - { - return $this->criticalLogMessage; - } -} +use OpenCloud\Tests\MockLogger; class DatabaseTestCase extends OpenCloudTestCase { @@ -45,7 +30,7 @@ class DatabaseTestCase extends OpenCloudTestCase public function setupObjects() { $client = $this->getClient(); - $client->setLogger(new UnitTestLogger()); + $client->setLogger(new MockLogger()); $this->service = $client->databaseService(); $this->addMockSubscriber($this->getTestFilePath('Instance')); diff --git a/tests/OpenCloud/Tests/MockLogger.php b/tests/OpenCloud/Tests/MockLogger.php new file mode 100644 index 000000000..9aca3dd68 --- /dev/null +++ b/tests/OpenCloud/Tests/MockLogger.php @@ -0,0 +1,35 @@ +criticalLogMessage; + } + + public function getCriticalLogMessage() + { + return $this->criticalLogMessage; + } +} From 819e94a63ffc03f3b8bddb0176cb0f78300fb884 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 10:30:04 -0700 Subject: [PATCH 773/835] Use mock logger for database service test to supress log output. --- tests/OpenCloud/Tests/RackspaceTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/OpenCloud/Tests/RackspaceTest.php b/tests/OpenCloud/Tests/RackspaceTest.php index 790d54645..7cf37f183 100644 --- a/tests/OpenCloud/Tests/RackspaceTest.php +++ b/tests/OpenCloud/Tests/RackspaceTest.php @@ -17,6 +17,8 @@ namespace OpenCloud\Tests; +use OpenCloud\Tests\MockLogger; + class RackspaceTest extends OpenCloudTestCase { const CREDENTIALS = <<getClient()->getLogger(); + $this->getClient()->setLogger(new MockLogger()); + $this->assertInstanceOf( 'OpenCloud\Database\Service', $this->getClient()->databaseService('cloudDatabases', 'DFW') ); + + // Re-inject old logger + $this->getClient()->setLogger($oldLogger); + $this->assertInstanceOf( 'OpenCloud\LoadBalancer\Service', $this->getClient()->loadBalancerService('cloudLoadBalancers', 'DFW') From 078f2d180ce2530d9cb514fe0ec0e5f276391908 Mon Sep 17 00:00:00 2001 From: Shaunak Kashyap Date: Mon, 16 Mar 2015 12:06:59 -0700 Subject: [PATCH 774/835] Adding reference to cipher list comment in docblock. --- lib/OpenCloud/Database/Service.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 58c5ba260..de20872b6 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -130,6 +130,9 @@ public function setClient(ClientInterface $client) $this->client = $client; } + /** + * @see https://github.com/rackspace/php-opencloud/issues/560#issuecomment-81790778 + */ public static function getSslCipherList() { return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' From 5b5766af47abff68f1278fdc0827ef16d8cee61b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 20 Mar 2015 15:00:34 +0100 Subject: [PATCH 775/835] [ci skip] Add more links to our official docs --- README.md | 2 +- docs/README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/README.md diff --git a/README.md b/README.md index 6031cf4ac..8ddd9f1b5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ PHP SDK for OpenStack/Rackspace APIs [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) -For SDKs in different languages, see http://developer.rackspace.com. +Our official documentation is now available on http://docs.php-opencloud.com. For SDKs in different languages, see http://developer.rackspace.com. The PHP SDK should work with most OpenStack-based cloud deployments, though it specifically targets the Rackspace public cloud. In diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..154fd158d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Documentation + +Our official docs are hosted on http://docs.php-opencloud.com. From c3297ae26906bb82d53e6769fbf84046191d5bb6 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 20 Mar 2015 17:42:30 +0100 Subject: [PATCH 776/835] [ci skip] Fix incorrect method in docs --- doc/services/object-store/objects.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 0eb0ea836..7d9b2120e 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -204,8 +204,8 @@ Get file name .. code-block:: php - /** @param $container OpenCloud\ObjectStore\Resource\Container */ - $container = $object->getContainer(); + /** @param $name string */ + $name = $object->getName(); Get file size From 20af47912171e4238b7e26e26cee12782856cd72 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 29 Apr 2015 11:25:03 +0200 Subject: [PATCH 777/835] Bump apigen version --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index fe720466b..059a07e5d 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ } }, "require": { - "php" : ">=5.3.3", + "php" : ">=5.4", "guzzle/http" : "~3.8", "psr/log": "~1.0", "mikemccabe/json-patch-php": "~0.1" @@ -36,6 +36,6 @@ "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", - "apigen/apigen": "~2.8" + "apigen/apigen": "~4.0" } } From 9f0dbcb035d5179085ededc7dd99ce3a27f72b0b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 29 Apr 2015 12:32:26 +0200 Subject: [PATCH 778/835] Fix to JSON syntax --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cebfc7280..059a07e5d 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "guzzle/http" : "~3.8", "psr/log": "~1.0", "mikemccabe/json-patch-php": "~0.1" - , + }, "require-dev" : { "phpunit/phpunit": "4.3.*", "satooshi/php-coveralls": "0.6.*@dev", From 393b22052a153306d6615c729cb2ee5e9cd4f8bb Mon Sep 17 00:00:00 2001 From: Evan Lucas Date: Mon, 4 May 2015 15:54:56 -0500 Subject: [PATCH 779/835] Fix Error message typo s/his/This --- lib/OpenCloud/Common/Resource/PersistentResource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Resource/PersistentResource.php b/lib/OpenCloud/Common/Resource/PersistentResource.php index bb11b79be..33aa1c398 100644 --- a/lib/OpenCloud/Common/Resource/PersistentResource.php +++ b/lib/OpenCloud/Common/Resource/PersistentResource.php @@ -319,7 +319,7 @@ protected function noDelete() */ protected function noUpdate() { - throw new UpdateError('his resource does not support the update operation'); + throw new UpdateError('This resource does not support the update operation'); } /** From c683d832e63fd61cebbefc2d7d7862894e5a421b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 5 May 2015 11:15:50 +0200 Subject: [PATCH 780/835] Do not assume all data entries are objects --- lib/OpenCloud/ObjectStore/CDNService.php | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/CDNService.php b/lib/OpenCloud/ObjectStore/CDNService.php index 0092941e0..6a13a8ee0 100644 --- a/lib/OpenCloud/ObjectStore/CDNService.php +++ b/lib/OpenCloud/ObjectStore/CDNService.php @@ -54,18 +54,19 @@ public function cdnContainer($data) { $container = new CDNContainer($this, $data); - $metadata = new ContainerMetadata(); - $metadata->setArray(array( - 'Streaming-Uri' => $data->cdn_streaming_uri, - 'Ios-Uri' => $data->cdn_ios_uri, - 'Ssl-Uri' => $data->cdn_ssl_uri, - 'Enabled' => $data->cdn_enabled, - 'Ttl' => $data->ttl, - 'Log-Retention' => $data->log_retention, - 'Uri' => $data->cdn_uri, - )); - - $container->setMetadata($metadata); + if (is_object($data)) { + $metadata = new ContainerMetadata(); + $metadata->setArray(array( + 'Streaming-Uri' => $data->cdn_streaming_uri, + 'Ios-Uri' => $data->cdn_ios_uri, + 'Ssl-Uri' => $data->cdn_ssl_uri, + 'Enabled' => $data->cdn_enabled, + 'Ttl' => $data->ttl, + 'Log-Retention' => $data->log_retention, + 'Uri' => $data->cdn_uri, + )); + $container->setMetadata($metadata); + } return $container; } From 7cdca1357e1e8aec90ffd034c32478569456d21f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 5 May 2015 11:15:59 +0200 Subject: [PATCH 781/835] Add clarity about CDN containers --- doc/services/object-store/cdn.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/services/object-store/cdn.rst b/doc/services/object-store/cdn.rst index eb29bb27f..b4eebc1b6 100644 --- a/doc/services/object-store/cdn.rst +++ b/doc/services/object-store/cdn.rst @@ -71,6 +71,12 @@ Once a container has been CDN-enabled, you can retrieve it like so: $cdnContainer = $cdnService->cdnContainer('{containerName}'); +If you already have a container object and want to avoid instantiating a new service, you can also do: + +.. code-block:: php + + $cdnContainer = $container->getCdn(); + Retrieve the SSL URL of a CDN container ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From a488c836df047ae68a017abb98b20a22927d82e9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 7 May 2015 12:07:08 +0200 Subject: [PATCH 782/835] Provide the option to override inherited URL types for temp URLs --- .../ObjectStore/Resource/DataObject.php | 12 +++++--- .../ObjectStore/Resource/DataObjectTest.php | 30 +++++++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index de302b4cd..c4841c89f 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -451,14 +451,18 @@ public function createSymlinkFrom($source) * * @link http://docs.rackspace.com/files/api/v1/cf-devguide/content/TempURL-d1a4450.html * - * @param $expires Expiration time in seconds - * @param $method What method can use this URL? (`GET' or `PUT') + * @param int $expires Expiration time in seconds + * @param string $method What method can use this URL? (`GET' or `PUT') + * @param bool $forcePublicUrl If set to TRUE, a public URL will always be used. The default is to use whatever + * URL type the user has set for the main service. + * * @return string + * * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError * @throws \OpenCloud\Common\Exceptions\ObjectError * */ - public function getTemporaryUrl($expires, $method) + public function getTemporaryUrl($expires, $method, $forcePublicUrl = false) { $method = strtoupper($method); $expiry = time() + (int) $expires; @@ -477,7 +481,7 @@ public function getTemporaryUrl($expires, $method) } // @codeCoverageIgnoreEnd - $url = $this->getUrl(); + $url = ($forcePublicUrl === true) ? $this->getService()->getEndpoint()->getPublicUrl() : $this->getUrl(); $urlPath = urldecode($url->getPath()); $body = sprintf("%s\n%d\n%s", $method, $expiry, $urlPath); $hash = hash_hmac('sha1', $body, $secret); diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 4bebaf346..3dab21171 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -72,7 +72,7 @@ public function test_Copy() } /** - * @expectedException OpenCloud\Common\Exceptions\NoNameError + * @expectedException \OpenCloud\Common\Exceptions\NoNameError */ public function test_Copy_Fails() { @@ -80,13 +80,39 @@ public function test_Copy_Fails() } /** - * @expectedException OpenCloud\Common\Exceptions\InvalidArgumentError + * @expectedException \OpenCloud\Common\Exceptions\InvalidArgumentError */ public function test_Temp_Url_Fails_With_Incorrect_Method() { $this->container->dataObject('foobar')->getTemporaryUrl(1000, 'DELETE'); } + public function test_Temp_Url_Inherits_Url_Type() + { + $service = $this->getClient()->objectStoreService(null, 'IAD', 'internalURL'); + $object = $service->getContainer('foo')->dataObject('bar'); + + $this->addMockSubscriber(new Response(204, ['X-Account-Meta-Temp-URL-Key' => 'secret'])); + + $tempUrl = $object->getTemporaryUrl(60, 'GET'); + + // Check that internal URLs are used + $this->assertContains('snet-storage', $tempUrl); + } + + public function test_temp_urls_can_be_forced_to_use_public_urls() + { + $service = $this->getClient()->objectStoreService(null, 'IAD', 'internalURL'); + $object = $service->getContainer('foo')->dataObject('bar'); + + $this->addMockSubscriber(new Response(204, ['X-Account-Meta-Temp-URL-Key' => 'secret'])); + + $tempUrl = $object->getTemporaryUrl(60, 'GET', true); + + // Check that internal URLs are NOT used + $this->assertNotContains('snet-storage', $tempUrl); + } + public function test_Purge() { $object = $this->container->dataObject('foobar'); From 8c31cfc55789a801da35e93dec8bbbdb5959cf58 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 7 May 2015 13:41:37 +0200 Subject: [PATCH 783/835] Add all Guzzle deps --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 059a07e5d..9a6d30576 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ }, "require": { "php" : ">=5.4", - "guzzle/http" : "~3.8", + "guzzle/guzzle" : "~3.8", "psr/log": "~1.0", "mikemccabe/json-patch-php": "~0.1" }, From 736e80138a746c04b23ac73a4beed1357c1428e8 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 13 May 2015 12:00:47 +0200 Subject: [PATCH 784/835] Remove custom cipher list for DB service --- lib/OpenCloud/Database/Service.php | 35 ------------------- .../OpenCloud/Tests/Database/ServiceTest.php | 8 ----- 2 files changed, 43 deletions(-) diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index de20872b6..947e0a664 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -105,39 +105,4 @@ public function datastoreList($params = array()) return $this->resourceList('Datastore', $url); } - - /** - * {@inheritDoc} - */ - public function setClient(ClientInterface $client) - { - // The Rackspace Cloud Databases service only supports the - // RC4 SSL cipher which is not supported by modern OpenSSL clients. - // Until the service can support additional, more modern and secure - // ciphers, this SDK has to ask curl to allow using the weaker - // cipher. For more information, see https://github.com/rackspace/php-opencloud/issues/560 - - $curlOptions = $client->getConfig()->get('curl.options'); - $curlOptions['CURLOPT_SSL_CIPHER_LIST'] = static::getSslCipherList(); - $client->getConfig()->set('curl.options', $curlOptions); - - $logMessage = 'The SDK is using a custom cipher suite when connecting ' - . 'to the Rackspace Cloud Databases service. This suite contains ' - . 'a weak cipher (RC4) so please use at your own risk. See ' - . 'https://github.com/rackspace/php-opencloud/issues/560 for details.'; - $client->getLogger()->critical($logMessage); - - $this->client = $client; - } - - /** - * @see https://github.com/rackspace/php-opencloud/issues/560#issuecomment-81790778 - */ - public static function getSslCipherList() - { - return 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' - . 'ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:' - . 'DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:' - . 'ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5'; - } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 96a6be1c6..837824435 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -73,12 +73,4 @@ public function testDatastoreList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } - - public function testClientUsesCustomCipherSuite() - { - $client = $this->service->getClient(); - $curlOptions = $client->getConfig('curl.options'); - $this->assertEquals(Service::getSslCipherList(), $curlOptions['CURLOPT_SSL_CIPHER_LIST']); - $this->assertCriticalMessageWasLogged(); - } } From 8ad29bccb040341a48bb1f26a4feadfee63d8041 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 13 May 2015 12:01:05 +0200 Subject: [PATCH 785/835] Bump version --- lib/OpenCloud/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Version.php b/lib/OpenCloud/Version.php index f71d254fa..2072ec8ec 100644 --- a/lib/OpenCloud/Version.php +++ b/lib/OpenCloud/Version.php @@ -27,7 +27,7 @@ */ class Version { - const VERSION = '1.14.0'; + const VERSION = '1.14.2'; /** * @return string Indicate current SDK version. From fdce9ccfa02e707faa4d0ca504a658429734d490 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Fri, 15 May 2015 11:50:11 +0200 Subject: [PATCH 786/835] Fix samples for create server from bootable volume --- samples/Compute/create_server_from_bootable_volume.php | 3 ++- ..._server_from_bootable_volume_delete_on_termination.php | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/samples/Compute/create_server_from_bootable_volume.php b/samples/Compute/create_server_from_bootable_volume.php index 141a94f12..3be223a90 100644 --- a/samples/Compute/create_server_from_bootable_volume.php +++ b/samples/Compute/create_server_from_bootable_volume.php @@ -47,7 +47,8 @@ $response = $server->create(array( 'name' => '{serverName}', 'imageId' => '{imageId}', - 'flavorId' => '{flavorId}' + 'flavorId' => '{flavorId}', + 'volume' => $bootableVolume )); } catch (BadResponseException $e) { echo $e->getResponse(); diff --git a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php index 20457105b..3b6425a6a 100644 --- a/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php +++ b/samples/Compute/create_server_from_bootable_volume_delete_on_termination.php @@ -47,9 +47,11 @@ // list_images.php scripts. try { $response = $server->create(array( - 'name' => '{serverName}', - 'imageId' => '{imageId}', - 'flavorId' => '{flavorId}' + 'name' => '{serverName}', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'volume' => $bootableVolume, + 'volumeDeleteOnTermination' => true )); } catch (BadResponseException $e) { echo $e->getResponse(); From 6ee1f095472a147ebe6220ba5ba512709372aec0 Mon Sep 17 00:00:00 2001 From: Tim Gunter Date: Tue, 19 May 2015 19:28:11 -0400 Subject: [PATCH 787/835] Allow port update() to modify security groups. --- lib/OpenCloud/Networking/Resource/Port.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Networking/Resource/Port.php b/lib/OpenCloud/Networking/Resource/Port.php index bc7ee4dd0..ee58bc0e9 100644 --- a/lib/OpenCloud/Networking/Resource/Port.php +++ b/lib/OpenCloud/Networking/Resource/Port.php @@ -78,7 +78,8 @@ class Port extends PersistentResource protected $updateKeys = array( 'name', - 'deviceId' + 'deviceId', + 'securityGroups' ); /** From 7821d555377084f6c2f7a8f10638366b19d5541d Mon Sep 17 00:00:00 2001 From: Tim Gunter Date: Tue, 19 May 2015 19:52:54 -0400 Subject: [PATCH 788/835] Add slightly more verbose documentation to listPorts() to clarify filtering ability. --- doc/services/networking/ports.rst | 15 +++++++++ samples/Networking/list-ports-filtered.php | 39 ++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 samples/Networking/list-ports-filtered.php diff --git a/doc/services/networking/ports.rst b/doc/services/networking/ports.rst index 8eeb07bf0..3e2cf482e 100644 --- a/doc/services/networking/ports.rst +++ b/doc/services/networking/ports.rst @@ -86,6 +86,21 @@ You can list all the ports to which you have access as shown in the following ex `Get the executable PHP script for this example `_ +The port list query may be filtered by numerous optional parameters as per the `API documentation `_ + +.. code-block:: php + + $ports = $networkingService->listPorts([ + 'status' => 'ACTIVE', + 'device_id' => '9ae135f4-b6e0-4dad-9e91-3c223e385824' + ]); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + Get a port ---------- diff --git a/samples/Networking/list-ports-filtered.php b/samples/Networking/list-ports-filtered.php new file mode 100644 index 000000000..d676c82a3 --- /dev/null +++ b/samples/Networking/list-ports-filtered.php @@ -0,0 +1,39 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain a Networking service object from the client. +$networkingService = $client->networkingService(null, '{region}'); + +// 3. List ACTIVE ports for given device ID +$ports = $networkingService->listPorts([ + 'status' => 'ACTIVE', + 'device_id' => '{deviceId}' +]); +foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ +} From ba5c9bb484ab46cea1096b349f0ae0e3cfd3fdd1 Mon Sep 17 00:00:00 2001 From: Floran Brutel Date: Mon, 25 May 2015 16:43:11 +0200 Subject: [PATCH 789/835] Fix "create_server_with_keypair" sample --- samples/Compute/create_server_with_keypair.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/Compute/create_server_with_keypair.php b/samples/Compute/create_server_with_keypair.php index 1fb932692..45a655664 100644 --- a/samples/Compute/create_server_with_keypair.php +++ b/samples/Compute/create_server_with_keypair.php @@ -40,7 +40,8 @@ $response = $server->create(array( 'name' => '{serverName}', 'imageId' => '{imageId}', - 'flavorId' => '{flavorId}' + 'flavorId' => '{flavorId}', + 'keypair' => '{keypairName}' )); } catch (BadResponseException $e) { echo $e->getResponse(); From 6742812d58646c4bde8bd50fe979a9a435972a6d Mon Sep 17 00:00:00 2001 From: Evan Lucas Date: Tue, 5 May 2015 17:12:51 -0500 Subject: [PATCH 790/835] Add ability to interact with backups. --- .../Common/Exceptions/BackupInstanceError.php | 22 +++++ .../Common/Exceptions/BackupNameError.php | 22 +++++ lib/OpenCloud/Database/Resource/Backup.php | 92 +++++++++++++++++++ lib/OpenCloud/Database/Resource/Instance.php | 60 +++++++++++- lib/OpenCloud/Database/Service.php | 26 ++++++ .../Tests/Database/DatabaseTestCase.php | 1 + .../Tests/Database/Resource/BackupTest.php | 66 +++++++++++++ .../Tests/Database/Resource/InstanceTest.php | 18 +++- .../OpenCloud/Tests/Database/ServiceTest.php | 10 ++ 9 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 lib/OpenCloud/Common/Exceptions/BackupInstanceError.php create mode 100644 lib/OpenCloud/Common/Exceptions/BackupNameError.php create mode 100644 lib/OpenCloud/Database/Resource/Backup.php create mode 100644 tests/OpenCloud/Tests/Database/Resource/BackupTest.php diff --git a/lib/OpenCloud/Common/Exceptions/BackupInstanceError.php b/lib/OpenCloud/Common/Exceptions/BackupInstanceError.php new file mode 100644 index 000000000..612d1fa2e --- /dev/null +++ b/lib/OpenCloud/Common/Exceptions/BackupInstanceError.php @@ -0,0 +1,22 @@ + 'Instance' + ); + + protected $aliases = array( + 'instance_id' => 'instanceId' + ); + + public function __construct(Service $service, $info = null) + { + $this->instance = new \stdClass; + return parent::__construct($service, $info); + } + + /** + * Returns the JSON object for creating the backup + */ + protected function createJson() + { + if (!isset($this->instanceId)) { + throw new Exceptions\BackupInstanceError( + Lang::translate('The `instanceId` attribute is required and must be a string') + ); + } + + if (!isset($this->name)) { + throw new Exceptions\BackupNameError( + Lang::translate('Backup name is required') + ); + } + + $out = [ + 'backup' => [ + 'name' => $this->name, + 'instance' => $this->instanceId + ] + ]; + + if (isset($this->description)) { + $out['backup']['description'] = $this->description; + } + return (object) $out; + } +} diff --git a/lib/OpenCloud/Database/Resource/Instance.php b/lib/OpenCloud/Database/Resource/Instance.php index 83262ddd6..e7f09fddf 100644 --- a/lib/OpenCloud/Database/Resource/Instance.php +++ b/lib/OpenCloud/Database/Resource/Instance.php @@ -39,6 +39,7 @@ class Instance extends NovaResource public $created; public $updated; public $flavor; + public $backupRef; protected static $json_name = 'instance'; protected static $url_resource = 'instances'; @@ -171,6 +172,49 @@ public function userList() return $this->getService()->resourceList('User', $this->getUrl('users'), $this); } + /** + * Returns a Collection of all backups for the instance + * + * @return OpenCloud\Common\Collection\PaginatedIterator + */ + public function backupList() + { + return $this->getService()->resourceList('Backup', $this->getUrl('backups'), $this); + } + + /** + * Creates a backup for the given instance + * + * @api + * @param array $params - an associate array of key/value pairs + * name is required + * description is optional + * @return Backup + */ + public function createBackup($params = array()) + { + if (!isset($params['instanceId'])) { + $params['instanceId'] = $this->id; + } + + $backup = new Backup($this->getService(), $params); + $backup->create($params); + return $backup; + } + + public function populate($info, $setObjects = true) + { + parent::populate($info, $setObjects); + + if (is_object($info)) { + $info = (array) $info; + } + + if (isset($info['restorePoint']['backupRef'])) { + $this->backupRef = $info['restorePoint']['backupRef']; + } + } + /** * Generates the JSON string for Create() * @@ -190,13 +234,21 @@ protected function createJson() ); } - return (object) array( - 'instance' => (object) array( + $out = [ + 'instance' => [ 'flavorRef' => $this->flavor->links[0]->href, 'name' => $this->name, 'volume' => $this->volume - ) - ); + ] + ]; + + if (isset($this->backupRef)) { + $out['instance']['restorePoint'] = [ + 'backupRef' => $this->backupRef + ]; + } + + return (object) $out; } /** diff --git a/lib/OpenCloud/Database/Service.php b/lib/OpenCloud/Database/Service.php index 947e0a664..31504d208 100644 --- a/lib/OpenCloud/Database/Service.php +++ b/lib/OpenCloud/Database/Service.php @@ -22,6 +22,7 @@ use OpenCloud\Database\Resource\Instance; use OpenCloud\Database\Resource\Configuration; use OpenCloud\Database\Resource\Datastore; +use OpenCloud\Database\Resource\Backup; /** * The Rackspace Database service @@ -105,4 +106,29 @@ public function datastoreList($params = array()) return $this->resourceList('Datastore', $url); } + + /** + * Returns a Backup + * + * @param string $id the ID of the backup to retrieve + * @return \OpenCloud\Database\Resource\Backup + */ + public function backup($id = null) + { + return $this->resource('Backup', $id); + } + + /** + * Returns a Collection of Backup objects + * + * @param array $params + * @return \OpenCloud\Common\Collection\PaginatedIterator + */ + public function backupList($params = array()) + { + $url = clone $this->getUrl(); + $url->addPath(Backup::resourceName())->setQuery($params); + + return $this->resourceList('Backup', $url); + } } diff --git a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php index 403f6955f..c9cd9417b 100644 --- a/tests/OpenCloud/Tests/Database/DatabaseTestCase.php +++ b/tests/OpenCloud/Tests/Database/DatabaseTestCase.php @@ -39,6 +39,7 @@ public function setupObjects() $this->configuration = $this->service->configuration('005a8bb7-a8df-40ee-b0b7-fc144641abc2'); $this->datastore = $this->service->datastore('10000000-0000-0000-0000-000000000001'); $this->datastoreVersion = $this->datastore->version('b00000b0-00b0-0b00-00b0-000b000000bb'); + $this->backup = $this->service->backup(); } protected function assertCriticalMessageWasLogged() diff --git a/tests/OpenCloud/Tests/Database/Resource/BackupTest.php b/tests/OpenCloud/Tests/Database/Resource/BackupTest.php new file mode 100644 index 000000000..bcd5e3c57 --- /dev/null +++ b/tests/OpenCloud/Tests/Database/Resource/BackupTest.php @@ -0,0 +1,66 @@ +assertInstanceOf('OpenCloud\Database\Resource\Backup', $this->backup); + } + + public function testDelete() + { + $this->backup->delete(); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\BackupInstanceError + */ + public function test_Create_Fails_Without_InstanceId() + { + $this->assertFalse($this->backup->create(array( + 'name' => 'test', + 'description' => 'test desc' + ))); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\BackupNameError + */ + public function test_Create_Fails_Without_Name() + { + $this->assertFalse($this->backup->create(array( + 'description' => 'test description', + 'instanceId' => '1234' + ))); + } + + public function testCreate() + { + $this->backup->create(array( + 'name' => 'test backup', + 'instanceId' => '1234' + )); + + $this->assertEquals('test backup', $this->backup->name); + $this->assertEquals('1234', $this->backup->instanceId); + } +} diff --git a/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php b/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php index 53b0498fb..99805a5be 100644 --- a/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php +++ b/tests/OpenCloud/Tests/Database/Resource/InstanceTest.php @@ -31,10 +31,10 @@ public function testUpdateJson() $replacementValues = array( 'configuration' => '005a8bb7-a8df-40ee-b0b7-fc144641abc2' ); - + $method = new \ReflectionMethod('OpenCloud\Database\Resource\Instance', 'updateJson'); $method->setAccessible(true); - + $expected = (object) array( 'instance' => $replacementValues ); @@ -91,4 +91,18 @@ public function testUserList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->instance->userList()); } + + public function testBackupList() + { + $this->assertInstanceOf(self::COLLECTION_CLASS, $this->instance->backupList()); + } + + public function testCreateBackup() + { + $backup = $this->instance->createBackup(array( + 'name' => 'test-backup', + 'description' => 'test' + )); + $this->assertInstanceOf('OpenCloud\Database\Resource\Backup', $backup); + } } diff --git a/tests/OpenCloud/Tests/Database/ServiceTest.php b/tests/OpenCloud/Tests/Database/ServiceTest.php index 837824435..ee1989538 100644 --- a/tests/OpenCloud/Tests/Database/ServiceTest.php +++ b/tests/OpenCloud/Tests/Database/ServiceTest.php @@ -73,4 +73,14 @@ public function testDatastoreList() { $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->datastoreList()); } + + public function testBackup() + { + $this->assertInstanceOf('OpenCloud\Database\Resource\Backup', $this->service->Backup()); + } + + public function testBackupList() + { + $this->assertInstanceOf(self::COLLECTION_CLASS, $this->service->backupList()); + } } From dd794abd73115de1b2ed8e95f30c2fdbbdb448a9 Mon Sep 17 00:00:00 2001 From: Joe Bottigliero Date: Tue, 26 May 2015 20:20:41 -0500 Subject: [PATCH 791/835] DataObject->(get|set)ContentLength Docblocks Updates content length getter and setter docblocks to better reflect what can be returned due to various population methods. --- lib/OpenCloud/ObjectStore/Resource/DataObject.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index c4841c89f..942338b80 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -265,7 +265,7 @@ public function getContentType() } /** - * @param $contentType int + * @param $contentType mixed * @return $this */ public function setContentLength($contentLength) @@ -276,7 +276,7 @@ public function setContentLength($contentLength) } /** - * @return int + * @return mixed */ public function getContentLength() { From f250bad24c6da46baa56e3e51292c0b400b500d5 Mon Sep 17 00:00:00 2001 From: Joe Bottigliero Date: Tue, 26 May 2015 20:26:39 -0500 Subject: [PATCH 792/835] documentation - parameter name fix --- lib/OpenCloud/ObjectStore/Resource/DataObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index 942338b80..731d1c370 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -265,7 +265,7 @@ public function getContentType() } /** - * @param $contentType mixed + * @param $contentLength mixed * @return $this */ public function setContentLength($contentLength) From 88195eef1952bfe57cc31fb7dc7a86a08e2c9a30 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 16 Jun 2015 10:38:00 +0200 Subject: [PATCH 793/835] Add prophecy as dev dependency for better mocking --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 9a6d30576..ba7ef6ff2 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ }, "require-dev" : { "phpunit/phpunit": "4.3.*", + "phpspec/prophecy": "~1.4", "satooshi/php-coveralls": "0.6.*@dev", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", From 8b502733f649da861de4c726ced521d7d273f0dd Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 16 Jun 2015 10:38:44 +0200 Subject: [PATCH 794/835] Add fixtures --- tests/OpenCloud/Tests/ObjectStore/Upload/fixtures/test1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/OpenCloud/Tests/ObjectStore/Upload/fixtures/test1 diff --git a/tests/OpenCloud/Tests/ObjectStore/Upload/fixtures/test1 b/tests/OpenCloud/Tests/ObjectStore/Upload/fixtures/test1 new file mode 100644 index 000000000..e69de29bb From ec67a6110801ac63fd629a3c507d1e0b592fab5a Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 16 Jun 2015 10:39:00 +0200 Subject: [PATCH 795/835] Add ability to test uploads into pseudo-directories --- .../ObjectStore/Resource/Container.php | 7 +- .../ObjectStore/Upload/DirectorySync.php | 38 +++++---- .../ObjectStore/Upload/DirectorySyncTest.php | 78 +++++++++++++++++++ 3 files changed, 107 insertions(+), 16 deletions(-) create mode 100644 tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 57b8ce34a..ed47a4d35 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -570,11 +570,12 @@ public function setupObjectTransfer(array $options = array()) /** * Upload the contents of a local directory to a remote container, effectively syncing them. * - * @param $path The local path to the directory. + * @param string $path The local path to the directory. + * @param string $targetDir The path (or pseudo-directory) that all files will be nested in. */ - public function uploadDirectory($path) + public function uploadDirectory($path, $targetDir = null) { - $sync = DirectorySync::factory($path, $this); + $sync = DirectorySync::factory($path, $this, $targetDir); $sync->execute(); } diff --git a/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php b/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php index b95475bf3..fc30b9968 100644 --- a/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php +++ b/lib/OpenCloud/ObjectStore/Upload/DirectorySync.php @@ -31,32 +31,34 @@ */ class DirectorySync { - /** - * @var string The path to the directory you're syncing. - */ + /** @var string The path to the directory you're syncing. */ private $basePath; - /** - * @var ResourceIterator A collection of remote files in Swift. - */ + + /** @var ResourceIterator A collection of remote files in Swift. */ private $remoteFiles; - /** - * @var AbstractContainer The Container object you are syncing. - */ + + /** @var AbstractContainer The Container object you are syncing. */ private $container; + /** @var string */ + private $targetDir; + /** * Basic factory method to instantiate a new DirectorySync object with all the appropriate properties. * - * @param $path The local path + * @param string $path The local path * @param Container $container The container you're syncing + * @param string $targetDir The path (or pseudo-directory) that the files will be nested in + * * @return DirectorySync */ - public static function factory($path, Container $container) + public static function factory($path, Container $container, $targetDir = null) { $transfer = new self(); $transfer->setBasePath($path); $transfer->setContainer($container); $transfer->setRemoteFiles($container->objectList()); + $transfer->setTargetDir($targetDir); return $transfer; } @@ -90,6 +92,14 @@ public function setContainer(Container $container) $this->container = $container; } + /** + * @param string $dir The target path that all files will be nested in. By default, the files will not be nested. + */ + public function setTargetDir($dir) + { + $this->targetDir = rtrim($dir, '/'); + } + /** * Execute the sync process. This will collect all the remote files from the API and do a comparison. There are * four scenarios that need to be dealt with: @@ -112,7 +122,9 @@ public function execute() // Handle PUT requests (create/update files) foreach ($localFiles as $filename) { - $callback = $this->getCallback($filename); + $remoteFilename = $this->targetDir ? $this->targetDir . '/' . $filename : $filename; + + $callback = $this->getCallback($remoteFilename); $filePath = rtrim($this->basePath, '/') . '/' . $filename; if (!is_readable($filePath)) { @@ -133,7 +145,7 @@ public function execute() } else { // upload new file $url = clone $this->container->getUrl(); - $url->addPath($filename); + $url->addPath($remoteFilename); $requests[] = $this->container->getClient()->put($url, array(), $entityBody); } diff --git a/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php b/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php new file mode 100644 index 000000000..23948e15a --- /dev/null +++ b/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php @@ -0,0 +1,78 @@ +prophet = new Prophet(); + } + + public function test_it_uploads_to_a_container() + { + $baseUrl = 'foo.com/bar'; + + $containerMock = $this->prophet->prophesize('OpenCloud\ObjectStore\Resource\Container'); + $iteratorMock = $this->prophet->prophesize('OpenCloud\Common\Collection\PaginatedIterator'); + + $iteratorMock->rewind()->shouldBeCalled(); + $iteratorMock->populateAll()->shouldBeCalled(); + $iteratorMock->valid()->willReturn(false); + $iteratorMock->search(Argument::type('callable'))->willReturn(false); + + $containerMock->getUrl()->willReturn(Url::factory($baseUrl)); + + $guzzleMock = $this->prophet->prophesize('Guzzle\Http\Client'); + + $guzzleMock->put($baseUrl . '/test1', [], Argument::type('Guzzle\Http\EntityBody'))->shouldBeCalled(); + $guzzleMock->send(Argument::that(function($array) { return count($array) === 1; }))->shouldBeCalled(); + + $containerMock->getClient()->willReturn($guzzleMock->reveal()); + + $sync = new DirectorySync(); + $sync->setBasePath(__DIR__ . '/fixtures'); + $sync->setContainer($containerMock->reveal()); + $sync->setRemoteFiles($iteratorMock->reveal()); + + $sync->execute(); + } + + public function test_it_uploads_to_a_nested_sub_dir_in_a_container() + { + $baseUrl = 'foo.com/bar'; + + $containerMock = $this->prophet->prophesize('OpenCloud\ObjectStore\Resource\Container'); + $iteratorMock = $this->prophet->prophesize('OpenCloud\Common\Collection\PaginatedIterator'); + + $iteratorMock->rewind()->shouldBeCalled(); + $iteratorMock->populateAll()->shouldBeCalled(); + $iteratorMock->valid()->willReturn(false); + $iteratorMock->search(Argument::type('callable'))->willReturn(false); + + $containerMock->getUrl()->willReturn(Url::factory($baseUrl)); + + $guzzleMock = $this->prophet->prophesize('Guzzle\Http\Client'); + + $guzzleMock->put($baseUrl . '/sub-dir/test1', [], Argument::type('Guzzle\Http\EntityBody'))->shouldBeCalled(); + $guzzleMock->send(Argument::that(function($array) { return count($array) === 1; }))->shouldBeCalled(); + + $containerMock->getClient()->willReturn($guzzleMock->reveal()); + + $sync = new DirectorySync(); + $sync->setBasePath(__DIR__ . '/fixtures'); + $sync->setContainer($containerMock->reveal()); + $sync->setRemoteFiles($iteratorMock->reveal()); + $sync->setTargetDir('sub-dir'); + + $sync->execute(); + } +} \ No newline at end of file From 360abf3fdc663e35cbee287151659f4cc64c1109 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 16 Jun 2015 10:44:31 +0200 Subject: [PATCH 796/835] Appease the syntax gods --- .../Tests/ObjectStore/Upload/DirectorySyncTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php b/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php index 23948e15a..b95ad2ea9 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Upload/DirectorySyncTest.php @@ -34,7 +34,7 @@ public function test_it_uploads_to_a_container() $guzzleMock = $this->prophet->prophesize('Guzzle\Http\Client'); $guzzleMock->put($baseUrl . '/test1', [], Argument::type('Guzzle\Http\EntityBody'))->shouldBeCalled(); - $guzzleMock->send(Argument::that(function($array) { return count($array) === 1; }))->shouldBeCalled(); + $guzzleMock->send(Argument::that(function ($array) { return count($array) === 1; }))->shouldBeCalled(); $containerMock->getClient()->willReturn($guzzleMock->reveal()); @@ -63,7 +63,7 @@ public function test_it_uploads_to_a_nested_sub_dir_in_a_container() $guzzleMock = $this->prophet->prophesize('Guzzle\Http\Client'); $guzzleMock->put($baseUrl . '/sub-dir/test1', [], Argument::type('Guzzle\Http\EntityBody'))->shouldBeCalled(); - $guzzleMock->send(Argument::that(function($array) { return count($array) === 1; }))->shouldBeCalled(); + $guzzleMock->send(Argument::that(function ($array) { return count($array) === 1; }))->shouldBeCalled(); $containerMock->getClient()->willReturn($guzzleMock->reveal()); @@ -75,4 +75,4 @@ public function test_it_uploads_to_a_nested_sub_dir_in_a_container() $sync->execute(); } -} \ No newline at end of file +} From 22234239d9f78583642498dae7120d5b03abe398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Podg=C3=B3rski?= Date: Fri, 19 Jun 2015 12:22:45 +0200 Subject: [PATCH 797/835] Fixed missing use statement and php doc in Queues/Service I believe does not need explaining :) ps there are other issues in this class but I will make separate PRs once I get it running --- lib/OpenCloud/Queues/Service.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/Queues/Service.php b/lib/OpenCloud/Queues/Service.php index 738e4eec4..dd3a17b0d 100644 --- a/lib/OpenCloud/Queues/Service.php +++ b/lib/OpenCloud/Queues/Service.php @@ -21,6 +21,7 @@ use Guzzle\Http\Exception\BadResponseException; use OpenCloud\Common\Exceptions\InvalidArgumentError; use OpenCloud\Common\Service\CatalogService; +use OpenCloud\Queues\Resource\Queue; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -117,8 +118,11 @@ public function getClientId() /** * Create a new Queue. * - * @param $name Name of the new queue + * @param string $name Name of the new queue + * * @return Queue + * + * @throws InvalidArgumentError */ public function createQueue($name) { From 46bbbeea8ce88ee6a54c9bd1f3d8e5e57452b878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Podg=C3=B3rski?= Date: Fri, 19 Jun 2015 12:45:32 +0200 Subject: [PATCH 798/835] phpdoc @return must be last --- lib/OpenCloud/Queues/Service.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/Queues/Service.php b/lib/OpenCloud/Queues/Service.php index dd3a17b0d..27c6eb082 100644 --- a/lib/OpenCloud/Queues/Service.php +++ b/lib/OpenCloud/Queues/Service.php @@ -119,10 +119,10 @@ public function getClientId() * Create a new Queue. * * @param string $name Name of the new queue - * - * @return Queue * * @throws InvalidArgumentError + * + * @return Queue */ public function createQueue($name) { From df93d272f148142ec1327dd45f853391b362319b Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 13 Jul 2015 15:36:33 +0200 Subject: [PATCH 799/835] ensure CDN catalog entries are not checked for regions --- lib/OpenCloud/Common/Service/CatalogItem.php | 8 +++++--- lib/OpenCloud/Common/Service/CatalogService.php | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/OpenCloud/Common/Service/CatalogItem.php b/lib/OpenCloud/Common/Service/CatalogItem.php index 5d0e44d1a..d6bf956ee 100644 --- a/lib/OpenCloud/Common/Service/CatalogItem.php +++ b/lib/OpenCloud/Common/Service/CatalogItem.php @@ -134,16 +134,18 @@ public function getEndpoints() /** * Using a standard data object, extract its endpoint. * - * @param $region + * @param string $region + * @param bool $isRegionless + * * @return mixed * @throws \OpenCloud\Common\Exceptions\EndpointError */ - public function getEndpointFromRegion($region) + public function getEndpointFromRegion($region, $isRegionless = false) { foreach ($this->endpoints as $endpoint) { // Return the endpoint if it is regionless OR if the endpoint's // region matches the $region supplied by the caller. - if (!isset($endpoint->region) || $endpoint->region == $region) { + if ($isRegionless || !isset($endpoint->region) || $endpoint->region == $region) { return $endpoint; } } diff --git a/lib/OpenCloud/Common/Service/CatalogService.php b/lib/OpenCloud/Common/Service/CatalogService.php index 69e5c659d..0e26405c5 100644 --- a/lib/OpenCloud/Common/Service/CatalogService.php +++ b/lib/OpenCloud/Common/Service/CatalogService.php @@ -215,7 +215,8 @@ private function findEndpoint() // Search each service to find The One foreach ($catalog->getItems() as $service) { if ($service->hasType($this->type) && $service->hasName($this->name)) { - return Endpoint::factory($service->getEndpointFromRegion($this->region), static::SUPPORTED_VERSION, $this->getClient()); + $endpoint = $service->getEndpointFromRegion($this->region, $this->regionless); + return Endpoint::factory($endpoint, static::SUPPORTED_VERSION, $this->getClient()); } } From 25d0ff4b62ba99a5019cb0272aea6858a5e3528e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 30 Jul 2015 10:48:05 +0200 Subject: [PATCH 800/835] Add ability for DBs to be granted to users --- doc/services/database/users.rst | 12 +++++++++ lib/OpenCloud/Database/Resource/User.php | 25 +++++++++++++++++++ .../Tests/Database/Resource/UserTest.php | 21 ++++++++++++---- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/doc/services/database/users.rst b/doc/services/database/users.rst index 0343c4904..12917ce6e 100644 --- a/doc/services/database/users.rst +++ b/doc/services/database/users.rst @@ -65,3 +65,15 @@ Check if root user is enabled // true for yes, false for no $instance->isRootEnabled(); + + +Grant database access +--------------------- + +To grant access to one or more databases, you can run: + +.. code-block:: php + + $user = $instance->user('{userName}'); + $user->grantDbAccess(['{dbName1}', '{dbName2}']); + diff --git a/lib/OpenCloud/Database/Resource/User.php b/lib/OpenCloud/Database/Resource/User.php index d1f66b181..8939fa003 100644 --- a/lib/OpenCloud/Database/Resource/User.php +++ b/lib/OpenCloud/Database/Resource/User.php @@ -152,4 +152,29 @@ protected function createJson() 'users' => array($user) ); } + + /** + * Grant access to a set of one or more databases to a user. + * + * @param []string $databases An array of one or more database names that this user will be granted access to. For + * example, ['foo', 'bar'] or ['baz'] are valid inputs. + * + * @return \Guzzle\Http\Message\Response + */ + public function grantDbAccess(array $databases) + { + $json = []; + + foreach ($databases as $database) { + $json[] = ['name' => $database]; + } + + $json = ['databases' => $json]; + + $url = $this->getUrl('databases'); + $headers = self::getJsonHeader(); + $body = json_encode($json); + + return $this->getClient()->put($url, $headers, $body)->send(); + } } diff --git a/tests/OpenCloud/Tests/Database/Resource/UserTest.php b/tests/OpenCloud/Tests/Database/Resource/UserTest.php index 04d9189c9..134ff606b 100644 --- a/tests/OpenCloud/Tests/Database/Resource/UserTest.php +++ b/tests/OpenCloud/Tests/Database/Resource/UserTest.php @@ -46,7 +46,7 @@ public function test__construct() 'OpenCloud\Database\Resource\User', $this->user ); - $u = $this->instance->user('glen', array('one', 'two')); + $u = $this->instance->user('glen', ['one', 'two']); $this->assertEquals('glen', $u->name); $this->assertEquals(2, count($u->databases)); } @@ -84,14 +84,14 @@ public function testAddDatabase() public function testCreate() { - $response = $this->user->create(array( + $response = $this->user->create([ 'name' => 'FOOBAR', 'password' => 'BAZ', - 'databases' => array( + 'databases' => [ 'foo', 'baz' - ) - )); + ] + ]); $this->assertLessThan(205, $response->getStatusCode()); $this->assertEquals('FOOBAR', $this->user->getName()); $this->assertEquals('BAZ', $this->user->password); @@ -119,4 +119,15 @@ public function testNameFailsWhenNotSet() { $this->instance->user()->getName(); } + + public function test_It_Grants_Access_To_Db() + { + $this->user->name = 'foo'; + + $response = $this->user->grantDbAccess([ + 'foo', 'bar', 'baz', + ]); + + $this->isResponse($response); + } } From 29af2c59fedc19e51b10d00edfe4c6ec93ad6c31 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 4 Aug 2015 10:27:49 +0200 Subject: [PATCH 801/835] use correct version in user-agent header --- lib/OpenCloud/Common/Http/Client.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/OpenCloud/Common/Http/Client.php b/lib/OpenCloud/Common/Http/Client.php index 9afbf9a38..a82c5ddeb 100644 --- a/lib/OpenCloud/Common/Http/Client.php +++ b/lib/OpenCloud/Common/Http/Client.php @@ -18,7 +18,7 @@ namespace OpenCloud\Common\Http; use Guzzle\Http\Client as GuzzleClient; -use Guzzle\Http\Curl\CurlVersion; +use OpenCloud\Version; use OpenCloud\Common\Exceptions\UnsupportedVersionError; /** @@ -27,7 +27,6 @@ */ class Client extends GuzzleClient { - const VERSION = '1.9.0'; const MINIMUM_PHP_VERSION = '5.3.0'; public function __construct($baseUrl = '', $config = null) @@ -46,9 +45,10 @@ public function __construct($baseUrl = '', $config = null) public function getDefaultUserAgent() { - return 'OpenCloud/' . self::VERSION - . ' cURL/' . CurlVersion::getInstance()->get('version') - . ' PHP/' . PHP_VERSION; + return 'OpenCloud/' . Version::getVersion() + . ' Guzzle/' . Version::getGuzzleVersion() + . ' cURL/' . Version::getCurlVersion() + . ' PHP/' . PHP_VERSION; } public function getUserAgent() From 69fd07b26fa9d08f023120a25354221a4c856ba7 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 4 Aug 2015 10:37:22 +0200 Subject: [PATCH 802/835] syntax for the syntax gods --- lib/OpenCloud/Common/Http/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Common/Http/Client.php b/lib/OpenCloud/Common/Http/Client.php index a82c5ddeb..1988eda5e 100644 --- a/lib/OpenCloud/Common/Http/Client.php +++ b/lib/OpenCloud/Common/Http/Client.php @@ -47,7 +47,7 @@ public function getDefaultUserAgent() { return 'OpenCloud/' . Version::getVersion() . ' Guzzle/' . Version::getGuzzleVersion() - . ' cURL/' . Version::getCurlVersion() + . ' cURL/' . Version::getCurlVersion() . ' PHP/' . PHP_VERSION; } From eff8bfe53253b4004036d60049cd92b1729e40b7 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 4 Aug 2015 10:40:21 +0200 Subject: [PATCH 803/835] fix weird syntax problem --- lib/OpenCloud/Queues/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Queues/Service.php b/lib/OpenCloud/Queues/Service.php index 27c6eb082..506555b4e 100644 --- a/lib/OpenCloud/Queues/Service.php +++ b/lib/OpenCloud/Queues/Service.php @@ -119,7 +119,7 @@ public function getClientId() * Create a new Queue. * * @param string $name Name of the new queue - * + * * @throws InvalidArgumentError * * @return Queue From 704cbe9ae0db2ad4807fc5ba89036ca5612cbe1f Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Sat, 15 Aug 2015 13:24:35 +0200 Subject: [PATCH 804/835] Remove old maintainers --- composer.json | 9 --------- 1 file changed, 9 deletions(-) diff --git a/composer.json b/composer.json index ba7ef6ff2..cd8747755 100644 --- a/composer.json +++ b/composer.json @@ -9,15 +9,6 @@ "name": "Jamie Hannaford", "email": "jamie.hannaford@rackspace.com", "homepage" : "https://github.com/jamiehannaford" - }, - { - "name": "Glen Campbell", - "email": "glen.campbell@rackspace.com" - }, - { - "name": "Shaunak Kashyap", - "email": "shaunak.kashyap@rackspace.com", - "homepage": "https://github.com/ycombinator" } ], "autoload": { From 10ba865a09ccf815751d6f370e404d51b5f225ab Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Sat, 15 Aug 2015 13:28:39 +0200 Subject: [PATCH 805/835] Remove unnecessary PHP version checking; Composer does that for us --- lib/OpenCloud/Common/Http/Client.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/OpenCloud/Common/Http/Client.php b/lib/OpenCloud/Common/Http/Client.php index 1988eda5e..55cbafe9b 100644 --- a/lib/OpenCloud/Common/Http/Client.php +++ b/lib/OpenCloud/Common/Http/Client.php @@ -19,7 +19,6 @@ use Guzzle\Http\Client as GuzzleClient; use OpenCloud\Version; -use OpenCloud\Common\Exceptions\UnsupportedVersionError; /** * Base client object which handles HTTP transactions. Each service is based off of a Client which acts as a @@ -27,22 +26,6 @@ */ class Client extends GuzzleClient { - const MINIMUM_PHP_VERSION = '5.3.0'; - - public function __construct($baseUrl = '', $config = null) - { - // @codeCoverageIgnoreStart - if (PHP_VERSION < self::MINIMUM_PHP_VERSION) { - throw new UnsupportedVersionError(sprintf( - 'You must have PHP version >= %s installed.', - self::MINIMUM_PHP_VERSION - )); - } - // @codeCoverageIgnoreEnd - - parent::__construct($baseUrl, $config); - } - public function getDefaultUserAgent() { return 'OpenCloud/' . Version::getVersion() From b949f3ac22a4b4efe2f0593a187d6de4fa5b91cd Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 25 Aug 2015 14:29:40 +0200 Subject: [PATCH 806/835] Fix metadata creation for servers; fixes #625 --- lib/OpenCloud/Compute/Resource/ServerMetadata.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/ServerMetadata.php b/lib/OpenCloud/Compute/Resource/ServerMetadata.php index aba039563..234fb172b 100644 --- a/lib/OpenCloud/Compute/Resource/ServerMetadata.php +++ b/lib/OpenCloud/Compute/Resource/ServerMetadata.php @@ -179,10 +179,7 @@ private function getMetadataJson() if ($name = $this->key) { $object->meta->$name = $this->$name; } else { - $object->metadata = new \stdClass(); - foreach ($this->keylist() as $key) { - $object->metadata->$key = (string) $this->$key; - } + $object->metadata = $this->keylist(); } $json = json_encode($object); From 0ad1ffb8f4f78872df04b96c8e2692e2ed767ecf Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 25 Aug 2015 14:37:49 +0200 Subject: [PATCH 807/835] fix syntax issue --- lib/OpenCloud/Compute/Resource/ServerMetadata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/Compute/Resource/ServerMetadata.php b/lib/OpenCloud/Compute/Resource/ServerMetadata.php index 234fb172b..203e2d63a 100644 --- a/lib/OpenCloud/Compute/Resource/ServerMetadata.php +++ b/lib/OpenCloud/Compute/Resource/ServerMetadata.php @@ -179,7 +179,7 @@ private function getMetadataJson() if ($name = $this->key) { $object->meta->$name = $this->$name; } else { - $object->metadata = $this->keylist(); + $object->metadata = $this->keylist(); } $json = json_encode($object); From 25248cbc796f1ffefa8b242c35965efa4e19419d Mon Sep 17 00:00:00 2001 From: dataskills Date: Mon, 12 Oct 2015 11:59:19 -0700 Subject: [PATCH 808/835] override TempURL file names I propose to add instructions override TempURL file names, it took me some time to figure out how to do it, I found it in a blog post from 2013: http://blog.rackspace.com/cloud-files-update-new-api-only-and-control-panel-features/?cm_mmc=Social-_-FaceBook-_-Blog-_-CustRef&sf13063985=1 I tried it, it works. --- doc/services/object-store/access.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/services/object-store/access.rst b/doc/services/object-store/access.rst index 40fe61670..745339d97 100644 --- a/doc/services/object-store/access.rst +++ b/doc/services/object-store/access.rst @@ -49,6 +49,17 @@ To allow PUT access for 1 hour: `Get the executable PHP script for this example `_ +Override TempURL file names +--------------------------- + +Override tempURL file names simply by adding the filename parameter to the url: + +.. code-block:: php + + $tempUrl = $object->getTemporaryUrl(60, 'GET'); + $url = $tempUrl.'&filename='.$label; + + Hosting HTML sites on CDN ========================= From 9d423ba0da4f34ed77e3c838325cb33557590f74 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Fri, 23 Oct 2015 10:56:02 +0200 Subject: [PATCH 809/835] Add docs for checking whether a file exists --- doc/services/object-store/objects.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 7e32f3610..3bc221dcc 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -518,3 +518,13 @@ Bulk delete a set of paths: $service->bulkDelete($pathsToBeDeleted); `Get the executable PHP script for this example `_ + +Check an object exists +---------------------- + +To check whether an object exists: + +.. code-block:: php + + /** @var bool $exists */ + $exists = $container->objectExists('{objectName}'); From 384ceb887a88f384cce75368d747371735731ef9 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 26 Oct 2015 12:09:00 +0100 Subject: [PATCH 810/835] add ability to use ports when creating server --- lib/OpenCloud/Compute/Resource/Server.php | 21 ++++++++----------- .../Tests/Compute/Resource/ServerTest.php | 14 +++++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index 0d47f5dec..f26ba8bde 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -21,6 +21,7 @@ use OpenCloud\DNS\Resource\HasPtrRecordsInterface; use OpenCloud\Image\Resource\ImageInterface; use OpenCloud\Networking\Resource\NetworkInterface; +use OpenCloud\Networking\Resource\Port; use OpenCloud\Volume\Resource\Volume; use OpenCloud\Common\Exceptions; use OpenCloud\Common\Http\Message\Formatter; @@ -689,22 +690,18 @@ protected function createJson() $server->networks = array(); foreach ($this->networks as $network) { - if (!$network instanceof NetworkInterface) { + if ($network instanceof NetworkInterface) { + $server->networks[] = (object) array('uuid' => $network->getId()); + } elseif ($network instanceof Port) { + $server->networks[] = (object) array('port' => $network->getId()); + } else { throw new Exceptions\InvalidParameterError(sprintf( 'When creating a server, the "networks" key must be an ' . - 'array of objects which implement OpenCloud\Networking\Resource\NetworkInterface;' . - 'variable passed in was a [%s]', - gettype($network) + 'array of objects which implement either %s or %s. The ' . + 'variable you passed in was a [%s]', + NetworkInterface::class, Port::class, gettype($network) )); } - if (!($networkId = $network->getId())) { - $this->getLogger()->warning('When creating a server, the ' - . 'network objects passed in must have an ID' - ); - continue; - } - // Stock networks array - $server->networks[] = (object) array('uuid' => $networkId); } } diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index 13ed80a35..7d173f2cb 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -336,6 +336,20 @@ public function test_Create_With_Networks() )); } + public function test_Create_With_Ports() + { + $neutronService = $this->client->networkingService(null, 'IAD'); + $port = $neutronService->port(); + $port->setId('foo'); + + $this->service->server()->create(array( + 'name' => 'port test', + 'image' => $this->service->imageList()->first(), + 'flavor' => $this->service->flavorList()->first(), + 'networks' => [$port] + )); + } + /** * @expectedException OpenCloud\Common\Exceptions\InvalidParameterError */ From 3c3557b44f1b27ac3d36c0e7bbfcd65b89f5a678 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 26 Oct 2015 12:10:46 +0100 Subject: [PATCH 811/835] remove php 5.5 feature --- lib/OpenCloud/Compute/Resource/Server.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index f26ba8bde..b9536947e 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -697,9 +697,9 @@ protected function createJson() } else { throw new Exceptions\InvalidParameterError(sprintf( 'When creating a server, the "networks" key must be an ' . - 'array of objects which implement either %s or %s. The ' . - 'variable you passed in was a [%s]', - NetworkInterface::class, Port::class, gettype($network) + 'array of objects which implement either OpenCloud\Networking\Resource\NetworkInterface ' . + 'or OpenCloud\Networking\Resource\Port. The variable you passed in was a [%s]', + gettype($network) )); } } From 36cc9c43351dfb830d2a6e15e4f911fbbe217e3f Mon Sep 17 00:00:00 2001 From: Omer Karadagli Date: Mon, 9 Nov 2015 16:05:15 +0000 Subject: [PATCH 812/835] Autoload tests only for dev This will prevent people that require the library in composer getting the tests in their optimised composer classmap. --- composer.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cd8747755..68d43364b 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,12 @@ ], "autoload": { "psr-0": { - "OpenCloud": ["lib/", "tests/"] + "OpenCloud": ["lib/"] + } + }, + "autoload-dev": { + "psr-0": { + "OpenCloud": ["tests/"] } }, "require": { From de8edb7224e2127ff46a2b8368964093bca11a2b Mon Sep 17 00:00:00 2001 From: Patrick Rose Date: Tue, 10 Nov 2015 16:48:53 +0000 Subject: [PATCH 813/835] Make sure container name is valid when retrieving --- lib/OpenCloud/ObjectStore/Service.php | 4 ++++ .../Tests/ObjectStore/ServiceTest.php | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index babd764dd..54ecdeaf1 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -92,6 +92,10 @@ public function listContainers(array $filter = array()) */ public function getContainer($data = null) { + if (is_string($data)) { + $this->checkContainerName($data); + } + return new Container($this, $data); } diff --git a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php index dfb62e63c..1c4ae6cc4 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php @@ -99,6 +99,30 @@ public function test_Bad_Container_Name_Long() $this->service->createContainer(str_repeat('a', Service::MAX_CONTAINER_NAME_LENGTH + 1)); } + /** + * @expectedException OpenCloud\Common\Exceptions\InvalidArgumentError + */ + public function test_Get_Bad_Container_Name_Empty() + { + $this->service->getContainer(''); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\InvalidArgumentError + */ + public function test_Get_Bad_Container_Name_Slashes() + { + $this->service->getContainer('foo/bar'); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\InvalidArgumentError + */ + public function test_Get_Bad_Container_Name_Long() + { + $this->service->getContainer(str_repeat('a', Service::MAX_CONTAINER_NAME_LENGTH + 1)); + } + public function test_Bulk_Extract() { $response = $this->service->bulkExtract('fooBarContainer', 'CONTENT', UrlType::TAR); From 6040f6828ec1bca79f8e87791ebe63b8f8e3f169 Mon Sep 17 00:00:00 2001 From: Patrick Rose Date: Wed, 11 Nov 2015 10:27:29 +0000 Subject: [PATCH 814/835] Check container name if numeric --- lib/OpenCloud/ObjectStore/Service.php | 2 +- .../Tests/ObjectStore/ServiceTest.php | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Service.php b/lib/OpenCloud/ObjectStore/Service.php index 54ecdeaf1..0079027b0 100644 --- a/lib/OpenCloud/ObjectStore/Service.php +++ b/lib/OpenCloud/ObjectStore/Service.php @@ -92,7 +92,7 @@ public function listContainers(array $filter = array()) */ public function getContainer($data = null) { - if (is_string($data)) { + if (is_string($data) || is_numeric($data)) { $this->checkContainerName($data); } diff --git a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php index 1c4ae6cc4..97267f933 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php @@ -123,6 +123,24 @@ public function test_Get_Bad_Container_Name_Long() $this->service->getContainer(str_repeat('a', Service::MAX_CONTAINER_NAME_LENGTH + 1)); } + /** + * @expectedException OpenCloud\Common\Exceptions\InvalidArgumentError + */ + public function test_Get_Bad_Container_Name_Float() + { + $this->service->getContainer(4.5); + } + + public function test_Get_Container_String() + { + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\Container', $this->service->getContainer('foo')); + } + + public function test_Get_Container_Integer() + { + $this->assertInstanceOf('OpenCloud\ObjectStore\Resource\Container', $this->service->getContainer(1)); + } + public function test_Bulk_Extract() { $response = $this->service->bulkExtract('fooBarContainer', 'CONTENT', UrlType::TAR); @@ -166,7 +184,8 @@ public function test_Batch_Delete_Returns_Array_Of_Responses() { $responses = array_fill(0, 2, new Response(200)); - foreach ($responses as $response) { + foreach ($responses as $response) + { $this->addMockSubscriber($response); } From b5b70113acefa4f7abd88233a7ec1024b3db7316 Mon Sep 17 00:00:00 2001 From: Patrick Rose Date: Wed, 11 Nov 2015 11:04:51 +0000 Subject: [PATCH 815/835] Run CS fixer --- tests/OpenCloud/Tests/ObjectStore/ServiceTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php index 97267f933..f5f371f22 100644 --- a/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/ServiceTest.php @@ -184,8 +184,7 @@ public function test_Batch_Delete_Returns_Array_Of_Responses() { $responses = array_fill(0, 2, new Response(200)); - foreach ($responses as $response) - { + foreach ($responses as $response) { $this->addMockSubscriber($response); } From 8401e0eb56bb4334978b6a568830cd56f562ee85 Mon Sep 17 00:00:00 2001 From: Daniel Seif Date: Mon, 11 Jan 2016 18:08:48 +0100 Subject: [PATCH 816/835] refactored load balancer node adding to use one request instead of several in parallel --- lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php index cc3e85339..551198c1b 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php +++ b/lib/OpenCloud/LoadBalancer/Resource/LoadBalancer.php @@ -255,17 +255,20 @@ public function addNodes() ); } - $requests = array(); + $requestData = array('nodes' => array()); + /** @var Node $node */ foreach ($this->nodes as $node) { // Only add the node if it is new if (null === $node->getId()) { - $json = json_encode($node->createJson()); - $requests[] = $this->getClient()->post($node->getUrl(), self::getJsonHeader(), $json); + $nodeJson = $node->createJson(); + $requestData['nodes'][] = $nodeJson['nodes'][0]; } } - return $this->getClient()->send($requests); + $request = $this->getClient()->post($node->getUrl(), self::getJsonHeader(), json_encode($requestData)); + + return $this->getClient()->send($request); } /** From b7a5e5fcd03e6ef40d74fa04fe53068bb737578b Mon Sep 17 00:00:00 2001 From: Jan Fabry Date: Wed, 13 Jan 2016 16:50:18 +0100 Subject: [PATCH 817/835] Add support for security groups on server create --- lib/OpenCloud/Compute/Resource/Server.php | 28 +++++++++++++ .../Tests/Compute/Resource/ServerTest.php | 39 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/lib/OpenCloud/Compute/Resource/Server.php b/lib/OpenCloud/Compute/Resource/Server.php index b9536947e..9038c291d 100644 --- a/lib/OpenCloud/Compute/Resource/Server.php +++ b/lib/OpenCloud/Compute/Resource/Server.php @@ -21,6 +21,7 @@ use OpenCloud\DNS\Resource\HasPtrRecordsInterface; use OpenCloud\Image\Resource\ImageInterface; use OpenCloud\Networking\Resource\NetworkInterface; +use OpenCloud\Networking\Resource\SecurityGroup; use OpenCloud\Networking\Resource\Port; use OpenCloud\Volume\Resource\Volume; use OpenCloud\Common\Exceptions; @@ -104,6 +105,12 @@ class Server extends NovaResource implements HasPtrRecordsInterface */ public $networks = array(); + /** + * Security groups for this server. An array of either the names or SecurityGroup objects. + * @var (string|SecurityGroup)[] + */ + public $security_groups = array(); + /** * @var string The server ID. */ @@ -705,6 +712,27 @@ protected function createJson() } } + // Security groups + if (is_array($this->security_groups) && count($this->security_groups)) { + $server->security_groups = array(); + + foreach ($this->security_groups as $security_group) { + if ($security_group instanceof SecurityGroup) { + $securityGroupName = $security_group->name(); + } elseif (is_string($security_group)) { + $securityGroupName = $security_group; + } else { + throw new Exceptions\InvalidParameterError(sprintf( + 'When creating a server, the "security_groups" key must be an ' . + 'array of strings or objects of type OpenCloud\Networking\Resource\SecurityGroup;' . + 'variable passed in was a [%s]', + gettype($security_group) + )); + } + $server->security_groups[] = (object) array('name' => $securityGroupName); + } + } + // Personality files if (!empty($this->personality)) { $server->personality = array(); diff --git a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php index 7d173f2cb..1c105396d 100644 --- a/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php +++ b/tests/OpenCloud/Tests/Compute/Resource/ServerTest.php @@ -365,6 +365,45 @@ public function test_Create_Fails_Without_Correct_Networks() )); } + public function test_Create_With_Security_Group_Strings() + { + $new = new PublicServer($this->service); + $new->security_groups[] = 'foo'; + $obj = $new->createJson(); + + $this->assertCount(1, $obj->server->security_groups); + $this->assertEquals((object) array('name' => 'foo'), $obj->server->security_groups[0]); + } + + public function test_Create_With_Security_Group_Objects() + { + $neutronService = $this->client->networkingService(null, 'IAD'); + $securityGroup = $neutronService->securityGroup(); + $securityGroup->setName('foo'); + + $new = new PublicServer($this->service); + $new->security_groups[] = $securityGroup; + $obj = $new->createJson(); + + $this->assertCount(1, $obj->server->security_groups); + $this->assertEquals((object) array('name' => 'foo'), $obj->server->security_groups[0]); + } + + /** + * @expectedException OpenCloud\Common\Exceptions\InvalidParameterError + */ + public function test_Create_Fails_Without_Correct_Security_Groups() + { + $this->service->server()->create(array( + 'name' => 'personality test 1', + 'image' => $this->service->imageList()->first(), + 'flavor' => $this->service->flavorList()->first(), + 'security_groups' => array( + 1234 + ) + )); + } + public function test_Create_With_Bootable_Volume() { $new = new PublicServer($this->service); From 4872bae09479b8637335df4b61c246d4cb0aa245 Mon Sep 17 00:00:00 2001 From: David Ducatel Date: Fri, 15 Jan 2016 11:10:44 +0100 Subject: [PATCH 818/835] Add delete object method in the container of ObjectStore. --- doc/services/object-store/objects.rst | 15 ++++++-- .../ObjectStore/Resource/Container.php | 22 ++++++++++++ .../delete-object-without-download.php | 36 +++++++++++++++++++ .../ObjectStore/Resource/ContainerTest.php | 26 ++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 samples/ObjectStore/delete-object-without-download.php diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 3bc221dcc..b9928e3fb 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -495,9 +495,20 @@ filenames inside the archive. `Get the executable PHP script for this example `_ - Delete object -------------- +--------------------------------- + +.. code-block:: php + + /** @var bool $deleted */ + $deleted = $container->deleteObject('{objectName}'); + + +`Get the executable PHP script for this example `_ + + +Delete already downloaded object +--------------------------------- .. code-block:: php diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index ed47a4d35..37826b78e 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -235,6 +235,28 @@ public function deleteAllObjects() return $this->getService()->batchDelete($paths); } + /** + * Delete an object from the API. + * + * @param string $name The name of object you want to delete + * @return True if object was delete with success. False if an error occurred + * @throws ObjectNotFoundException When the file you want to delete is not found + */ + public function deleteObject($name) + { + try { + $response = $this->getClient() + ->delete($this->getUrl($name)) + ->send(); + return ($response->getStatusCode() == 204); + } catch (BadResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + throw ObjectNotFoundException::factory($name, $e); + } + } + return false; + } + /** * Creates a Collection of objects in the container * diff --git a/samples/ObjectStore/delete-object-without-download.php b/samples/ObjectStore/delete-object-without-download.php new file mode 100644 index 000000000..bbed09c2e --- /dev/null +++ b/samples/ObjectStore/delete-object-without-download.php @@ -0,0 +1,36 @@ + '{username}', + 'apiKey' => '{apiKey}', +)); + +// 2. Obtain an Object Store service object from the client. +$objectStoreService = $client->objectStoreService(null, '{region}'); + +// 3. Get container. +$container = $objectStoreService->getContainer('{containerName}'); + +// 4. Delete object. +$container->deleteObject('{objectName}'); diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index c5b191249..c79b35634 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -118,6 +118,32 @@ public function test_Delete_NonEmpty_Container() $container->delete(); } + public function test_Delete_Object() + { + $container = $this->container; + $this->addMockSubscriber($this->makeResponse('[]', 204)); + $response = $container->deleteObject("someObject"); + $this->assertTrue($response); + } + + public function test_Delete_Object_With_Failure() + { + $container = $this->container; + $this->addMockSubscriber($this->makeResponse('[]', 500)); + $response = $container->deleteObject("someObject"); + $this->assertFalse($response); + } + + /** + * @expectedException \OpenCloud\ObjectStore\Exception\ObjectNotFoundException + */ + public function test_Delete_NonExisting_Object() + { + $container = $this->container; + $this->addMockSubscriber($this->makeResponse('[]', 404)); + $container->deleteObject("someObject"); + } + public function test_Object_List() { $container = $this->container; From 409bddd274daa22976793addf74997c8d48c72a7 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 19 Jan 2016 13:58:30 +0100 Subject: [PATCH 819/835] Add code of conduct --- CODE_OF_CONDUCT.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..fb82d68b1 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,50 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of +fostering an open and welcoming community, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to +fairly and consistently applying these principles to every aspect of managing +this project. Project maintainers who do not follow or enforce the Code of +Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting a project maintainer at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. Maintainers are +obligated to maintain confidentiality with regard to the reporter of an +incident. + + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.3.0, available at +[http://contributor-covenant.org/version/1/3/0/][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/3/0/ From ec165c68183eda0a44003a319ac9bf6c8dbff3e2 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Tue, 19 Jan 2016 13:59:45 +0100 Subject: [PATCH 820/835] add email --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index fb82d68b1..0832bf8bb 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -35,7 +35,7 @@ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting a project maintainer at [INSERT EMAIL ADDRESS]. All +reported by contacting a project maintainer at sdk-support@rackspace.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an From 1b57e794dbb6a4b339338aa66daf7c9020d4d91c Mon Sep 17 00:00:00 2001 From: matt-in-a-hat Date: Wed, 20 Jan 2016 18:07:52 +1300 Subject: [PATCH 821/835] Fix temp public URL missing file path info Should fix #648 Using `combine` didn't work, as it would either override the path info, or when swapped it would override the external host info with the internal host. --- lib/OpenCloud/ObjectStore/Resource/DataObject.php | 6 +++++- .../OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/DataObject.php b/lib/OpenCloud/ObjectStore/Resource/DataObject.php index 731d1c370..5eb3c085e 100644 --- a/lib/OpenCloud/ObjectStore/Resource/DataObject.php +++ b/lib/OpenCloud/ObjectStore/Resource/DataObject.php @@ -481,7 +481,11 @@ public function getTemporaryUrl($expires, $method, $forcePublicUrl = false) } // @codeCoverageIgnoreEnd - $url = ($forcePublicUrl === true) ? $this->getService()->getEndpoint()->getPublicUrl() : $this->getUrl(); + $url = $this->getUrl(); + if ($forcePublicUrl === true) { + $url->setHost($this->getService()->getEndpoint()->getPublicUrl()->getHost()); + } + $urlPath = urldecode($url->getPath()); $body = sprintf("%s\n%d\n%s", $method, $expiry, $urlPath); $hash = hash_hmac('sha1', $body, $secret); diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php index 3dab21171..5aee9cd81 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/DataObjectTest.php @@ -111,6 +111,9 @@ public function test_temp_urls_can_be_forced_to_use_public_urls() // Check that internal URLs are NOT used $this->assertNotContains('snet-storage', $tempUrl); + + // Check that the URL contains the required file path + $this->assertContains('/foo/bar', $tempUrl); } public function test_Purge() From 87a0eb5655754a3a1626fb81db315e1d488c9c33 Mon Sep 17 00:00:00 2001 From: David Ducatel Date: Thu, 28 Jan 2016 11:05:50 +0100 Subject: [PATCH 822/835] Remove catching exception --- .../ObjectStore/Resource/Container.php | 17 ++++------------- .../ObjectStore/Resource/ContainerTest.php | 11 ++++++----- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 37826b78e..94f33b32e 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -239,22 +239,13 @@ public function deleteAllObjects() * Delete an object from the API. * * @param string $name The name of object you want to delete - * @return True if object was delete with success. False if an error occurred - * @throws ObjectNotFoundException When the file you want to delete is not found + * @throws \Guzzle\Http\Exception\BadResponseException When an error occurred */ public function deleteObject($name) { - try { - $response = $this->getClient() - ->delete($this->getUrl($name)) - ->send(); - return ($response->getStatusCode() == 204); - } catch (BadResponseException $e) { - if ($e->getResponse()->getStatusCode() == 404) { - throw ObjectNotFoundException::factory($name, $e); - } - } - return false; + $response = $this->getClient() + ->delete($this->getUrl($name)) + ->send(); } /** diff --git a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php index c79b35634..2b02418a0 100644 --- a/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php +++ b/tests/OpenCloud/Tests/ObjectStore/Resource/ContainerTest.php @@ -122,20 +122,21 @@ public function test_Delete_Object() { $container = $this->container; $this->addMockSubscriber($this->makeResponse('[]', 204)); - $response = $container->deleteObject("someObject"); - $this->assertTrue($response); + $container->deleteObject("someObject"); } + /** + * @expectedException \Guzzle\Http\Exception\BadResponseException + */ public function test_Delete_Object_With_Failure() { $container = $this->container; $this->addMockSubscriber($this->makeResponse('[]', 500)); - $response = $container->deleteObject("someObject"); - $this->assertFalse($response); + $container->deleteObject("someObject"); } /** - * @expectedException \OpenCloud\ObjectStore\Exception\ObjectNotFoundException + * @expectedException \Guzzle\Http\Exception\BadResponseException */ public function test_Delete_NonExisting_Object() { From 90358518a0ff057ac6c8ba0217be6cbdd634922e Mon Sep 17 00:00:00 2001 From: David Ducatel Date: Fri, 29 Jan 2016 11:25:56 +0100 Subject: [PATCH 823/835] Remove unused variables --- doc/services/object-store/objects.rst | 3 +-- lib/OpenCloud/ObjectStore/Resource/Container.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index b9928e3fb..6d7362246 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -500,8 +500,7 @@ Delete object .. code-block:: php - /** @var bool $deleted */ - $deleted = $container->deleteObject('{objectName}'); + $container->deleteObject('{objectName}'); `Get the executable PHP script for this example `_ diff --git a/lib/OpenCloud/ObjectStore/Resource/Container.php b/lib/OpenCloud/ObjectStore/Resource/Container.php index 94f33b32e..474d9134b 100644 --- a/lib/OpenCloud/ObjectStore/Resource/Container.php +++ b/lib/OpenCloud/ObjectStore/Resource/Container.php @@ -243,7 +243,7 @@ public function deleteAllObjects() */ public function deleteObject($name) { - $response = $this->getClient() + $this->getClient() ->delete($this->getUrl($name)) ->send(); } From 845f32d1c032e59169234ecf58ad999e6558c831 Mon Sep 17 00:00:00 2001 From: georaldc Date: Tue, 12 Apr 2016 09:29:02 -0700 Subject: [PATCH 824/835] Updated bulk delete method call in docs --- doc/services/object-store/objects.rst | 2 +- samples/ObjectStore/bulk-delete.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/services/object-store/objects.rst b/doc/services/object-store/objects.rst index 6d7362246..18d3e7502 100644 --- a/doc/services/object-store/objects.rst +++ b/doc/services/object-store/objects.rst @@ -525,7 +525,7 @@ Bulk delete a set of paths: $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); - $service->bulkDelete($pathsToBeDeleted); + $service->batchDelete($pathsToBeDeleted); `Get the executable PHP script for this example `_ diff --git a/samples/ObjectStore/bulk-delete.php b/samples/ObjectStore/bulk-delete.php index 4bcedda7d..81a47cd0b 100644 --- a/samples/ObjectStore/bulk-delete.php +++ b/samples/ObjectStore/bulk-delete.php @@ -31,7 +31,7 @@ $objectStoreService = $client->objectStoreService(null, '{region}'); // 3. Bulk delete objects and empty containers. -$response = $objectStoreService->bulkDelete(array( +$response = $objectStoreService->batchDelete(array( '{container1}/{objectName}', '{container2}/{objectName}', '{container3}' From ba9c6312ec71f901af593bc2290d5e6591c73e95 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 27 Apr 2016 13:05:31 -0500 Subject: [PATCH 825/835] promote new SDK; fixes #665 --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 12fb530b8..81fbc7bea 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -**php-opencloud** -============= -PHP SDK for OpenStack/Rackspace APIs +# php-opencloud + +> OpenStack users are recommended to use https://github.com/php-opencloud/openstack, which +> is the latest supported version of the SDK. This codebase which you are now looking at will +> enter end-of-life soon. For Rackspace Cloud users, a v2 is actively being worked on. [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) -Our official documentation is now available on http://docs.php-opencloud.com. For SDKs in different languages, see http://developer.rackspace.com. +PHP SDK for OpenStack/Rackspace APIs. Our official documentation is now available on http://docs.php-opencloud.com. For SDKs in different languages, see http://developer.rackspace.com. The PHP SDK should work with most OpenStack-based cloud deployments, though it specifically targets the Rackspace public cloud. In From f478f1a3c3904c2039d985f6bdf7dedfbddbcd1e Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Wed, 27 Apr 2016 13:08:46 -0500 Subject: [PATCH 826/835] use bold --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81fbc7bea..e7f2b17a1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # php-opencloud -> OpenStack users are recommended to use https://github.com/php-opencloud/openstack, which +> **OpenStack users are recommended to use https://github.com/php-opencloud/openstack, which > is the latest supported version of the SDK. This codebase which you are now looking at will -> enter end-of-life soon. For Rackspace Cloud users, a v2 is actively being worked on. +> enter end-of-life soon. For Rackspace Cloud users, a v2 is actively being worked on.** [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) From 91889c264c7caf7993bfb57bb28ab8f9a2fa38b5 Mon Sep 17 00:00:00 2001 From: Daniel Cordero Date: Thu, 5 May 2016 11:07:00 +0000 Subject: [PATCH 827/835] Document server user_data parameter --- doc/services/compute/servers.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/services/compute/servers.rst b/doc/services/compute/servers.rst index 0709bda9a..3ebb99736 100644 --- a/doc/services/compute/servers.rst +++ b/doc/services/compute/servers.rst @@ -133,6 +133,8 @@ Create parameters +-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ | personality | Files that you can upload to your newly created instance's filesystem. | array | No | +-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| user_data | User script to configure the server at launch time | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ Creating a server with keypairs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From facd93e874e6aa2643dccb43b5e1ea9ee4b1da2b Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Mon, 30 May 2016 18:52:23 -0300 Subject: [PATCH 828/835] Add `extra.branch-alias` setting in `composer.json` | Q | A | ------------- | --- | Branch | working | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | Add `extra.branch-alias` setting in `composer.json` in order to allow install latest development version while respecting semantic versioning. --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index 68d43364b..e5074921d 100644 --- a/composer.json +++ b/composer.json @@ -34,5 +34,10 @@ "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", "apigen/apigen": "~4.0" + }, + "extra": { + "branch-alias": { + "dev-working": "1.16-dev" + } } } From d8aa69dc655f0654ce07e5ad3dc3ae9452ab7e76 Mon Sep 17 00:00:00 2001 From: Eric Vachaviolos Date: Wed, 13 Jul 2016 13:09:57 -0400 Subject: [PATCH 829/835] Fix segment name to support more than 9 parts Zero-padding the segment number to 5 digits allows to support the minimum chunk size of 1MB and the maximum of 5GB. Without padding, when retrieving a file segmented in 10 or more segments, it will be reassembled in the wrong order and thus will be rendered useless. --- lib/OpenCloud/ObjectStore/Upload/TransferPart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenCloud/ObjectStore/Upload/TransferPart.php b/lib/OpenCloud/ObjectStore/Upload/TransferPart.php index 3da3f0162..a6de553c1 100644 --- a/lib/OpenCloud/ObjectStore/Upload/TransferPart.php +++ b/lib/OpenCloud/ObjectStore/Upload/TransferPart.php @@ -135,7 +135,7 @@ public function getPath() */ public static function createRequest($part, $number, $client, $options) { - $name = sprintf('%s/%s/%d', $options['objectName'], $options['prefix'], $number); + $name = sprintf('%s/%s/%05d', $options['objectName'], $options['prefix'], $number); $url = clone $options['containerUrl']; $url->addPath($name); From 0dd031c35cec727863245e4dd734d667a104e9f5 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 16 Mar 2017 13:32:11 +0100 Subject: [PATCH 830/835] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index e7f2b17a1..4bf61b4bb 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # php-opencloud > **OpenStack users are recommended to use https://github.com/php-opencloud/openstack, which -> is the latest supported version of the SDK. This codebase which you are now looking at will -> enter end-of-life soon. For Rackspace Cloud users, a v2 is actively being worked on.** +> is the latest supported version of the SDK for OpenStack. [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) From 02097d064d7d97bd04699b8d4146796c8013eacc Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Thu, 16 Mar 2017 13:32:26 +0100 Subject: [PATCH 831/835] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bf61b4bb..15191ba3d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # php-opencloud > **OpenStack users are recommended to use https://github.com/php-opencloud/openstack, which -> is the latest supported version of the SDK for OpenStack. +> is the latest supported version of the SDK for OpenStack.** [![Latest Stable Version](https://poser.pugx.org/rackspace/php-opencloud/v/stable.png)](https://packagist.org/packages/rackspace/php-opencloud) [![Travis CI](https://secure.travis-ci.org/rackspace/php-opencloud.png)](https://travis-ci.org/rackspace/php-opencloud) [![Total Downloads](https://poser.pugx.org/rackspace/php-opencloud/downloads.png)](https://packagist.org/packages/rackspace/php-opencloud) From 430a35de4f548fcfd5685a33f1c102af76733506 Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 3 Apr 2017 11:19:49 +0200 Subject: [PATCH 832/835] Remove email refs --- CODE_OF_CONDUCT.md | 2 +- composer.json | 2 +- doc/index.rst | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0832bf8bb..5ff44c32b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -35,7 +35,7 @@ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting a project maintainer at sdk-support@rackspace.com. All +reported by opening an issue. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an diff --git a/composer.json b/composer.json index 68d43364b..3fd501ae3 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "require-dev" : { "phpunit/phpunit": "4.3.*", "phpspec/prophecy": "~1.4", - "satooshi/php-coveralls": "0.6.*@dev", + "satooshi/php-coveralls": "0.6.1", "jakub-onderka/php-parallel-lint": "0.*", "fabpot/php-cs-fixer": "1.0.*@dev", "apigen/apigen": "~4.0" diff --git a/doc/index.rst b/doc/index.rst index 3c47091cc..9771b365c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -67,9 +67,6 @@ have a `mailing list `_, so feel free to join to keep up to date with all the latest changes and announcements to the library. -For general feedback and support requests, send an email to -sdk-support@rackspace.com. - You can also find assistance via IRC on #rackspace at freenode.net. Contributing From 9f5aa4de8e0aa2a66442d6f0c4aa5b83f606ed7f Mon Sep 17 00:00:00 2001 From: Lea Fairbanks Date: Tue, 18 Apr 2017 14:59:43 -0600 Subject: [PATCH 833/835] Fix certificateMappingList returning valid CertificateMappings when iterated --- .../LoadBalancer/Resource/CertificateMapping.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php index 3b770a212..b5b844866 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php @@ -93,4 +93,16 @@ protected function updateJson($params = array()) return $object; } + + /** + * Sets properties from array || object of $values + * + * Used by LoadBalancer::certificateMappingList's paginated iterator + * to return CertificateMappings with a valid id and hostname + * + * @var array|object $values + */ + protected function setCertificateMapping($values) { + $this->populate($values); + } } From 3bf08b23054609370669de133a8d28d18d89ff5a Mon Sep 17 00:00:00 2001 From: Lea Fairbanks Date: Tue, 18 Apr 2017 15:27:16 -0600 Subject: [PATCH 834/835] fix psr2 formatting --- lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php b/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php index b5b844866..834bfd4a6 100644 --- a/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php +++ b/lib/OpenCloud/LoadBalancer/Resource/CertificateMapping.php @@ -102,7 +102,8 @@ protected function updateJson($params = array()) * * @var array|object $values */ - protected function setCertificateMapping($values) { + protected function setCertificateMapping($values) + { $this->populate($values); } } From 7696e62992a8a45db0822dae0ac8a8fbfa5c5561 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Tue, 18 Jul 2017 18:52:15 +0200 Subject: [PATCH 835/835] Fix invalid PHPDoc The classes were not referenced and thus the PHPDoc not valid as pointed out by PHPStorm and Phan when running on @nextcloud: ``` apps/files_external/lib/Lib/Storage/Swift.php:387 PhanUndeclaredClassMethod Call to method get from undeclared class \OpenCloud\ObjectStore\Resource\Client ``` Signed-off-by: Lukas Reschke --- lib/OpenCloud/ObjectStore/Resource/AbstractResource.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php index 37722e93d..17dd4f0f9 100644 --- a/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php +++ b/lib/OpenCloud/ObjectStore/Resource/AbstractResource.php @@ -19,7 +19,9 @@ use Guzzle\Http\Message\Response; use OpenCloud\Common\Base; +use OpenCloud\Common\Http\Client; use OpenCloud\Common\Service\ServiceInterface; +use OpenCloud\ObjectStore\Service; /** * Abstract base class which implements shared functionality of ObjectStore @@ -36,7 +38,7 @@ abstract class AbstractResource extends Base /** @var string The FQCN of the metadata object used for the container. */ protected $metadataClass = 'OpenCloud\\Common\\Metadata'; - /** @var \OpenCloud\Common\Service\ServiceInterface The service object. */ + /** @var Service The service object. */ protected $service; public function __construct(ServiceInterface $service) @@ -230,7 +232,7 @@ public function retrieveMetadata() /** * To delete or unset a particular metadata item. * - * @param $key Metadata key to unset + * @param string $key Metadata key to unset * @return Response HTTP response returned from API operation to unset metadata item. */ public function unsetMetadataItem($key)

          g{+X1o#F%__L8 z`E<2ZudsJC0@!hSXep#8Nc}379xleqPI4lZCkoD``f@dr=k;lNk^m2|JG2?{HB!60 zOJj5`s?DBeG?_nsh}TKq zJ_H)%U?<%j^i&38kFzGqGj0{z!cRlZNUu52X{7XY2JA7noduGf!S73wi~aOWl$>MC z&ql?lIUsh`lH&DJyDY5XOR}t*_)`EOJqLBz z)MoR^V#f`rGY?K;$IQ%3p)ngdL0w`8)@elmhediWLtzG}N7jzA<4!p~{*UNJu3qE^ z^(lHDzaH2ry)GAgK0rKi4O};lf^th-FW}A#-74M0F9Yy}cD26{mAlOYwA&*mC1D9- zjhr&}B*i3O1Q0k;q7QM7kY0?+F}j&i?kDboYehE6hA{4vL48%a1&zDRrMSrtzt&YR zK^0FDBF`+a--_DN=7hPrdn%Jpb$TgxS|C^J={CH_^fEk$oCEbCkV8CPGdqXjBGtj( zDtgo$O_?Rb}yzLG3W{}cf>Fmc@;H&4yJbixVgl>i;1SK%qE4E$XHdNm5z)xxtm GQTQL%mCRuP literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/queues/messages.doctree b/doc/_build/doctrees/services/queues/messages.doctree new file mode 100644 index 0000000000000000000000000000000000000000..d7e276ff2dc56c4caa39a1f2bc9fe590b87805ec GIT binary patch literal 38569 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/queues/queues.doctree b/doc/_build/doctrees/services/queues/queues.doctree new file mode 100644 index 0000000000000000000000000000000000000000..773851323a38dd31c5f0c8439645215f30ebb5e7 GIT binary patch literal 19485 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 PdJhUAy%*2=NalY4OFqYp literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/volume/snapshots.doctree b/doc/_build/doctrees/services/volume/snapshots.doctree new file mode 100644 index 0000000000000000000000000000000000000000..41b745a3b5f56a370b0a249a1ab6f34e09939943 GIT binary patch literal 10012 zcmds7378y5b=I}VjI^u6k`R_;w{>_&LbGFA1{=vDNi2$;j136O9&BqV`^1astn)zfqAtiH&? z27l;V^>o+q>b?KH`s;O7-@2sg1l1^XJw5CP4%bX6scMp?NED6Nj=8la@?X&pqW%H-tzeB`45c ztQGXUA(l@~pO>)ShP*RJFNZ+|rn%CENxSCqLR|%u5TpwLtT1(cUOUGM@Ld5-rravm zg{i=UBMVJfhLsNGM%EYF6XZ_0iS_A36Sigf!s!b z$sGbuRpELLaFRPq@@ZXQ!$}^`%3S~^AIo6?V_bc>5k^`T0NVy}rr0Ppu^!V}o+n_y z`8i>g#5pCgr6jhN#JQzNU~qvU1qQlczDthq83g?9dBQKes!647UqRz0a!m*xZ+Iugrgk)bqk zuIQRLmqCw<5`%@YbYI7S4NIFcFBiK{BIpVjcV#S}O~#SX5o6qZU=YLhgtF@*E3blX z`(t@E>GnqGw$BtxG1#%jRZugt`wB0J9P4r~s2;NphXEvlB9bs2 z&R!i%^C_-&S+41fFbk?K*ut@<-B5rtk~rRF*}h}V29Z^ZX!h56)g7O0R%bEU8!Qv} zd`}A`mT8(G50HbNvk+c=sSgh30N4^^oxBDJT^q|H8JrYB?Ea`SA0y8Vxw5^iybcH* ziskjxVFhHg<3mH=&^ZJ$eti zJh_0Eqfq?0v3wq3@Z?i3aUegb56cw_(yV+wJb5&hC9>jE0J%`GihSB~eI431bluZEzIrMo6SP-VUEB?`0hJ&pw%Nk zl0yUNnh&5Ei9j2JwS`LSHC2f;gQ+Ayp>Wa48l9K$0 z>tuKA*xNQk1{y(?7%yKyp<2#Stohszv>cmb{hsYlM3}eI9$gANu;#GLOwHS)>o%6V z3hS>cP8M=mCB6mgCiI&S@A7ndISATcPCO+?;E%_0BAu~Elf{BqQ^eAxNP0bx8{6NV z@*KMQ(8*FB>k<)$b-kxhKyooW0Y;1}5rD1+eu(5^xEj<@Q$1$IfQU zF5w_r&0ZDDSCf_$za8t6V|&Q$+m@xFo6{BQfD;dyl_FKwEDf*8iLp7+Tn9u&EJXP` zQ-bk3#U^Go)oW?OYdv*u;J1-Eac(g z6rnff1Tp5DV)A7#PT;& zkH5*#qyRSxBEh%C^0&zAvwOwj;yk@6fC71@ius!hZL3 zDD3wZqA=P8czY~=KgFSg!2X~klH?zm9ND z6M~X9Nx)d$n^uIb#L z{W4;8eLI_crDLcDn=}Rf101`6lz)Wdz8cG~QT&p`FSdB(>#_VN8vAaH-JI-t!Iy4Q z&AfOc8Rd3WSh=GF|?VNb`L8XiAzt5(~a9Cg%o&oku$I z$K>XpP++hg4|~%d4i3zomi(!B)K1T2aO7ZKKa-;k9BeG312Wck)4m&u2a5aEqQ#3A z(f0oKMlh+xC=l2VQcG|XtEIRmaGn*bW#(c2iN#v68a`7!)N%Ld^#J$EsP4H{I!og- zbca3jgpV{ zD6*`>t2jHNqf@oTl&rC%Fc=Sl&<{dh#@4(F)mGFTT+I4tKHzLpZNqb{w&R);z2Z*M zC-#c|qR5DcMOJJV%f$nFmF;UJ2sN(M5Gsmnpg9Wm#k9&9p*wJ8 z{V1TZwAyJ3pGJkN^lYsXc;&R0R=ZGKQhBOe-J!LSH*9JPRJ(Rw*RpAFo@sDCHOOGn z8xXX*z?AOBmAubZq_EM(y}A&^MYg7COL}6h)zeM4XW&ZO*8_!JVs(*e-tRgM952T6 z!DVcqU0JQ3X}a#At^=taiH3Vo7OP9BW|iwZuEJqpIpHR)E;ZdosIp)4FvR|vR?os? z+b%Lr3u#N+$=+yt5xzrEm!UpT`*4lb<R}Mzy9bwtN=ERu~`92@d<;UOov0JVOH2) z*l2b=HS1vm4gHeSdvyb~6XzA#CU`thW#AMza7H7pX zsJO@Bm1sgSlEhZ=T7^@@%z9i4&O>!0%IDbTAPVbIcry7nk)U2uts1IPdKjQD5&WoD zo}p={o{KU#b)^&4>g8qxYxO*oV>03k0@oi88jYWyG#;F`l~1-nF1KsysA;>7+&1od z#`$G@xB*iT?j=;>%!=3Z+t0huQ7@p!)qKVUoVJX+6Kc$eG(Jag zPr2H~VYaeO={lUb*vHDehL2&&wu4U^=z9rOLFEay8K%~nY|sj$vN^U_=KP5Bx^58t$y?;jedOcjuQY^v!^1aAY{{&Zs&yqZwyc%G0hB3MEl;u%8Wf2B}iiu!kMF3My&3?@Jem z5Km$wTZ!x^#Frd2A8{@jstDyLSZ0EQ4{h?*6bg!L(8N^>#c5NzLf5c>lx2j)M5`H; z&9M~}K27eJMHxH0!5!1n)5G=IPz1i=4u?+_&{!AL3#k=W06(;6%EFxx9{z{wW-4Ch z1^$G35#4U;L~q^{jL~G$OpQTeV4UNoaJ`s1FL51p3*8K2F0{M-Rus;$jmfmf$4`cq z0Bb19jZb*gaWugsYWTxU4y|rOVTr9bGX_F`ZnyfgO|=zRU#ig@FJinro{iuZlr_kG{@fwz9IdS-WKcZ1Fs82kX+%s+79&wC{84wbs zEvC}QDx=ELF<|o8l#TG-_p^^!WzGju{p^i%`a7(DMNiQbA^o!0D?oh7l73EWkp6V;F_y0ZBvl?ZgY ziC7i*p`?+qPAF>x5$#r;3e=pOU(5|09>^S>0%k^2+LKqCZBSj21=W5VYXvo|j#XRK zR<%uZSphm#0q$vebwWw)D5;$#wX38~DkTc5s>WLPSiIDJIfrOIeX0I4uDh1*nbN(m zxZ<@&lZu;E6nkrLN@rqNbSywn+j;GKx2;Y4)CLy-=q%OiGK#Jyiu>dh+je$J=df)Y z{+BKZ|NBDw-poK{EjzbOd&9DMo#(03kD%y&u;l!dE?`So>Dktj#Y6;tEVUbyD?wD9 z&e4UycTr0BXM8sS-_9(GEcedY&dx@3jh?eG&L-qX&dTK`BQ4#ka`J6Lu5nu4N1Us2 z4*nE&?{>~bpu4r!ug+z48J-_RiFak@^eZoy*$!yDGY)5WA(t+YB-XTm~TwEv-5 z0FS5iFb)vT?iVbd-5(AIUEUhSk7!$>5ykV0uLQAop{)e79+^@Ziz?PUzHBISI8AA{ zu5vViFm_X_FiICFFP?M`Mu{^WMzc<=q;v5FPBhtmAf4hQ1}7IjPE0HW_r!#QQ%*&m z6Gu5Mks#%abo>ypUbDxG6NQc+?^`fe^v&ZSY=)G)Dka4R z-fd}sMNT_HCdiQw@(!dl&AJDGtep=wDOhR}RB}?U!I~Uo78s-uP@|OUP-j!;0XPrI z8~f8{9wJ2N>EkCt=;){>4TUD9nDHIGSY|YD1$V^ew@;3!_^>Sgz3B?$rr#3`)38R85UX2e(Gxky4&_C6Hb{rZSHRfOWm5B&ChR9IxlQnTWyDx5M^DD0 z*QfLprtNrWnwaoC>}=lbS)ke*tK)mPclndP)bvzXb6w+_(KoM&p2onQz6^iGCXQ!V zAKj4BGno`tbA2NXTJE2YGw}Gq2Y6 z1(ckYPW3avT8t+>v=vG1a8oFs^EOF-oh<^24Sh`t&3TF+fsuT zSbV!>@%EJ7fprzj-qy^{BGXELx6gF4VnWD$$o2Rz1=-^C3HOj~`YT_vw8~Rp>zAbu-!3YIEv77@MS!?C(=^|71x3S{aEyYfvRNH^pivA0mwR= zAW9q9NYDPi;+KyE>BjHDf&}Utdq5hWWOE^ zq)}4F^ykB3Ym~(GBtDYPzo2OVHmWh*2}p2lnAFO19N_d(nzm`j;4#b3Pg| zi0Z^NxcN7@>8?iMq(4RfY4{2OH{%;WjmECKfJ1j7`gE@yUAxF9x(~ymD~TsZ&(t}7&NWma z;PvWElOMLaQTOw-J}WY6{3*SL-`AEs-?$Z!o-wEgtZW~4J4)6`e@?IEX%LLvN_p1z zaQ|C}k+EJe&>nhvkSBJD{TBh!5I^@c8fJPu-qcpH76~9m;^ut>Vq?7lDo zWywz8)7$uA0Ab(i^X(WsEQYgfECXqC2{?^axzYR54yItgG@M~~gVD!hup~Cwt>K4g zBiN>;4B2g-J^^13iPh-wVaH}eJ21pa+Va>;wR$J(PAX#K;`74xEA=iG>ag85eIlMy zeG-22Y6$JH^{BNssJ)38YU`WYGfU^~>@NAZ&CJ@`#3d;SZD8?s;k literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/volume/volumes.doctree b/doc/_build/doctrees/services/volume/volumes.doctree new file mode 100644 index 0000000000000000000000000000000000000000..2fc63a76cc46158890764e65eb7109663663668d GIT binary patch literal 13693 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/using-php-5.3.doctree b/doc/_build/doctrees/using-php-5.3.doctree new file mode 100644 index 0000000000000000000000000000000000000000..dfb43ad20a37d8d1484865259f96e1570ce475c0 GIT binary patch literal 4656 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 literal 0 HcmV?d00001 diff --git a/doc/_build/html/.buildinfo b/doc/_build/html/.buildinfo new file mode 100644 index 000000000..5ba10c9e0 --- /dev/null +++ b/doc/_build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: da7e999e4172075fe9536fec2c0e5662 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/doc/_build/html/_sources/auth.txt b/doc/_build/html/_sources/auth.txt new file mode 100644 index 000000000..ce5ba7233 --- /dev/null +++ b/doc/_build/html/_sources/auth.txt @@ -0,0 +1,54 @@ +Authentication +============== + +The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary. + +You can force authentication, by calling: + +.. code-block:: php + + $client->authenticate(); + +If the credentials are incorrect, a ``401`` error will be returned. If +credentials are correct, a ``200`` status is returned with your Service +Catalog. + + +Service Catalog +--------------- + +The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the ``Catalog`` +object, which allows you greater control and interactivity. + +.. code-block:: php + + /** @var OpenCloud\Common\Service\Catalog */ + $catalog = $client->getCatalog(); + + // Return a list of OpenCloud\Common\Service\CatalogItem objects + foreach ($catalog->getItems() as $catalogItem) { + + $name = $catalogItem->getName(); + $type = $catalogItem->getType(); + + if ($name == 'cloudServersOpenStack' && $type == 'compute') { + break; + } + + // Array of OpenCloud\Common\Service\Endpoint objects + $endpoints = $catalogItem->getEndpoints(); + foreach ($endpoints as $endpoint) { + if ($endpoint->getRegion() == 'DFW') { + echo $endpoint->getPublicUrl(); + } + } + } + +As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs. diff --git a/doc/_build/html/_sources/caching-creds.txt b/doc/_build/html/_sources/caching-creds.txt new file mode 100644 index 000000000..4eb5be5c8 --- /dev/null +++ b/doc/_build/html/_sources/caching-creds.txt @@ -0,0 +1,44 @@ +Caching credentials +=================== + +You can speed up your API operations by caching your credentials in a +(semi-)permanent location, such as your DB or local filesystem. This +enable subsequent requests to access a shared resource, instead of +repetitively having to re-authenticate on every thread of execution. + +Tokens are valid for 24 hours, so you can effectively re-use the same +cached value for that period. If you try to use a cached version that +has expired, an authentication request will be made. + +Filesystem example +------------------ + +In this example, credentials will be saved to a file in the local +filesystem. Be sure to exclude it from your VCS. + +.. code-block:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => 'foo', + 'apiKey' => 'bar' + )); + + $cacheFile = __DIR__ . '/.opencloud_token'; + + // If the cache file exists, try importing it into the client + if (file_exists($cacheFile)) { + $data = unserialize(file_get_contents($cacheFile)); + $client->importCredentials($data); + } + + $token = $client->getTokenObject(); + + // If no token exists, or the current token is expired, re-authenticate and save the new token to disk + if (!$token || ($token && $token->hasExpired())) { + $client->authenticate(); + file_put_contents($cacheFile, serialize($client->exportCredentials())); + } + +In tests, the above code shaved about 1-2s off the execution time. diff --git a/doc/_build/html/_sources/customizing-clients.txt b/doc/_build/html/_sources/customizing-clients.txt new file mode 100644 index 000000000..ab3b6a441 --- /dev/null +++ b/doc/_build/html/_sources/customizing-clients.txt @@ -0,0 +1,144 @@ +Customizing Clients +=================== + +Logger injection +---------------- + +As the ``Rackspace`` client extends the ``OpenStack`` client, they both support +passing ``$options`` as an array via the constructor's third parameter. The +options are passed as a config to the `Guzzle` client, but also allow to inject +your own logger. + +Your logger should implement the ``Psr\Log\LoggerInterface`` `as defined in +PSR-3 `_. +One example of a compatible logger is `Monolog `_. +When the client does create a service, it will inject the logger if one is +available. + +To inject a ``LoggerInterface`` compatible logger into a new client: + +.. code-block:: php + + use Monolog\Logger; + use OpenCloud\OpenStack; + + // create a log channel + $logger = new Logger('name'); + + $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' + ), array( + 'logger' => $logger, + )); + + +Authentication +-------------- + +The client does not automatically authenticate against the API when it is +instantiated - it waits for an API call. When this happens, it checks +whether the current “token” has expired, and (re-)authenticates if +necessary. + +You can force authentication, by calling: + +.. code-block:: php + + $client->authenticate(); + +If the credentials are incorrect, a ``401`` error will be returned. If +credentials are correct, a ``200`` status is returned with your Service +Catalog. + + +Service Catalog +--------------- + +The Service Catalog is returned on successful authentication, and is +composed of all the different API services available to the current +tenant. All of this functionality is encapsulated in the ``Catalog`` +object, which allows you greater control and interactivity. + +.. code-block:: php + + /** @var OpenCloud\Common\Service\Catalog */ + $catalog = $client->getCatalog(); + + // Return a list of OpenCloud\Common\Service\CatalogItem objects + foreach ($catalog->getItems() as $catalogItem) { + + $name = $catalogItem->getName(); + $type = $catalogItem->getType(); + + if ($name == 'cloudServersOpenStack' && $type == 'compute') { + break; + } + + // Array of OpenCloud\Common\Service\Endpoint objects + $endpoints = $catalogItem->getEndpoints(); + foreach ($endpoints as $endpoint) { + if ($endpoint->getRegion() == 'DFW') { + echo $endpoint->getPublicUrl(); + } + } + } + +As you can see, you have access to each Service’s name, type and list of +endpoints. Each endpoint provides access to the specific region, along +with its public and private endpoint URLs. + + +Default HTTP headers +-------------------- + +To set default HTTP headers: + +.. code-block:: php + + $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); + + +User agents +----------- + +php-opencloud will send a default ``User-Agent`` header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format: + + User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz + +where ``xxx`` is the current version of the SDK, ``yyy`` is the current +version of cURL, and ``zzz`` is the current PHP version. To override +this default, you must run: + +.. code-block:: php + + $client->setUserAgent('MyCustomUserAgent'); + +which will result in: + + User-Agent: MyCustomUserAgent + +If you want to set a *prefix* for the user agent, but retain the default +``User-Agent`` as a suffix, you must run: + +.. code-block:: php + + $client->setUserAgent('MyPrefix', true); + +which will result in: + + User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + +where ``$client`` is an instance of ``OpenCloud\OpenStack`` or +``OpenCloud\Rackspace``. + + +Other functionality +------------------- + +For a full list of functionality provided by Guzzle, please consult the +`official documentation`_. + +.. _official documentation: http://docs.guzzlephp.org/en/latest/http-client/client.html diff --git a/doc/_build/html/_sources/debugging.txt b/doc/_build/html/_sources/debugging.txt new file mode 100644 index 000000000..5d35d5bc1 --- /dev/null +++ b/doc/_build/html/_sources/debugging.txt @@ -0,0 +1,103 @@ +Debugging +========= + +There are two important debugging strategies to use when encountering +problems with HTTP transactions. + +Strategy 1: Meaningful exception handling +----------------------------------------- + +If the API returns a ``4xx`` or ``5xx`` status code, it indicates that +there was an error with the sent request, meaning that the transaction +cannot be adequately completed. + +The Guzzle HTTP component, which forms the basis of our SDK's transport +layer, utilizes `numerous exception +classes `__ +to handle this error logic. + +The two most common exception classes are: + +- ``Guzzle\Http\Exception\ClientErrorResponseException``, which is + thrown when a ``4xx`` response occurs + +- ``Guzzle\Http\Exception\ServerErrorResponseException``, which is + thrown when a ``5xx`` response occurs + +Both of these classes extend the base ``BadResponseException`` class. + +This provides you with the granularity you need to debug and handle +exceptions. + +An example with Swift +~~~~~~~~~~~~~~~~~~~~~ + +If you're trying to retrieve a Swift resource, such as a Data Object, +and you're not completely certain that it exists, it makes sense to wrap +your call in a try/catch block: + +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + return $service->getObject('foo.jpg'); + } catch (ClientErrorResponseException $e) { + if ($e->getResponse()->getStatusCode() == 404) { + // Okay, the resource does not exist + return false; + } + } catch (\Exception $e) { + // Some other exception was thrown... + } + + +Both ``ClientErrorResponseException`` and +``ServerErrorResponseException`` have two methods that allow you to +access the HTTP transaction: + +.. code-block:: php + + // Find out the faulty request + $request = $e->getRequest(); + + // Display everything by casting as string + echo (string) $request; + + // Find out the HTTP response + $response = $e->getResponse(); + + // Output that too + echo (string) $response; + + +Strategy 2: Wire logging +------------------------ + +Guzzle provides a `Log +plugin `__ +that allows you to log everything over the wire, which is useful if you +don't know what's going on. + +Here's how you enable it: + +Install the plugin +~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + composer require guzzle/guzzle + + +Add to your client +~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use Guzzle\Plugin\Log\LogPlugin; + + $client->addSubscriber(LogPlugin::getDebugPlugin()); + + +The above will add a generic logging subscriber to your client, which +will output every HTTP transaction to `STDOUT`. diff --git a/doc/_build/html/_sources/getting-started-with-openstack.txt b/doc/_build/html/_sources/getting-started-with-openstack.txt new file mode 100644 index 000000000..da6c6ee24 --- /dev/null +++ b/doc/_build/html/_sources/getting-started-with-openstack.txt @@ -0,0 +1,233 @@ +Getting Started with OpenStack +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Nova instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against Keystone: + +.. code-block:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('http://my-openstack.com:35357/v2.0/', array( + 'username' => 'foo', + 'password' => 'bar', + 'tenantName' => 'baz' + )); + +You will need to substitute in the public URL endpoint for your Keystone +service, as well as your ``username``, ``password`` and ``tenantName``. +You can also specify your ``tenantId`` instead of ``tenantName`` if you +prefer. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Nova service: + +.. code-block:: php + + $compute = $client->computeService('nova', 'regionOne'); + + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. For OpenStack users, this must be retrieved +and entered in your code. If you are unsure how to retrieve the service +name, follow these steps: + +1. Setup the ``$client`` object, as above +2. Copy and run this code: + +.. code-block:: php + + $client->authenticate(); + print_r($client->getCatalog()->getItems()); + + +3. This will output all the items in your service catalog. Go through + the outputted list and find your service, making note of the "name" + field. This is the name you will need to enter as the first argument. + You will also be able to see the available regions. + +The second argument is the region. The third and last argument is the +type of URL; you may use either ``publicURL`` or ``internalURL``. + + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instances are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of an instance is its +hardware configuration. So if you want a 2GB instance but don't know the +ID, you have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + + use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +As you can see, you're creating a server called "My lovely server" - +this will take a few minutes for the build to complete. You can always +check the progress by logging into your Controller node and running: + +.. code-block:: bash + + nova list + +You can also execute a polling function immediately after the ``create`` +method that checks the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +More fun with Nova +------------------ + +Once you've booted up your instance, you can use other API operations to +monitor your Compute nodes. To list every node on record, you can +execute: + +.. code-block:: php + + $servers = $compute->serverList(); + + foreach ($servers as $server) { + // do something with each server... + echo $server->name, PHP_EOL; + } + +or, if you know a particular instance ID you can retrieve its details: + +.. code-block:: php + + $server = $compute->server('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'); + +allowing you to update its properties: + +.. code-block:: php + + $server->update(array( + 'name' => 'New server name' + )); + +or delete it entirely: + +.. code-block:: php + + $server->delete(); + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. diff --git a/doc/_build/html/_sources/getting-started-with-rackspace.txt b/doc/_build/html/_sources/getting-started-with-rackspace.txt new file mode 100644 index 000000000..73aa3a8d4 --- /dev/null +++ b/doc/_build/html/_sources/getting-started-with-rackspace.txt @@ -0,0 +1,185 @@ +Getting Started with Rackspace +============================== + +Installing the SDK +------------------ + +You must install through Composer, because this library has a few +dependencies: + +.. code-block:: bash + + composer require rackspace/php-opencloud + +Once you have installed the library, you will need to load Composer's +autoloader (which registers all the required namespaces): + +.. code-block:: php + + require 'vendor/autoload.php'; + +And you're good to go! + + +Quick deep-dive: building some Nova instances +--------------------------------------------- + +In this example, you will write code that will create a Cloud Servers instance +running Ubuntu. + + +1. Setup the client and pass in your credentials +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To authenticate against the Rackspace API and use its services: + +.. code-block:: php + + 'foo', + 'apiKey' => 'bar' + )); + +You can see in the first example that the constant +``Rackspace::US_IDENTITY_ENDPOINT`` is just a string representation of +Rackspace's identity endpoint +(``https://identity.api.rackspacecloud.com/v2.0/``). Another difference +is that Rackspace uses API key for authentication, whereas OpenStack +uses a generic password. + + +2. Pick what service you want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this case, we want to use the Compute (Nova) service: + +.. code-block:: php + + $compute = $client->computeService(null, 'ORD'); + +The first argument is the **name** of the service as it appears in the +OpenStack service catalog. If in doubt, you can leave blank and it will +revert to the default name for the service. The second argument is the +region; you may use: + +- **DFW** (Dallas) +- **ORD** (Chicago) +- **IAD** (Virginia) +- **LON** (London) +- **HKG** (Hong Kong) +- **SYD** (Sydney) + +The third and last argument is the type of URL; you may use either +``publicURL`` or ``internalURL``. If you select ``internalUrl`` all API +traffic will use ServiceNet (internal IPs) and will receive a +performance boost. + +3. Select your server image +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Servers are based on "images", which are effectively just the type of +operating system you want. Let's go through the list and find an Ubuntu +one: + +.. code-block:: php + + $images = $compute->imageList(); + + foreach ($images as $image) { + if (strpos($image->name, 'Ubuntu') !== false) { + $ubuntu = $image; + break; + } + } + +Alternatively, if you already know the image ID, you can do this much +easier: + +.. code-block:: php + + $ubuntu = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + + +4. Select your flavor +--------------------- + +There are different server specs - some which offer 1GB RAM, others +which offer a much higher spec. The 'flavor' of a server is its hardware +configuration. So if you want a 2GB instance but don't know the ID, you +have to traverse the list: + +.. code-block:: php + + $flavors = $compute->flavorList(); + + foreach ($flavors as $flavor) { + if (strpos($flavor->name, '2GB') !== false) { + $twoGbFlavor = $flavor; + break; + } + } + +Again, it's much easier if you know the ID: + +.. code-block:: php + + $twoGbFlavor = $compute->flavor('4'); + +5. Thunderbirds are go! +----------------------- + +Okay, you're ready to spin up a server: + +.. code-block:: php + + use Guzzle\Http\Exception\BadResponseException; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntu, + 'flavor' => $twoGbFlavor + )); + } catch (BadResponseException $e) { + // No! Something failed. Let's find out: + printf("Request: %s\n\nResponse: %s", $e->getRequest(), $e->getResponse()); + } + +You can also call a polling function that checks on the build process: + +.. code-block:: php + + use OpenCloud\Compute\Constants\ServerState; + + $callback = function($server) { + if (!empty($server->error)) { + var_dump($server->error); + exit; + } else { + echo sprintf( + "Waiting on %s/%-12s %4s%%", + $server->name(), + $server->status(), + isset($server->progress) ? $server->progress : 0 + ); + } + }; + + $server->waitFor(ServerState::ACTIVE, 600, $callback); + +So, the server will be polled until it is in an ``ACTIVE`` state, with a +timeout of 600 seconds. When the poll happens, the callback function is +executed - which in this case just logs some output. + +Next steps +---------- + +Read our docs for the `Compute v2 `_ service. diff --git a/doc/_build/html/_sources/http-clients.txt b/doc/_build/html/_sources/http-clients.txt new file mode 100644 index 000000000..49828509b --- /dev/null +++ b/doc/_build/html/_sources/http-clients.txt @@ -0,0 +1,56 @@ +HTTP Clients +============ + +Default HTTP headers +-------------------- + +To set default HTTP headers: + +.. code-block:: php + + $client->setDefaultOption('headers/X-Custom-Header', 'FooBar'); + + +User agents +----------- + +php-opencloud will send a default ``User-Agent`` header for every HTTP +request, unless a custom value is provided by the end-user. The default +header will be in this format: + + User-Agent: OpenCloud/xxx cURL/yyy PHP/zzz + +where ``xxx`` is the current version of the SDK, ``yyy`` is the current +version of cURL, and ``zzz`` is the current PHP version. To override +this default, you must run: + +.. code-block:: php + + $client->setUserAgent('MyCustomUserAgent'); + +which will result in: + + User-Agent: MyCustomUserAgent + +If you want to set a *prefix* for the user agent, but retain the default +``User-Agent`` as a suffix, you must run: + +.. code-block:: php + + $client->setUserAgent('MyPrefix', true); + +which will result in: + + User-Agent: MyPrefix OpenCloud/xxx cURL/yyy PHP/zzz + +where ``$client`` is an instance of ``OpenCloud\OpenStack`` or +``OpenCloud\Rackspace``. + + +Other functionality +------------------- + +For a full list of functionality provided by Guzzle, please consult the +`official documentation`_. + +.. _official documentation: http://docs.guzzlephp.org/en/latest/http-client/client.html diff --git a/doc/_build/html/_sources/index.txt b/doc/_build/html/_sources/index.txt new file mode 100644 index 000000000..3c47091cc --- /dev/null +++ b/doc/_build/html/_sources/index.txt @@ -0,0 +1,80 @@ +Welcome to php-opencloud! +========================= + +Installation +------------ + +You must install this library through Composer: + +.. code-block:: bash + + composer require rackspace/php-opencloud + + +If you do not have Composer installed, please consult the `official docs +`_. + +Once you have installed the library, you will need to load Composer's autoloader +(which registers all the required namespaces). To do this, place the following +line of PHP code at the top of your application's PHP files: + +.. code-block:: php + + require 'vendor/autoload.php'; + +This assumes your application's PHP files are located in the same folder as +``vendor/``. If your files are located elsewhere, please supply the path to +``vendor/autoload.php`` in the require statement above. + +Read the :doc:`getting-started-with-openstack` or +:doc:`getting-started-with-rackspace` to help you get started with basic +Compute operations. + +.. note:: + + If you are running PHP 5.3, please see our :doc:`using-php-5.3` guide. + +Services +-------- + +.. toctree:: + :glob: + :maxdepth: 1 + + services/**/index + +Usage tips +---------- + +.. toctree:: + :maxdepth: 1 + + debugging + caching-creds + iterators + regions + url-types + logging + http-clients + auth + +Help and support +---------------- + +If you have specific problems or bugs with this SDK, please file an issue on +our official `Github `_. We also +have a `mailing list `_, +so feel free to join to keep up to date with all the latest changes and +announcements to the library. + +For general feedback and support requests, send an email to +sdk-support@rackspace.com. + +You can also find assistance via IRC on #rackspace at freenode.net. + +Contributing +------------ + +If you'd like to contribute to the project, or require help running the +unit/acceptance tests, please view the `contributing guidelines +`_. diff --git a/doc/_build/html/_sources/iterators.txt b/doc/_build/html/_sources/iterators.txt new file mode 100644 index 000000000..bef59e339 --- /dev/null +++ b/doc/_build/html/_sources/iterators.txt @@ -0,0 +1,174 @@ +Iterators +========= + +Iterators allow you to traverse over collections of your resources in an +efficient and easy way. Currently there are two Iterators provided by +the SDK: + +- **ResourceIterator**. The standard iterator class that implements + SPL's standard + `Iterator `__, + `ArrayAccess `__ + and `Countable `__ + interfaces. In short, this allows you to traverse this object (using + ``foreach``), count its internal elements like an array (using + ``count`` or ``sizeof``), and access its internal elements like an + array (using ``$iterator[1]``). + +- **PaginatedIterator**. This is a child of ResourceIterator, and as + such inherits all of its functionality. The difference however is + that when it reaches the end of the current collection, it attempts + to construct a URL to access the API based on predictive paginated + collection templates. + +Common behaviour +---------------- + +.. code-block:: php + + $iterator = $computeService->flavorList(); + +There are two ways to traverse an iterator. The first is the longer, +more traditional way: + +.. code-block:: php + + while ($iterator->valid()) { + $flavor = $iterator->current(); + + // do stuff.. + echo $flavor->id; + + $iterator->next(); + } + +There is also a shorter and more intuitive version: + +.. code-block:: php + + foreach ($iterator as $flavor) { + // do stuff... + echo $flavor->id; + } + +Because the iterator implements PHP's native ``Iterator`` interface, it +can inherit all the native functionality of traversible data structures +with ``foreach``. + +Very important note +------------------- + +Until now, users have been expected to do this: + +.. code-block:: php + + while ($flavor = $iterator->next()) { + // ... + } + +which is **incorrect**. The single responsibility of ``next`` is to move +the internal pointer forward. It is the job of ``current`` to retrieve +the current element. + +For your convenience, these two Iterator classes are fully backward +compatible: they exhibit all the functionality you'd expect from a +correctly implemented iterator, but they also allow previous behaviour. + +Using paginated collections +--------------------------- + +For large collections, such as retrieving DataObjects from +CloudFiles/Swift, you need to use pagination. Each resource will have a +different limit per page; so once that page is traversed, there needs to +be another API call to retrieve to *next* page's resources. + +There are two key concepts: + +- **limit** is the amount of resources returned per page +- **marker** is the way you define a starting point. It is some form of + identifier that allows the collection to begin from a specific + resource + +Resource classes +~~~~~~~~~~~~~~~~ + +When the iterator returns a current element in the internal list, it +populates the relevant resource class with all the data returned to the +API. In most cases, a ``stdClass`` object will become an instance of +``OpenCloud\Common\PersistentObject``. + +In order for this instantiation to happen, the ``resourceClass`` option +must correspond to some method in the parent class that creates the +resource. For example, if we specify 'ScalingPolicy' as the +``resourceClass``, the parent object (in this case +``OpenCloud\Autoscale\Group``, needs to have some method will allows the +iterator to instantiate the child resource class. These are all valid: + +1. ``Group::scalingGroup($data);`` + +2. ``Group::getScalingGroup($data);`` + +3. ``Group::resource('ScalingGroup', $data);`` + +where ``$data`` is the standard object. This list runs in order of +precedence. + +Setting up a PaginatedIterator +------------------------------ + +.. code-block:: php + + use OpenCloud\Common\Collection\PaginatedIterator; + + $service = $client->computeService(); + + $flavors = PaginatedIterator::factory($service, array( + 'resourceClass' => 'Flavor', + 'baseUrl' => $service->getUrl('flavors') + 'limit.total' => 350, + 'limit.page' => 100, + 'key.collection' => 'flavors' + )); + + foreach ($flavors as $flavor) { + echo $flavor->getId(); + } + +As you can see, there are a lot of configuration parameters to pass in - +and getting it right can be quite fiddly, involving a lot of API +research. For this reason, using the convenience methods like +``flavorList`` is recommended because it hides the complexity. + +PaginatedIterator options +~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are certain configuration options that the paginated iterator +needs to work. These are: + ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| Name | Description | Type | Required | Default | ++=========================+===================================================================================================================================================================================================================================================+==============================+============+===============+ +| resourceClass | The resource class that is instantiated when the current element is retrieved. This is relative to the parent/service which called the iterator. | string | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| baseUrl | The base URL that is used for making new calls to the API for new pages | ``Guzzle\Http\Url`` | Yes | - | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.total | The total amount of resources you want to traverse in your collection. The iterator will stop as this limit is reached, regardless if there are more items in the list | int | No | 10000 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| limit.page | The amount of resources each page contains | int | No | 100 | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.links | Often, API responses will contain "links" that allow easy access to the next page of a resource collection. This option specifies what that JSON element is called (its key). For example, for Rackspace Compute images it is ``images_links``. | string | No | links | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collection | The top-level key for the array of resources. For example, servers are returned with this data structure: ``{"servers": [...]}``. The **key.collection** value in this case would be ``servers``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.collectionElement | Rarely used. But it indicates the key name for each nested resource element. KeyPairs, for example, are listed like this: ``{"keypairs": [ {"keypair": {...}} ] }``. So in this case the collectionElement key would be ``keypair``. | string | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| key.marker | The value used as the marker. It needs to represent a valid property in the JSON resource objects. Often it is ``id`` or ``name``. | string | No | name | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.method | The HTTP method used when making API calls for new pages | string | No | GET | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.headers | The HTTP headers to send when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.body | The HTTP entity body to send when making API calls for new pages | ``Guzzle\Http\EntityBody`` | No | ``null`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ +| request.curlOptions | Additional cURL options to use when making API calls for new pages | array | No | ``array()`` | ++-------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------+------------+---------------+ diff --git a/doc/_build/html/_sources/logging.txt b/doc/_build/html/_sources/logging.txt new file mode 100644 index 000000000..bb2a3de09 --- /dev/null +++ b/doc/_build/html/_sources/logging.txt @@ -0,0 +1,33 @@ +Logging +======= + +Logger injection +---------------- + +As the ``Rackspace`` client extends the ``OpenStack`` client, they both support +passing ``$options`` as an array via the constructor's third parameter. The +options are passed as a config to the `Guzzle` client, but also allow to inject +your own logger. + +Your logger should implement the ``Psr\Log\LoggerInterface`` `as defined in +PSR-3 `_. +One example of a compatible logger is `Monolog `_. +When the client does create a service, it will inject the logger if one is +available. + +To inject a ``LoggerInterface`` compatible logger into a new client: + +.. code-block:: php + + use Monolog\Logger; + use OpenCloud\OpenStack; + + // create a log channel + $logger = new Logger('name'); + + $client = new OpenStack('http://identity.my-openstack.com/v2.0', array( + 'username' => 'foo', + 'password' => 'bar' + ), array( + 'logger' => $logger, + )); diff --git a/doc/_build/html/_sources/regions.txt b/doc/_build/html/_sources/regions.txt new file mode 100644 index 000000000..42c14595c --- /dev/null +++ b/doc/_build/html/_sources/regions.txt @@ -0,0 +1,20 @@ +Rackspace regions +================= + +Below are the supported regions on the Rackspace network: + ++------+-----------+ +| code | location | ++======+===========+ +| IAD | Virginia | ++------+-----------+ +| ORD | Chicago | ++------+-----------+ +| DFW | Dallas | ++------+-----------+ +| LON | London | ++------+-----------+ +| SYD | Sydney | ++------+-----------+ +| HKG | Hong Kong | ++------+-----------+ diff --git a/doc/_build/html/_sources/services/autoscale/group-config.txt b/doc/_build/html/_sources/services/autoscale/group-config.txt new file mode 100644 index 000000000..5dcfb6ce4 --- /dev/null +++ b/doc/_build/html/_sources/services/autoscale/group-config.txt @@ -0,0 +1,59 @@ +Group configurations +==================== + +Setup +----- + +In order to interact with the functionality of a group's configuration, +you must first retrieve the details of the group itself. To do this, you must +substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get group configuration +----------------------- + +.. code-block:: php + + /** @var */ + $groupConfig = $group->getGroupConfig(); + + +Edit group configuration +------------------------ + +.. code-block:: php + + $groupConfig->update(array( + 'name' => 'New name!' + )); + + +Get launch configuration +------------------------ + +.. code-block:: php + + /** @var */ + $launchConfig = $group->getLaunchConfig(); + + +Edit group/launch configuration +------------------------------- + +.. code-block:: php + + $launchConfig = $group->getLaunchConfig(); + + $server = $launchConfig->args->server; + $server->name = "BRAND NEW SERVER NAME"; + + $launchConfig->update(array + 'args' => array( + 'server' => $server, + 'loadBalancers' => $launchConfig->args->loadBalancers + ) + )); diff --git a/doc/_build/html/_sources/services/autoscale/groups.txt b/doc/_build/html/_sources/services/autoscale/groups.txt new file mode 100644 index 000000000..a6caf3f9d --- /dev/null +++ b/doc/_build/html/_sources/services/autoscale/groups.txt @@ -0,0 +1,81 @@ +Groups +====== + +List all groups +--------------- + +.. code-block:: php + + $groups = $service->groupList(); + foreach ($group as $group) { + /** @var $group OpenCloud\Autoscale\Resources\Group */ + } + +Please consult the `iterator guide `__ for more information about +iterators. + + +Retrieve group by ID +-------------------- + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Create a new group +------------------ + +.. code-block:: php + + // Set the config object for this autoscale group; contains all of properties + // which determine its behaviour + $groupConfig = array( + 'name' => 'new_autoscale_group', + 'minEntities' => 5, + 'maxEntities' => 25, + 'cooldown' => 60, + ); + + // We need specify what is going to be launched. For now, we'll launch a new server + $launchConfig = array( + 'type' => 'launch_server', + 'args' => array( + 'server' => array( + 'flavorRef' => 3, + 'name' => 'webhead', + 'imageRef' => '0d589460-f177-4b0f-81c1-8ab8903ac7d8' + ), + 'loadBalancers' => array( + array('loadBalancerId' => 2200, 'port' => 8081), + ) + ) + ); + + // Do we want particular scaling policies? + $policy = array( + 'name' => 'scale up by 10', + 'change' => 10, + 'cooldown' => 5, + 'type' => 'webhook', + ); + + $group->create(array( + 'groupConfiguration' => $groupConfig, + 'launchConfiguration' => $launchConfig, + 'scalingPolicies' => array($policy), + )); + +Delete a group +-------------- + +.. code-block:: php + + $group->delete(); + +Get the current state of the scaling group +------------------------------------------ + +.. code-block:: php + + $group->getState(); diff --git a/doc/_build/html/_sources/services/autoscale/index.txt b/doc/_build/html/_sources/services/autoscale/index.txt new file mode 100644 index 000000000..1bfd20e68 --- /dev/null +++ b/doc/_build/html/_sources/services/autoscale/index.txt @@ -0,0 +1,69 @@ +Auto Scale v2 +============= + +.. include:: ../common/rs-only.sample.rst + +Auto Scale service +~~~~~~~~~~~~~~~~~~ + +Now to instantiate the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + groups + group-config + policies + webhooks + + +Glossary +-------- + +.. glossary:: + + group + The scaling group is at the heart of an Auto Scale deployment. The scaling + group specifies the basic elements of the Auto Scale configuration. It + manages how many servers can participate in the scaling group. It also + specifies information related to load balancers if your configuration uses + a load balancer. + + group configuration + Outlines the basic elements of the Auto Scale configuration. The group + configuration manages how many servers can participate in the scaling group. + It sets a minimum and maximum limit for the number of entities that can be + used in the scaling process. It also specifies information related to load + balancers. + + launch configuration + Creates a blueprint for how new servers will be created. The launch + configuration specifies what type of server image will be started on + launch, what flavor the new server is, and which load balancer the new + server connects to. + + policy + Auto Scale uses policies to define the scaling activity that will take + place, as well as when and how that scaling activity will take place. + Scaling policies specify how to modify the scaling group and its behavior. + You can specify multiple policies to manage a scaling group. + + webhook + A webhook is a reachable endpoint that when visited will execute a scaling + policy for a particular scaling group. + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ + - `API release history `_ diff --git a/doc/_build/html/_sources/services/autoscale/policies.txt b/doc/_build/html/_sources/services/autoscale/policies.txt new file mode 100644 index 000000000..e103d328e --- /dev/null +++ b/doc/_build/html/_sources/services/autoscale/policies.txt @@ -0,0 +1,81 @@ +Scaling Policies +================ + +Setup +----- + +In order to interact with the functionality of a group's scaling +policies, you must first retrieve the details of the group itself. To do this, +you must substitute `{groupId}` for your group's ID: + +.. code-block:: php + + $group = $service->group('{groupId}'); + + +Get all policies +---------------- + +.. code-block:: php + + $policies = $group->getScalingPolicies(); + + foreach ($policies as $policy) { + printf("Name: %s Type: %s\n", $policy->name, $policy->type); + } + + +Create new scaling policies +--------------------------- + +Creating policies is achieved through passing an array to the ``create`` +method. + +.. code-block:: php + + $policies = array( + array( + 'name' => 'NEW NAME', + 'change' => 1, + 'cooldown' => 150, + 'type' => 'webhook', + ) + ); + + $group->createScalingPolicies($policies); + + +Get an existing scaling policy +------------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + + +Update a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->update(array( + 'name' => 'More relevant name' + )); + + +Delete a scaling policy +----------------------- + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->delete(); + +Execute a scaling policy +------------------------ + +.. code-block:: php + + $policy = $group->getScalingPolicy('{policyId}'); + $policy->execute(); diff --git a/doc/_build/html/_sources/services/autoscale/service.sample.txt b/doc/_build/html/_sources/services/autoscale/service.sample.txt new file mode 100644 index 000000000..dc9a94fbd --- /dev/null +++ b/doc/_build/html/_sources/services/autoscale/service.sample.txt @@ -0,0 +1,9 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->autoscaleService(); + +.. include:: ../common/service-args.rst diff --git a/doc/_build/html/_sources/services/autoscale/webhooks.txt b/doc/_build/html/_sources/services/autoscale/webhooks.txt new file mode 100644 index 000000000..9133aa9ed --- /dev/null +++ b/doc/_build/html/_sources/services/autoscale/webhooks.txt @@ -0,0 +1,62 @@ +Webhooks +======== + +Setup +----- + +In order to interact with webhooks, you must first retrieve the +details of the group and scaling policy you want to execute: + +.. code-block:: php + + $group = $service->group('{groupId}'); + $policy = $group->getScalingPolicy('{policyId}'); + +Get all webhooks +---------------- + +.. code-block:: php + + $webhooks = $policy->getWebookList(); + +Create a new webhook +-------------------- + +.. code-block:: php + + $policy->createWebhooks(array( + array( + 'name' => 'Alice', + 'metadata' => array( + 'firstKey' => 'foo', + 'secondKey' => 'bar' + ) + ) + )); + +Get webhook +----------- + +.. code-block:: php + + $webhook = $policy->getWebhook('{webhookId}'); + +Update webhook +-------------- + +.. code-block:: php + + // Update the metadata + $metadata = $webhook->metadata; + $metadata->thirdKey = 'blah'; + $webhook->update(array( + 'metadata' => $metadata + )); + + +Delete webhook +-------------- + +.. code-block: php + + $webhook->delete(); diff --git a/doc/_build/html/_sources/services/common/clients.sample.txt b/doc/_build/html/_sources/services/common/clients.sample.txt new file mode 100644 index 000000000..a952da730 --- /dev/null +++ b/doc/_build/html/_sources/services/common/clients.sample.txt @@ -0,0 +1,22 @@ +Setup +----- + +Rackspace setup +~~~~~~~~~~~~~~~ + +.. include:: /services/common/rs-client.rst + + +OpenStack setup +~~~~~~~~~~~~~~~ + +If you're an OpenStack user, you will also need to prove a few other +configuration parameters: + +.. code-block:: php + + $client = new OpenCloud\OpenStack('{keystoneUrl}', array( + 'username' => '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/_build/html/_sources/services/common/os-client.sample.txt b/doc/_build/html/_sources/services/common/os-client.sample.txt new file mode 100644 index 000000000..b00e0dece --- /dev/null +++ b/doc/_build/html/_sources/services/common/os-client.sample.txt @@ -0,0 +1,9 @@ +or if you're an OpenStack user: + +.. code-block:: php + + $client = new OpenCloud\OpenStack('{keystoneUrl}', array( + 'username' => '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/_build/html/_sources/services/common/rs-client.sample.txt b/doc/_build/html/_sources/services/common/rs-client.sample.txt new file mode 100644 index 000000000..88a37d819 --- /dev/null +++ b/doc/_build/html/_sources/services/common/rs-client.sample.txt @@ -0,0 +1,29 @@ +Rackspace setup +~~~~~~~~~~~~~~~ + +The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key: + +.. code-block:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', + )); + + +OpenStack setup +~~~~~~~~~~~~~~~ + +If you're an OpenStack user, you will also need to prove a few other +configuration parameters: + +.. code-block:: php + + $client = new OpenCloud\OpenStack('{keystoneUrl}', array( + 'username' => '{username}', + 'password' => '{apiKey}', + 'tenantId' => '{tenantId}', + )); diff --git a/doc/_build/html/_sources/services/common/rs-client.txt b/doc/_build/html/_sources/services/common/rs-client.txt new file mode 100644 index 000000000..46f9a5291 --- /dev/null +++ b/doc/_build/html/_sources/services/common/rs-client.txt @@ -0,0 +1,11 @@ +The first step is to pass in your credentials and set up a client. For +Rackspace users, you will need your username and API key: + +.. code-block:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '{username}', + 'apiKey' => '{apiKey}', + )); diff --git a/doc/_build/html/_sources/services/common/rs-only.sample.txt b/doc/_build/html/_sources/services/common/rs-only.sample.txt new file mode 100644 index 000000000..9a1ccdf93 --- /dev/null +++ b/doc/_build/html/_sources/services/common/rs-only.sample.txt @@ -0,0 +1,8 @@ +.. note:: + + This service is only available for Rackspace users. + +Setup +----- + +.. include:: /services/common/rs-client.rst diff --git a/doc/_build/html/_sources/services/common/service-args.txt b/doc/_build/html/_sources/services/common/service-args.txt new file mode 100644 index 000000000..c1cc754b8 --- /dev/null +++ b/doc/_build/html/_sources/services/common/service-args.txt @@ -0,0 +1,11 @@ +* ``{catalogName}`` is the name of the service as it appears in the service + catalog. OpenStack users *must* set this value. For Rackspace users, a + default will be provided if you pass in ``null``. + +* ``{region}`` is the region the service will operate in. For Rackspace + users, you can select one of the following from the `supported regions page + `_. + +* ``{urlType}`` is the `type of URL `_ to use, depending on which + endpoints your catalog provides. If omitted, it will default to the public + network. diff --git a/doc/_build/html/_sources/services/compute/Images.md.txt b/doc/_build/html/_sources/services/compute/Images.md.txt new file mode 100644 index 000000000..66f1fff83 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/Images.md.txt @@ -0,0 +1,98 @@ +Compute Images +============== + +Intro +----- + +An image is a collection of files for a specific operating system that +you use to create or rebuild a server. Rackspace provides prebuilt +images. You can also create custom images from servers that you have +launched. + +In addition to creating images manually, you can also schedule images of +your server automatically. Please consult the `official +docs `__ +for more information about this extension, including enabling and +disabling scheduled images and showing scheduled images. + +With standard servers, the entire disk (OS and data) is captured in the +image. With Performance servers, only the system disk is captured in the +image. The data disks should be backed up using Cloud Backup or Cloud +Block Storage to ensure availability in case you need to rebuild or +restore a server. + +Setup +----- + +You first need to setup a Compute service. For information, please +consult the `Compute service `__ documentation. + +List images +----------- + +.. code:: php + + $images = $service->imageList(); + + foreach ($images as $image) { + + } + +For more information about `iterators `__, +please consult the official documentation. + +Query parameters +~~~~~~~~~~~~~~~~ + +You can also refine the list of images returned by providing specific +URL parameters: + ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Field name | Description | ++=================+====================================================================================================================================================================================================================================================================================================================================================+ +| server | Filters the list of images by server. Specify the server reference by ID or by full URL. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | Filters the list of images by image name. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| status | Filters the list of images by status. In-flight images have a status of ``SAVING`` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the ``OpenCloud\Compute\Constants\ImageState`` class. Images with an ``ACTIVE`` status are available for use. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| changes-since | Filters the list of images to those that have changed since the changes-since time. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| type | Filters base Rackspace images or any custom server images that you have created. Can either be ``BASE`` or ``SNAPSHOT``. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +Example +~~~~~~~ + +You can return more information about each image by setting the +``$details`` argument to ``true``. The second argument can be an array +of query parameters: + +.. code:: php + + use OpenCloud\Compute\Constants\ImageState; + + $list = $service->imageList(true, array( + 'server' => 'fooBar', + 'status' => ImageState::ACTIVE + )); + +Get an image +------------ + +.. code:: php + + $imageId = '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001'; + $image = $service->image($imageId); + +Delete an image +--------------- + +.. code:: php + + $image->delete(); + diff --git a/doc/_build/html/_sources/services/compute/Keypair.md.txt b/doc/_build/html/_sources/services/compute/Keypair.md.txt new file mode 100644 index 000000000..46f3e3795 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/Keypair.md.txt @@ -0,0 +1,103 @@ +Keypairs +======== + +Generate new keypair +-------------------- + +This operation creates a new keypair under a provided name; the public +key value is automatically generated for you. + +.. code:: php + + $keypair = $service->keypair(); + + $keypair->create(array( + 'name' => 'jamie_keypair_1' + )); + + echo $keypair->getPublicKey(); + + // Save private key to a file so you can use it to SSH into + // your server later. + $sshPrivateKeyFilename = 'jamie_keypair_1_rsa'; + $privateKey = $keypair->getPrivateKey(); + file_put_contents($sshPrivateKeyFilename, $privateKey); + chmod($sshPrivateKeyFilename, 0600); + +Upload existing keypair +----------------------- + +This operation creates a new keypair under a provided name using a +provided public key value. This public key will probably exist on your +local filesystem, and so provide easy access to your server when +uploaded. + +.. code:: php + + $keypair = $service->keypair(); + + $key = <<create(array( + 'name' => 'jamie_macbook', + 'publicKey' => $key + )); + +List keypairs +------------- + +To list all existing keypairs: + +.. code:: php + + $keys = $service->listKeypairs(); + + foreach ($keys as $key) { + // ... + } + +For more information about iterators, please see `the +docs <../Iterators.md>`__. + +Delete keypairs +--------------- + +To delete a specific keypair: + +.. code:: php + + $keypair->delete(); + +Creating a server with a keypair +-------------------------------- + +In order to spawn an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter: + +.. code:: php + + use Guzzle\Http\Exception\BadResponseException; + use OpenCloud\Compute\Constants\Network; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'New server', + 'image' => $ubuntuImage, + 'flavor' => $twoGbFlavor, + 'networks' => array( + $compute->network(Network::RAX_PUBLIC), + $compute->network(Network::RAX_PRIVATE) + ), + 'keypair' => 'jamie_macbook' + )); + } catch (BadResponseException $e) { + // error... + } + +So, as you can see, you specify the **name** of an existing keypair that +you previously created on the API. diff --git a/doc/_build/html/_sources/services/compute/Server.md.txt b/doc/_build/html/_sources/services/compute/Server.md.txt new file mode 100644 index 000000000..871071e17 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/Server.md.txt @@ -0,0 +1,242 @@ +Servers +======= + +Intro +----- + +A server is a virtual machine instance in the Cloud Servers environment. + +Setup +----- + +Server objects are instantiated from the Compute service. For more +details, see the `Service `__ docs. + +Get server +---------- + +The easiest way to retrieve a specific server is by its unique ID: + +.. code:: php + + $serverId = 'ef08aa7a-b5e4-4bb8-86df-5ac56230f841'; + $server = $service->server($serverId); + +List servers +------------ + +You can list servers in two different ways: + +- return an *overview* of each server (ID, name and links) +- return *detailed information* for each server + +Knowing which option to use might help save unnecessary bandwidth and +reduce latency. + +.. code:: php + + // overview + $servers = $service->serverList(); + + // detailed + $servers = $service->serverList(true); + +URL parameters for filtering servers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| Name | Description | Type | ++==========================+====================================================================================================================================================================================================================================================================================================================+=================================================+ +| image | The image ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| flavor | The flavor ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| name | The server name | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| status | The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult ``OpenCloud\Compute\Constants\ServerState`` | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| changes-since | Value for checking for changes since a previous request | A valid ISO 8601 dateTime (2011-01-24T17:08Z) | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| RAX-SI:image\_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ + +Create server +------------- + +Using an image +~~~~~~~~~~~~~~ + +There are a few parameter requirements when creating a server using an +image: + +- **name** - needs to be a string; +- **flavor** - a ``OpenCloud\Compute\Resource\Flavor`` object, that is + populated with the values of a real API flavor; +- **image** - a ``OpenCloud\Compute\Resource\Image`` object, that is + populated with the values of a real API image; + +Firstly we need to find our flavor and image using their UUIDs. For more +information about these concepts, including how to find flavor/image +UUIDs, please consult §§ 3-4 in the `Getting Started +guide `__. + +.. code:: php + + $ubuntuImage = $compute->image('868a0966-0553-42fe-b8b3-5cadc0e0b3c5'); + $twoGbFlavor = $compute->flavor('4'); + +Now we're ready to create our instance: + +.. code:: php + + use OpenCloud\Compute\Constants\Network; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'image' => $ubuntuImage, + 'flavor' => $twoGbFlavor + )); + } catch (\Guzzle\Http\Exception\BadResponseException $e) { + + // No! Something failed. Let's find out: + $responseBody = (string) $e->getResponse()->getBody(); + $statusCode = $e->getResponse()->getStatusCode(); + $headers = $e->getResponse()->getHeaderLines(); + + echo sprintf('Status: %s\nBody: %s\nHeaders: %s', $statusCode, $responseBody, implode(', ', $headers); + } + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Using a bootable volume +~~~~~~~~~~~~~~~~~~~~~~~ + +There are a few parameter requirements when creating a server using a +bootable volume: + +- **name** - needs to be a string; +- **flavor** - a ``OpenCloud\Compute\Resource\Flavor`` object, that is + populated with the values of a real API flavor; +- **volume** - a ``OpenCloud\Volume\Resource\Volume`` object, that is + populated with the values of a real API volume; + +Firstly we need to find our flavor and volume using their IDs. + +.. code:: php + + $volumeService = $client->volumeService(); + $bootableVolume = $volumeService->volume(''); + $flavor = $compute->flavor(''); + +Now we're ready to create our instance: + +.. code:: php + + use OpenCloud\Compute\Constants\Network; + + $server = $compute->server(); + + try { + $response = $server->create(array( + 'name' => 'My lovely server', + 'volume' => $bootableVolume, + 'flavor' => $flavor + )); + } catch (\Guzzle\Http\Exception\BadResponseException $e) { + // No! Something failed. Let's find out: + echo $e->getRequest() . PHP_EOL . PHP_EOL; + echo $e->getResponse(); + } + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Create parameters +~~~~~~~~~~~~~~~~~ + ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| Name | Description | Type | Required | ++=============================+=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+===========+==============================+ +| name | The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync. | string | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| flavor | A populated ``OpenCloud\Compute\Resource\Flavor`` object representing your chosen flavor | object | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| image | A populated ``OpenCloud\Compute\Resource\Image`` object representing your chosen image | object | No, if volume is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volume | A populated ``OpenCloud\Volume\Resource\Volume`` object representing your chosen bootable volume | object | No, if image is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volumeDeleteOnTermination | ``true`` if the bootable volume should be deleted when the server is terminated; ``false``, otherwise | boolean | No; default = ``false`` | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| OS-DCF:diskConfig | The disk configuration value. You can use two options: ``AUTO`` or ``MANUAL``. \ ``AUTO`` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. ``AUTO`` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.\ ``MANUAL`` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration. | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| networks | An array of populated ``OpenCloud\Compute\Resource\Network`` objects that indicate which networks your instance resides in. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| metadata | An array of arbitrary data (key-value pairs) that adds additional meaning to your server. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| keypair | You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| personality | Files that you can upload to your newly created instance's filesystem. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ + +Creating a server with keypairs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please see the `Keypair `__ docs for more information. + +Creating a server with personality files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you execute the create operation, you can add "personality" files +to your ``OpenCloud\Compute\Resource\Server`` object. These files are +structured as a flat array. + +.. code:: php + + $server->addFile('/var/test_file', 'FILE CONTENT'); + +As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the `official +documentation `__. + +Update server +------------- + +You can update certain attributes of an existing server instance. These +attributes are detailed in the next section. + +.. code:: php + + $server->update(array( + 'name' => 'NEW SERVER NAME' + )); + +Updatable attributes +~~~~~~~~~~~~~~~~~~~~ + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | description | ++==============+==================================================================================================================================================+ +| name | The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv4 | The IP version 4 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv6 | The IP version 6 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Delete server +------------- + +.. code:: php + + $server->delete(); + diff --git a/doc/_build/html/_sources/services/compute/Service.md.txt b/doc/_build/html/_sources/services/compute/Service.md.txt new file mode 100644 index 000000000..bc0e8cb28 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/Service.md.txt @@ -0,0 +1,27 @@ +Compute service +=============== + +Setup +----- + +To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the `Clients documentation <../Clients.md>`__. + +.. code:: php + + $service = $client->computeService(); + +If no arguments are provided to the above method, certain values are set +to their default values: + ++----------------+-------------------------+ +| Param | Default value | ++================+=========================+ +| ``$name`` | cloudServersOpenStack | ++----------------+-------------------------+ +| ``$region`` | DFW | ++----------------+-------------------------+ +| ``$urltype`` | publicURL | ++----------------+-------------------------+ + diff --git a/doc/_build/html/_sources/services/compute/flavors.txt b/doc/_build/html/_sources/services/compute/flavors.txt new file mode 100644 index 000000000..470ce8368 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/flavors.txt @@ -0,0 +1,62 @@ +Flavors +======= + +Get a flavor +------------ + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + + +List flavors +------------ + +.. code-block:: php + + $flavors = $service->flavorList(); + + foreach ($flavors as $flavor) { + /** @param $flavor OpenCloud\Common\Resource\FlavorInterface */ + } + +`Get the executable PHP script for this example `_ + + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the ``flavorList`` method returns full details on all flavors. +However, because of the overhead involved in retrieving all the details, this +function can be slower than might be expected. To disable this feature and +keep bandwidth at a minimum, just pass ``false`` as the first argument: + +.. code-block:: php + + // Name and ID only + $compute->flavorList(false); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+================================================================================================================================================================================================+ +| minDisk | Filters the list of flavors to those with the specified minimum number of gigabytes of disk storage. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| minRam | Filters the list of flavors to those with the specified minimum amount of RAM in megabytes. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to return all flavors over 4GB in RAM: + +.. code-block:: php + + $flavors = $service->flavorList(true, array('minRam' => 4)); diff --git a/doc/_build/html/_sources/services/compute/images.txt b/doc/_build/html/_sources/services/compute/images.txt new file mode 100644 index 000000000..570790c04 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/images.txt @@ -0,0 +1,85 @@ +Images +====== + +.. note:: + + **Images on Rackspace servers:** with standard servers, the entire disk + (OS and data) is captured in the image. With Performance servers, only the s + ystem disk is captured in the image. The data disks should be backed up using + Cloud Backup or Cloud Block Storage to ensure availability in case you need + to rebuild or restore a server. + + +List images +----------- + +Below is the simplest usage for retrieving a list of images: + +.. code-block:: php + + $images = $service->imageList(); + + foreach ($images as $image) { + + } + +`Get the executable PHP script for this example `_ + + +Detailed results +~~~~~~~~~~~~~~~~ + +By default, the only fields returned in a list call are `id` and `name`, but +you can enable more detailed information to be result by passing in `true` as +the first argument of the call, like so: + +.. code-block:: php + + $images = $service->imageList(true); + + +Filtering +~~~~~~~~~ + +You can also refine the list of images returned by providing specific filters: + ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Array key | Description | ++=================+====================================================================================================================================================================================================================================================================================================================================================+ +| server | Filters the list of images by server. Specify the server reference by ID or by full URL. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | Filters the list of images by image name. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| status | Filters the list of images by status. In-flight images have a status of ``SAVING`` and the conditional progress element contains a value from 0 to 100, which indicates the percentage completion. For a full list, please consult the ``OpenCloud\Compute\Constants\ImageState`` class. Images with an ``ACTIVE`` status are available for use. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| changes-since | Filters the list of images to those that have changed since the changes-since time. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| marker | The ID of the last item in the previous list. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Sets the page size. See the `official docs `__ for more information. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| type | Filters base Rackspace images or any custom server images that you have created. Can either be ``BASE`` or ``SNAPSHOT``. | ++-----------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +These are defined in an array and passed in as the second argument. For example, +to filter images for a particular server: + +.. code-block:: php + + $images = $service->imageList(false, array('server' => '{serverId}')); + + +Retrieve details about an image +------------------------------- + +.. code-block:: php + + $image = $service->image('{imageId}'); + + +Delete an image +--------------- + +.. code-block:: php + + $image->delete(); diff --git a/doc/_build/html/_sources/services/compute/index.txt b/doc/_build/html/_sources/services/compute/index.txt new file mode 100644 index 000000000..ee27bce8e --- /dev/null +++ b/doc/_build/html/_sources/services/compute/index.txt @@ -0,0 +1,55 @@ +Compute v2 +========== + +.. include:: ../common/clients.sample.rst + +Compute service +~~~~~~~~~~~~~~~ + +Now to instantiate the Compute service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + images + flavors + servers + keypairs + + +Glossary +-------- + +.. glossary:: + + image + An image is a collection of files for a specific operating system that you + use to create or rebuild a server. Rackspace provides prebuilt images. You + can also create custom images from servers that you have launched. + + flavor + A flavor is a named definition of certain server parameters such as + the amount of RAM and disk space available. (There are other parameters + set via the flavor, such as the amount of disk space and the number of + virtual CPUs, but a discussion of those is too in-depth for a simple + Getting Started Guide like this one.) + + server + A server is a virtual machine instance in the Cloud Servers environment. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/compute/keypair.txt b/doc/_build/html/_sources/services/compute/keypair.txt new file mode 100644 index 000000000..369b91a37 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/keypair.txt @@ -0,0 +1,71 @@ +Keypairs +======== + +Setup +----- + +.. include:: service.sample.rst + + +Generate a new keypair +---------------------- + +This operation creates a new keypair under a provided name; the public key +value is automatically generated for you. + +.. code:: php + + // Instantiate empty object + $keypair = $service->keypair(); + + // Send to API + $keypair->create(array( + 'name' => 'jamie_keypair_1' + )); + + // Save these! + $pubKey = $keypair->getPublicKey(); + $priKey = $keypair->getPrivateKey(); + + +Upload existing keypair +----------------------- + +This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem. + +.. code:: php + + $keypair = $service->keypair(); + + // $key needs to be the string content of the key file, not the filename + $content = file_get_contents('~/.ssh/id_rsa.pub'); + + $keypair->create(array( + 'name' => 'main_key', + 'publicKey' => $content + )); + +List keypairs +------------- + +To list all existing keypairs: + +.. code:: php + + $keys = $service->listKeypairs(); + + foreach ($keys as $key) { + + } + + +Delete keypairs +--------------- + +To delete a specific keypair: + +.. code:: php + + $keypair->delete(); diff --git a/doc/_build/html/_sources/services/compute/keypairs.txt b/doc/_build/html/_sources/services/compute/keypairs.txt new file mode 100644 index 000000000..823fcf5a2 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/keypairs.txt @@ -0,0 +1,70 @@ +Keypairs +======== + +Generate a new keypair +---------------------- + +This operation creates a new keypair under a provided name; the public key +value is automatically generated for you. + +.. code-block:: php + + // Instantiate empty object + $keypair = $service->keypair(); + + // Send to API + $keypair->create(array( + 'name' => 'jamie_keypair_1' + )); + + // Save these! + $pubKey = $keypair->getPublicKey(); + $priKey = $keypair->getPrivateKey(); + +`Get the executable PHP script for this example `_ + + +Upload existing keypair +----------------------- + +This operation creates a new keypair according to a provided name and public +key value. This is useful when the public key already exists on your local +filesystem. + +.. code-block:: php + + $keypair = $service->keypair(); + + // $key needs to be the string content of the key file, not the filename + $content = file_get_contents('~/.ssh/id_rsa.pub'); + + $keypair->create(array( + 'name' => 'main_key', + 'publicKey' => $content + )); + +`Get the executable PHP script for this example `_ + + +List keypairs +------------- + +To list all existing keypairs: + +.. code-block:: php + + $keys = $service->listKeypairs(); + + foreach ($keys as $key) { + + } + + +Delete keypairs +--------------- + +To delete a specific keypair: + +.. code-block:: php + + $keypair->delete(); diff --git a/doc/_build/html/_sources/services/compute/server.txt b/doc/_build/html/_sources/services/compute/server.txt new file mode 100644 index 000000000..17daddb79 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/server.txt @@ -0,0 +1,202 @@ +Servers +======= + +Setup +----- + +.. include:: service.sample.rst + +Get server +---------- + +The easiest way to retrieve a specific server is by its unique ID: + +.. code:: php + + $server = $service->server('{serverId}'); + + +List servers +------------ + +You can list servers in two different ways: + +- return an *overview* of each server (ID, name and links) +- return *detailed information* for each server + +Knowing which option to use might help save unnecessary bandwidth and +reduce latency. + +.. code:: php + + // overview + $servers = $service->serverList(); + + // detailed + $servers = $service->serverList(true); + +URL parameters for filtering servers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| Name | Description | Type | ++==========================+====================================================================================================================================================================================================================================================================================================================+=================================================+ +| image | The image ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| flavor | The flavor ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| name | The server name | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| status | The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult ``OpenCloud\Compute\Constants\ServerState`` | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| changes-since | Value for checking for changes since a previous request | A valid ISO 8601 dateTime (2011-01-24T17:08Z) | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| RAX-SI:image\_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ + +Create server +------------- + +Using an image +~~~~~~~~~~~~~~ + +Now we're ready to create our instance: + +.. code:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Using a bootable volume +~~~~~~~~~~~~~~~~~~~~~~~ + +Firstly we need to find our volume using their IDs. + +.. code:: php + + $bootableVolume = $client->volumeService()->volume('{volumeId}'); + +Now we're ready to create our instance: + +.. code:: php + + $server = $compute->server(); + + $response = $server->create(array( + 'name' => 'My lovely server', + 'volume' => $bootableVolume, + 'flavorId' => '{flavorId}' + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +Create parameters +~~~~~~~~~~~~~~~~~ + ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| Name | Description | Type | Required | ++=============================+=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+===========+==============================+ +| name | The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync. | string | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| flavor | A populated ``OpenCloud\Compute\Resource\Flavor`` object representing your chosen flavor | object | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| image | A populated ``OpenCloud\Compute\Resource\Image`` object representing your chosen image | object | No, if volume is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volume | A populated ``OpenCloud\Volume\Resource\Volume`` object representing your chosen bootable volume | object | No, if image is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volumeDeleteOnTermination | ``true`` if the bootable volume should be deleted when the server is terminated; ``false``, otherwise | boolean | No; default = ``false`` | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| OS-DCF:diskConfig | The disk configuration value. You can use two options: ``AUTO`` or ``MANUAL``. \ ``AUTO`` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. ``AUTO`` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.\ ``MANUAL`` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration. | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| networks | An array of populated ``OpenCloud\Compute\Resource\Network`` objects that indicate which networks your instance resides in. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| metadata | An array of arbitrary data (key-value pairs) that adds additional meaning to your server. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| keypair | You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| personality | Files that you can upload to your newly created instance's filesystem. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ + +Creating a server with keypairs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter: + +.. code:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'New server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'keypair' => 'main_key' + )); + +So, as you can see, you specify the **name** of an existing keypair that +you previously created on the API. + + +Creating a server with personality files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you execute the create operation, you can add "personality" files +to your ``OpenCloud\Compute\Resource\Server`` object. These files are +structured as a flat array. + +.. code:: php + + $server->addFile('/var/test_file', 'FILE CONTENT'); + +As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the `official +documentation `__. + +Update server +------------- + +You can update certain attributes of an existing server instance. These +attributes are detailed in the next section. + +.. code:: php + + $server->update(array( + 'name' => 'NEW SERVER NAME' + )); + +Updatable attributes +~~~~~~~~~~~~~~~~~~~~ + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | description | ++==============+==================================================================================================================================================+ +| name | The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv4 | The IP version 4 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv6 | The IP version 6 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + +Delete server +------------- + +.. code:: php + + $server->delete(); diff --git a/doc/_build/html/_sources/services/compute/servers.txt b/doc/_build/html/_sources/services/compute/servers.txt new file mode 100644 index 000000000..0709bda9a --- /dev/null +++ b/doc/_build/html/_sources/services/compute/servers.txt @@ -0,0 +1,250 @@ +Servers +======= + +Get server +---------- + +The easiest way to retrieve a specific server is by its unique ID: + +.. code-block:: php + + $server = $service->server('{serverId}'); + + +List servers +------------ + +You can list servers in two different ways: + +- return an *overview* of each server (ID, name and links) +- return *detailed information* for each server + +Knowing which option to use might help save unnecessary bandwidth and +reduce latency. + +.. code-block:: php + + // overview + $servers = $service->serverList(); + + // detailed + $servers = $service->serverList(true); + +URL parameters for filtering servers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| Name | Description | Type | ++==========================+====================================================================================================================================================================================================================================================================================================================+=================================================+ +| image | The image ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| flavor | The flavor ID | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| name | The server name | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| status | The server status. Servers contain a status attribute that indicates the current server state. You can filter on the server status when you complete a list servers request, and the server status is returned in the response body. For a full list, please consult ``OpenCloud\Compute\Constants\ServerState`` | string | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| changes-since | Value for checking for changes since a previous request | A valid ISO 8601 dateTime (2011-01-24T17:08Z) | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ +| RAX-SI:image_schedule | If scheduled images enabled or not. If the value is TRUE, the list contains all servers that have an image schedule resource set on them. If the value is set to FALSE, the list contains all servers that do not have an image schedule. | bool | ++--------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+ + +`Get the executable PHP script for this example `_ + + +Create server +------------- + +Using an image +~~~~~~~~~~~~~~ + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'My lovely server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +`Get the executable PHP script for this example `_ + + +Using a bootable volume +~~~~~~~~~~~~~~~~~~~~~~~ + +Firstly we need to find our volume using their IDs. + +.. code-block:: php + + $bootableVolume = $client->volumeService()->volume('{volumeId}'); + +Now we're ready to create our instance: + +.. code-block:: php + + $server = $compute->server(); + + $response = $server->create(array( + 'name' => 'My lovely server', + 'volume' => $bootableVolume, + 'flavorId' => '{flavorId}' + )); + +It's always best to be defensive when executing functionality over HTTP; +you can achieve this best by wrapping calls in a try/catch block. It +allows you to debug your failed operations in a graceful and efficient +manner. + +`Get the executable PHP script for this example `_ + + +Create parameters +~~~~~~~~~~~~~~~~~ + ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| Name | Description | Type | Required | ++=============================+=================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+===========+==============================+ +| name | The server name. The name that you specify in a create request becomes the initial host name of the server. After the server is built, if you change the server name in the API or change the host name directly, the names are not kept in sync. | string | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| flavor | A populated ``OpenCloud\Compute\Resource\Flavor`` object representing your chosen flavor | object | Yes | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| image | A populated ``OpenCloud\Compute\Resource\Image`` object representing your chosen image | object | No, if volume is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volume | A populated ``OpenCloud\Volume\Resource\Volume`` object representing your chosen bootable volume | object | No, if image is specified | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| volumeDeleteOnTermination | ``true`` if the bootable volume should be deleted when the server is terminated; ``false``, otherwise | boolean | No; default = ``false`` | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| OS-DCF:diskConfig | The disk configuration value. You can use two options: ``AUTO`` or ``MANUAL``. \ ``AUTO`` means the server is built with a single partition the size of the target flavor disk. The file system is automatically adjusted to fit the entire partition. This keeps things simple and automated. ``AUTO`` is valid only for images and servers with a single partition that use the EXT3 file system. This is the default setting for applicable Rackspace base images.\ ``MANUAL`` means the server is built using whatever partition scheme and file system is in the source image. If the target flavor disk is larger, the remaining disk space is left unpartitioned. This enables images to have non-EXT3 file systems, multiple partitions, and so on, and enables you to manage the disk configuration. | string | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| networks | An array of populated ``OpenCloud\Compute\Resource\Network`` objects that indicate which networks your instance resides in. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| metadata | An array of arbitrary data (key-value pairs) that adds additional meaning to your server. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| keypair | You can install a registered keypair onto your newly created instance, thereby providing scope for keypair-based authentication. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ +| personality | Files that you can upload to your newly created instance's filesystem. | array | No | ++-----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+------------------------------+ + +Creating a server with keypairs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to provision an instance with a saved keypair (allowing you to SSH +in without passwords), you create your server using the same operation +as usual, with one extra parameter: + +.. code-block:: php + + $server = $compute->server(); + + $server->create(array( + 'name' => 'New server', + 'imageId' => '{imageId}', + 'flavorId' => '{flavorId}', + 'keypair' => 'main_key' + )); + +So, as you can see, you specify the **name** of an existing keypair that +you previously created on the API. + +`Get the executable PHP script for this example `_ + + +Creating a server with personality files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before you execute the create operation, you can add "personality" files +to your ``OpenCloud\Compute\Resource\Server`` object. These files are +structured as a flat array. + +.. code-block:: php + + $server->addFile('/var/test_file', 'FILE CONTENT'); + +As you can see, the first parameter represents the filename, and the +second is a string representation of its content. When the server is +created these files will be created on its local filesystem. For more +information about server personality files, please consult the `official +documentation `__. + +Update server +------------- + +You can update certain attributes of an existing server instance. These +attributes are detailed in the next section. + +.. code-block:: php + + $server->update(array( + 'name' => 'NEW SERVER NAME' + )); + + +`Get the executable PHP script for this example `_ + + +Updatable attributes +~~~~~~~~~~~~~~~~~~~~ + ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| name | description | ++==============+==================================================================================================================================================+ +| name | The name of the server. If you edit the server name, the server host name does not change. Also, server names are not guaranteed to be unique. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv4 | The IP version 4 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +| accessIPv6 | The IP version 6 address. | ++--------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ + + +Updating the access IP address(es) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example, you may have a private cloud with internal addresses in the +10.1.x range. However, you can access a server via a firewall device at +address 50.57.94.244. In this case, you can change the ``accessIPv4`` +attribute to point to the firewall: + +.. code-block:: php + + $server->update(array('accessIPv4' => '50.57.94.244')); + +When a client application retrieves the server’s information, it will +know that it needs to use the ``accessIPv4`` address to connect to the +server, and *not* the IP address assigned to one of the network +interfaces. + + +Retrieving the server’s IP address +---------------------------------- + +The ``Server::ip()`` method is used to retrieve the server’s IP address. +It has one optional parameter: the format (either IPv4 or IPv6) of the +address to return (by default, it returns the IPv4 address): + +.. code-block:: php + + // IPv4 + echo $server->ip(); + echo $server->ip(4); + + // IPv6 + echo $server->ip(6); + + +Delete server +------------- + +.. code-block:: php + + $server->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/compute/service.sample.txt b/doc/_build/html/_sources/services/compute/service.sample.txt new file mode 100644 index 000000000..e75252148 --- /dev/null +++ b/doc/_build/html/_sources/services/compute/service.sample.txt @@ -0,0 +1,9 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Auto Scale service: + +.. code-block:: php + + $service = $client->computeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst diff --git a/doc/_build/html/_sources/services/database/README.md.txt b/doc/_build/html/_sources/services/database/README.md.txt new file mode 100644 index 000000000..3f6bdd3c2 --- /dev/null +++ b/doc/_build/html/_sources/services/database/README.md.txt @@ -0,0 +1,125 @@ +Databases +========= + +A **cloud database** is a MySQL relational database service that allows +customers to programatically provision database instances of varying +virtual resource sizes without the need to maintain and/or update MySQL. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\Rackspace; + use OpenCloud\Common\Constants\State; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Create a database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $databaseService = $client->databaseService('cloudDatabases', 'DFW'); + + $twoGbFlavor = $databaseService->flavor(3); + + $dbInstance = $databaseService->instance(); + $dbInstance->name = 'Demo database instance'; + $dbInstance->volume = new stdClass(); + $dbInstance->volume->size = 20; // GB + $dbInstance->flavor = $twoGbFlavor; + $dbInstance->create(); + + $dbInstance->waitFor(State::ACTIVE, null, function ($dbInstance) { + + printf("Database instance build status: %s\n", $dbInstance->status); + + }); + +The example above creates a database server instance with 20GB of disk +space and 2GB of memory, then waits for it to become ACTIVE. + +3. Create a database on the database server instance. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $db = $dbInstance->database(); + $db->name = 'demo_db'; + + $db->create(); + +The example above creates a database named ``demo_db`` on the database +server instance created in the previous step. + +4. Create database user and give it access to database. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $user = $dbInstance->user(); + $user->name = 'demo_user'; + $user->password = 'h@X0r!'; + $user->databases = array('demo_db'); + + $user->create(); + +The example above creates a database user named ``demo_user``, sets its +password and gives it access to the ``demo_db`` database created in the +previous step. + +5. Optional step: Create a load balancer to allow access to the database from the Internet. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The database created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $loadBalancer->name = 'Load balancer - DB'; + $loadBalancer->addNode($dbInstance->hostname, 3306); + $loadBalancer->port = 3306; + $loadBalancer->protocol = 'MYSQL'; + $loadBalancer->addVirtualIp('PUBLIC'); + + $loadBalancer->create(); + + $loadBalancer->waitFor(State::ACTIVE, null, function ($lb) { + printf("Load balancer build status: %s\n", $lb->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. diff --git a/doc/_build/html/_sources/services/database/configurations.txt b/doc/_build/html/_sources/services/database/configurations.txt new file mode 100644 index 000000000..f50619914 --- /dev/null +++ b/doc/_build/html/_sources/services/database/configurations.txt @@ -0,0 +1,127 @@ +Configurations +============== + +Creating a configuration +------------------------ + +.. code-block:: php + + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + $configuration = $service->configuration(); + + $configuration->create(array( + 'name' => 'example-configuration-name', + 'description' => 'An example configuration', + 'values' => array( + 'collation_server' => 'latin1_swedish_ci', + 'connect_timeout' => 120 + ), + 'datastore' => array( + 'type' => '10000000-0000-0000-0000-000000000001', + 'version' => '1379cc8b-4bc5-4c4a-9e9d-7a9ad27c0866' + ) + )); + +`Get the executable PHP script for this example `__ + + +Listing configurations +---------------------- + +You can list out all the configurations you have created as shown below: + +.. code-block:: php + + $configurations = $service->configurationList(); + foreach ($configurations as $configuration) { + /** @var $configuration OpenCloud\Database\Resource\Configuration **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a configuration +-------------------------- + +You can retrieve a specific configuration, using its ID, as shown below: + +.. code-block:: php + + $configuration = $service->configuration('{configId}'); + /** @var OpenCloud\Database\Resource\Configuration **/ + +`Get the executable PHP script for this example `__ + + +Updating a configuration +------------------------ + +You have two choices when updating a configuration: + +* you can `patch a configuration <#patching-a-configuration>`__ to change only +some configuration parameters +* you can `entirely replace a configuration <#replacing-a-configuration>`__ to +replace all configuration parameters with new ones + + +Patching a configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can patch a configuration as shown below: + +.. code-block:: php + + $configuration->patch(array( + 'values' => array( + 'connect_timeout' => 30 + ) + )); + +`Get the executable PHP script for this example `__ + + +Replacing a configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can replace a configuration as shown below: + +.. code-block:: php + + $configuration->update(array( + 'values' => array( + 'collation_server' => 'utf8_general_ci', + 'connect_timeout' => 60 + ) + )); + +`Get the executable PHP script for this example `__ + + +Deleting a configuration +------------------------ + +.. code-block:: php + + $configuration->delete(); + +`Get the executable PHP script for this example `__ + +.. note:: + + You cannot delete a configuration if it is in use by a running instance. + + +Listing instances using a configuration +--------------------------------------- + +You can list all instances using a specific configuration, using its ID, +as shown below: + +.. code-block:: php + + $instances = $configuration->instanceList(); + foreach ($instances as $instance) { + /** @var $instance OpenCloud\Database\Resource\Instance **/ + } + +`Get the executable PHP script for this example `__ diff --git a/doc/_build/html/_sources/services/database/databases.txt b/doc/_build/html/_sources/services/database/databases.txt new file mode 100644 index 000000000..83d07c5e2 --- /dev/null +++ b/doc/_build/html/_sources/services/database/databases.txt @@ -0,0 +1,60 @@ +Databases +========= + +Setup +----- + +In order to interact with the functionality of databases, you must first +retrieve the details of the instance itself. To do this, you must substitute +`{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating a new database +----------------------- + +To create a new database, you must supply it with a name; you can +optionally specify its character set and collating sequence: + +.. code-block:: php + + // Create an empty object + $database = $instance->database(); + + // Send to API + $database->create(array( + 'name' => 'production', + 'character_set' => 'utf8', + 'collate' => 'utf8_general_ci' + )); + +You can find values for ``character_set`` and ``collate`` at `the MySQL +website `__. + + +Deleting a database +------------------- + +.. code-block:: php + + $database->delete(); + +.. note:: + + This is a destructive operation: all your data will be wiped away and will + not be retrievable. + + +Listing databases +----------------- + +.. code-block:: php + + $databases = $service->databaseList(); + + foreach ($databases as $database) { + /** @param $database OpenCloud\Database\Resource\Database */ + } diff --git a/doc/_build/html/_sources/services/database/datastores.txt b/doc/_build/html/_sources/services/database/datastores.txt new file mode 100644 index 000000000..ad6d90e85 --- /dev/null +++ b/doc/_build/html/_sources/services/database/datastores.txt @@ -0,0 +1,59 @@ +Datastores +========== + +Listing datastores +------------------ + +You can list out all the datastores available as shown below: + +.. code-block:: php + + $datastores = $service->datastoreList(); + foreach ($datastores as $datastore) { + /** @var $datastore OpenCloud\Database\Resource\Datastore **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore +---------------------- + +You can retrieve a specific datastore's information, using its ID, as +shown below: + +.. code-block:: php + + /** @var OpenCloud\Database\Resource\Datastore **/ + $datastore = $service->datastore('{datastoreId}'); + +`Get the executable PHP script for this example `__ + + +Listing datastore versions +-------------------------- + +You can list out all the versions available for a specific datastore, as +shown below: + +.. code-block:: php + + $versions = $datastore->versionList(); + foreach ($versions as $version) { + /** @var $version OpenCloud\Database\Resource\DatastoreVersion **/ + } + +`Get the executable PHP script for this example `__ + + +Retrieving a datastore version +------------------------------ + +You a retrieve a specific datastore version, using its ID, as shown +below: + +.. code-block:: php + + $datastoreVersion = $datastore->version('{versionId}'); + +`Get the executable PHP script for this example `__ diff --git a/doc/_build/html/_sources/services/database/index.txt b/doc/_build/html/_sources/services/database/index.txt new file mode 100644 index 000000000..32888e27d --- /dev/null +++ b/doc/_build/html/_sources/services/database/index.txt @@ -0,0 +1,77 @@ +Databases v1 +============ + +.. include:: ../common/clients.sample.rst + +Databases service +~~~~~~~~~~~~~~~~~ + +Now to instantiate the Databases service: + +.. code-block:: php + + $service = $client->databaseService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + instances + databases + users + datastores + + +Glossary +-------- + +.. glossary:: + + configuration group + A configuration group is a collection of key/value pairs which configure a + database instance. Some directives are capable of being applied dynamically, + while other directives require a server restart to take effect. The + configuration group can be applied to an instance at creation or applied to + an existing instance to modify the behavior of the running datastore on the + instance. + + flavor + A flavor is an available hardware configuration for a database instance. + Each flavor has a unique combination of memory capacity and priority for + CPU time. + + instance + A database instance is an isolated MySQL instance in a single tenant + environment on a shared physical host machine. Also referred to as + instance. + + database + A database is a local MySQL database running on an instance. + + user + A user is a local MySQL user that can access a database running on an + instance. + + datastore + The database engine running on your instance. Currently, there is support + for MySQL 5.6, MySQL 5.1, Percona 5.6 and MariaDB 10. + + volume + A volume is user-specified storage that contains the database engine data + directory. Volumes are automatically provisioned on shared Internet Small + Computer System Interface (iSCSI) storage area networks (SAN) that provide + for increased performance, scalability, availability and manageability. + Applications with high I/O demands are performance optimized and data is + protected through both local and network RAID-10. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/database/instances.txt b/doc/_build/html/_sources/services/database/instances.txt new file mode 100644 index 000000000..6f3afc1e0 --- /dev/null +++ b/doc/_build/html/_sources/services/database/instances.txt @@ -0,0 +1,155 @@ +Instances +========= + +Create a new instance +--------------------- + +.. code-block:: php + + // Create an empty object + $instance = $service->instance(); + + // Send to the API + $instance->create(array( + 'name' => '{name}', + 'flavor' => $service->flavor('{flavorId}'), + 'volume' => array('size' => 4) // 4GB of volume disk + )); + +`Get the executable PHP script for this sample `__ + + +Waiting for the instance to build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The SDK provides a blocking operation that will wait until your instance resource +has transitioned into an ``ACTIVE`` state. During this period, it will +continuously poll the API and break the loop when the state has been achieved: + +.. code-block:: php + + $instance->waitFor('ACTIVE', null, function ($instance) { + // This will be executed continuously + printf("Database instance build status: %s\n", $instance->status); + }); + + +Connecting an instance to a load balancer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The instance created in the previous step can only be accessed from the +Rackspace private network (aka ``SERVICENET``). If you have a cloud +server instance in the same region as the database server instance, you +will be able to connect to the database from that cloud server instance. + +If, however, you would like to access the database from the Internet, +you will need to create a load balancer with an IP address that is +routable from the Internet and attach the database server instance as a +back-end node of this load balancer. + +.. code-block:: php + + $lbService = $client->loadBalancerService(null, '{region}'); + + // Create empty object + $loadBalancer = $lbService->loadBalancer(); + + // Associate this LB with the instance as a "node" + $loadBalancer->addNode($instance->hostname, 3306); + $loadBalancer->addVirtualIp('PUBLIC'); + + // Configure other parameters and send to the API + $loadBalancer->create(array( + 'name' => 'DB Load Balancer', + 'port' => 3306, + 'protocol' => 'MYSQL', + )); + + // Wait for the resource to create + $loadBalancer->waitFor('ACTIVE', null, function ($loadBalancer) { + printf("Load balancer build status: %s\n", $loadBalancer->status); + }); + + foreach ($loadBalancer->virtualIps as $vip) { + if ($vip->type == 'PUBLIC') { + printf("Load balancer public %s address: %s\n", $vip->ipVersion, $vip->address); + } + } + +In the example above, a load balancer is created with the database +server instance as its only back-end node. Further, this load balancer +is configured to listen for MySQL connections on port 3306. Finally a +virtual IP address (VIP) is configured in the ``PUBLIC`` network address +space so that this load balancer may receive connections from the +Internet. + +Once the load balancer is created and becomes ``ACTIVE``, it's +Internet-accessible IP addresses are printed out. If you connect to any +of these IP addresses on port 3306 using the MySQL protocol, you will be +connected to the database created in step 3. + + +Retrieving an instance +---------------------- + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + +`Get the executable PHP script for this example `__ + + +Updating an instance +-------------------- + +An instance can be updated to use a specific `configuration `__ as shown below. + +.. code-block:: php + + $instance->update(array( + 'configuration' => '{configurationId}' + )); + +.. note:: + + If any parameters in the associated configuration require a restart, then you + will need to `restart the instance <#restarting-an-instance>`__ after the update. + + +Deleting an instance +-------------------- + +.. code-block:: php + + $instance->delete(); + + +Restarting an instance +---------------------- + +.. code-block:: php + + $instance->restart(); + + +Resizing an instance's RAM +-------------------------- + +To change the amount of RAM allocated to the instance: + +.. code-block:: php + + $flavor = $service->flavor('{flavorId}'); + $instance->resize($flavor); + + +Resizing an instance's volume +----------------------------- + +You can also independently change the volume size to increase the disk +space: + +.. code-block:: php + + // Increase to 8GB disk + $instance->resizeVolume(8); diff --git a/doc/_build/html/_sources/services/database/service.sample.txt b/doc/_build/html/_sources/services/database/service.sample.txt new file mode 100644 index 000000000..60964126d --- /dev/null +++ b/doc/_build/html/_sources/services/database/service.sample.txt @@ -0,0 +1,9 @@ +.. include:: ../common/rs-client.sample.rst + +Now, set up the Database service: + +.. code-block:: php + + $service = $client->databaseService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst diff --git a/doc/_build/html/_sources/services/database/users.txt b/doc/_build/html/_sources/services/database/users.txt new file mode 100644 index 000000000..0343c4904 --- /dev/null +++ b/doc/_build/html/_sources/services/database/users.txt @@ -0,0 +1,67 @@ +Users +===== + +Setup +----- + +Finally, in order to interact with the functionality of databases, you must +first retrieve the details of the instance itself. To do this, you must +substitute `{instanceId}` for your instance's ID: + +.. code-block:: php + + $instance = $service->instance('{instanceId}'); + + +Creating users +-------------- + +Database users exist at the ``Instance`` level, but can be associated +with a specific ``Database``. They are represented by the +``OpenCloud\Database\Resource\User`` class. + +.. code-block:: php + + // New instance of OpenCloud\Database\Resource\User + $user = $instance->user(); + + // Send to API + $user->create(array( + 'name' => 'Alice', + 'password' => 'fooBar' + 'databases' => array('production') + )); + + +Deleting a user +--------------- + +.. code-block:: php + + $user->delete(); + + +The root user +------------- + +By default, Cloud Databases does not enable the root user. In most +cases, the root user is not needed, and having one can leave you open to +security violations. However, if you do want to enable access to the root user: + +.. code-block:: php + + $rootUser = $instance->enableRootUser(); + + +This returns a regular ``User`` object with the ``name`` attribute set +to ``root`` and the ``password`` attribute set to an auto-generated +password. + + +Check if root user is enabled +----------------------------- + +.. code-block:: php + + // true for yes, false for no + $instance->isRootEnabled(); diff --git a/doc/_build/html/_sources/services/dns/Domains.md.txt b/doc/_build/html/_sources/services/dns/Domains.md.txt new file mode 100644 index 000000000..824c05099 --- /dev/null +++ b/doc/_build/html/_sources/services/dns/Domains.md.txt @@ -0,0 +1,290 @@ +Domains +======= + +A domain is an entity/container of all DNS-related information +containing one or more records. + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name. + +.. code:: php + + $domain = $service->domain(12345); + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the aforementioned search by using the ``name`` parameter +in a key/value array supplied as a method argument. For example, +providing ``array('name' => 'hoola.com')`` will return hoola.com and +similar names such as main.hoola.com and sub.hoola.com. + +.. code:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'hoola.com' + )); + +Filter criteria may consist of: + +- Any letter (A-Za-z) +- Numbers (0-9) +- Hyphen ("-") +- 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +If you know a domain's name, but not its unique identifier, you can do +this: + +.. code:: php + + $domains = $service->domainList(array( + 'name' => 'foo.com' + )); + + foreach ($domains as $domain) { + $id = $domain->id; + } + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. The since parameter is optional and defaults to midnight of +the current day. + +.. code:: php + + $changes = $domain->changes(); + + # Changes since last week + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + +Export domain +------------- + +This call provides the BIND (Berkeley Internet Name Domain) 9 formatted +contents of the requested domain. This call is for a single domain only, +and as such, does not traverse up or down the domain hierarchy for +details (that is, no subdomain information is provided). + +.. code:: php + + $asyncResponse = $domain->export(); + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + +Create domain +------------- + +A domain is composed of DNS records (e.g. ``A``, ``CNAME`` or ``MX`` +records) and an optional list of sub-domains. You will need to specify +these before creating the domain itself: + +.. code:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + $domain->addRecord($aRecord); + + // add optional C record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add optional MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add optional NS records + $nsRecord1 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord1); + + $nsRecord2 = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns2.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord2); + + // add optional subdomains + $sub1 = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($sub1); + + // send to API + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + +Clone domain +------------ + +This call will duplicate a single existing domain configuration with a +new domain name for the specified Cloud account. By default, all records +and, optionally, subdomain(s) are duplicated as well. + +The method signature you will need to use is: + +.. code:: php + + cloneDomain ( string $newDomainName [, bool $subdomains [, bool $comments [, bool $email [, bool $records ]]]] ) + ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Data type | Default | Description | ++======================+==============+============+====================================================================================================================================================================================+ +| ``$newDomainName`` | ``string`` | - | The new name for your cloned domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$subdomains`` | ``bool`` | ``true`` | Set to ``TRUE`` to clone all the subdomains for this domain | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$comments`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``$email`` | ``bool`` | ``true`` | Set to ``TRUE`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. | ++----------------------+--------------+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +For example: + +.. code:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true); + +Import domain +------------- + +This call provisions a new DNS domain under the account specified by the +BIND 9 formatted file configuration contents defined in the request +object. + +You will need to ensure that the BIND 9 formatted file configuration +contents are valid by adhering to the following rules: + +- Each record starts on a new line and on the first column. If a record + will not fit on one line, use the BIND\_9 line continuation + convention where you put a left parenthesis and continue the one + record on the next line and put a right parenthesis when the record + ends. For example, + + example2.net. 3600 IN SOA dns1.stabletransit.com. ( + sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +- The attribute values of a record must be separated by a single blank + or tab. No other white space characters. + +- If there are any NS records, the data field should not be + dns1.stabletransit.com or dns2.stabletransit.com. They will result in + "duplicate record" errors. + +For example: + +.. code:: php + + $bind9Data = <<import($bind9Data); + +Modify domain +------------- + +This call modifies DNS domain(s) attributes only. Only the TTL, email +address and comment attributes of a domain can be modified. Records +cannot be added, modified, or removed through this API operation - you +will need to use the `add +records `__, `modify +records `__ or `remove +records `__ operations +respectively. + +.. code:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + +Remove domain +------------- + +.. code:: php + + $domain->delete(); + diff --git a/doc/_build/html/_sources/services/dns/Limits.md.txt b/doc/_build/html/_sources/services/dns/Limits.md.txt new file mode 100644 index 000000000..72f8219a0 --- /dev/null +++ b/doc/_build/html/_sources/services/dns/Limits.md.txt @@ -0,0 +1,70 @@ +Limits +====== + +Setup +----- + +Limit methods will be called on the DNS service, an instance of +``OpenCloud\DNS\Service``. Please see the `DNS service `__ +documentation for setup instructions. + +List all limits +--------------- + +This call provides a list of all applicable limits for the specified +account. + +.. code:: php + + $limits = $service->limits(); + +Absolute limits +~~~~~~~~~~~~~~~ + +There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain: + +.. code:: php + + $absoluteLimits = $limits->absolute; + + # Domain limit + echo $absoluteLimits->domains; + + # Record limit per domain + echo $absoluteLimits->{'records per domain'}; + +List limit types +---------------- + +To find out the different limit types you can query, run: + +.. code:: php + + $limitTypes = $service->limitTypes(); + +will return: + +:: + + array(3) { + [0] => + string(10) "RATE_LIMIT" + [1] => + string(12) "DOMAIN_LIMIT" + [2] => + string(19) "DOMAIN_RECORD_LIMIT" + } + +Query a specific limit +---------------------- + +.. code:: php + + $limit = $service->limits('DOMAIN_LIMIT'); + + echo $limit->absolute->limits->value; + + >>> 500 + diff --git a/doc/_build/html/_sources/services/dns/Records.md.txt b/doc/_build/html/_sources/services/dns/Records.md.txt new file mode 100644 index 000000000..4e492e8ef --- /dev/null +++ b/doc/_build/html/_sources/services/dns/Records.md.txt @@ -0,0 +1,111 @@ +Records +======= + +A DNS record belongs to a particular domain and is used to specify +information about the domain. + +There are several types of DNS records. Examples include mail exchange +(MX) records, which specify the mail server for a particular domain, and +name server (NS) records, which specify the authoritative name servers +for a domain. + +It is represented by the ``OpenCloud\DNS\Resource\Record`` class. +Records belong to a `Domain `__. + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('NS-1234567'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+---------------+ +| Name | Data type | Default | Description | ++============+==============+========================+===============+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+---------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+---------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+---------------+ + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record(123456); + $record->ttl -= 100; + $record->update(); + +Delete record +------------- + +.. code:: php + + $record->delete(); + diff --git a/doc/_build/html/_sources/services/dns/Reverse-DNS.md.txt b/doc/_build/html/_sources/services/dns/Reverse-DNS.md.txt new file mode 100644 index 000000000..b8a5c0e76 --- /dev/null +++ b/doc/_build/html/_sources/services/dns/Reverse-DNS.md.txt @@ -0,0 +1,96 @@ +Reverse DNS +=========== + +DNS usually determines an IP address associated with a domain name. +Reverse DNS is the opposite process: resolving a domain name from an IP +address. This is usually achieved with a domain name pointer. + +Get PTR record +-------------- + +PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one's PTR record: + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptr = $service->ptrRecord(array( + 'parent' => $parent + )); + +So, in the above example, a ``$parent`` could be an instance of +``OpenCloud\Compute\Resource\Server`` or +``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both +implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please +consult the `server documentation <../Compute/Server.md>`__ and `load +balancer documentation <../LoadBalancer/USERGUIDE.md>`__ for more +detailed usage instructions. + +List PTR records +---------------- + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptrRecords = $service->ptrRecordList($parent); + + foreach ($ptrRecords as $ptrRecord) { + + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add PTR record +-------------- + +.. code:: php + + $parent = $computeService->server('foo-server-id'); + + $ptr = $dnsService->ptrRecord(array( + 'parent' => $parent, + 'ttl' => 3600, + 'name' => 'example.com', + 'type' => 'PTR', + 'data' => '192.0.2.7' + )); + + $ptr->create(); + +Here is a table that explains the above attributes: + ++-----------+------------------------------------------------------------------------------------+------------+ +| Name | Description | Required | ++===========+====================================================================================+============+ +| type | Specifies the record type as "PTR". | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| name | Specifies the name for the domain or subdomain. Must be a valid domain name. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| data | The data field for PTR records must be a valid IPv4 or IPv6 IP address. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| ttl | If specified, must be greater than 300. Defaults to 3600 if no TTL is specified. | No | ++-----------+------------------------------------------------------------------------------------+------------+ +| comment | If included, its length must be less than or equal to 160 characters. | No | ++-----------+------------------------------------------------------------------------------------+------------+ + +Modify PTR record +----------------- + +.. code:: php + + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + +Delete PTR record +----------------- + +.. code:: php + + $ptr->delete(); + diff --git a/doc/_build/html/_sources/services/dns/Service.md.txt b/doc/_build/html/_sources/services/dns/Service.md.txt new file mode 100644 index 000000000..29ea79193 --- /dev/null +++ b/doc/_build/html/_sources/services/dns/Service.md.txt @@ -0,0 +1,13 @@ +DNS Service +=========== + +To instantiate a Compute service object, you first need to setup a +Rackspace/OpenStack client. To do this, or for more information, please +consult the `Clients documentation <../Clients.md>`__. + +You will then need to run: + +.. code:: php + + $service = $client->dnsService(); + diff --git a/doc/_build/html/_sources/services/dns/domains.txt b/doc/_build/html/_sources/services/dns/domains.txt new file mode 100644 index 000000000..0a7b83b96 --- /dev/null +++ b/doc/_build/html/_sources/services/dns/domains.txt @@ -0,0 +1,288 @@ +Domains +======= + +Get domain +---------- + +To retrieve a specific domain, you will need the domain's **id**, not +its domain name: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +If you are having trouble remembering or accessing the domain ID, you +can do a domain list search for your domain and then access its ID. + + +List domains +------------ + +These calls provide a list of all DNS domains manageable by a given +account. The resulting list is flat, and does not break the domains down +hierarchically by subdomain. All representative domains are included in +the list, even if a domain is conceptually a subdomain of another domain +in the list. + +.. code-block:: php + + $domains = $service->domainList(); + + # Return detailed information for each domain + $domains = $service->domainList(true); + + +Filter parameters +~~~~~~~~~~~~~~~~~ + +You can filter the search by using the ``name`` parameter in a key/value array +supplied as a method argument. For example, to retrieve domains named ``foo.com``, +along with any subdomains like ``bar.foo.com``: + +.. code-block:: php + + $hoolaDomains = $service->domainList(array( + 'name' => 'foo.com' + )); + +Filter criteria may consist of: + +* Any letter (A-Za-z) +* Numbers (0-9) +* Hyphen ("-") +* 1 to 63 characters + +Filter criteria should not include any of the following characters: + + ' + , \| ! " £ $ % & / ( ) = ? ^ \* ç ° § ; : \_ > ] [ @ à, é, ò + + +Finding a domain ID +~~~~~~~~~~~~~~~~~~~ + +Once you have a list of domains, to retrieve a domain's ID: + +.. code-block:: php + + foreach ($domains as $domain) { + $id = $domain->id; + } + + +List domain changes +------------------- + +This call shows all changes to the specified domain since the specified +date/time. To list all available changes for a domain for the current day: + +.. code-block:: php + + $changes = $domain->changes(); + + +For more granular control, you can manually define the ``since`` parameter like +so: + +.. code-block:: php + + $since = date('c', strtotime('last week')); + $changes = $domain->changes($since); + +Once you have a set of changes, you can iterate over them like so: + +.. code-block:: php + + foreach ($changes->changes as $change) { + printf("Domain: %s\nAction: %s\nTarget: %s", $change->domain, $change->action, $change->targetType); + + foreach ($change->changeDetails as $detail) { + printf("Details: %s was changed from %s to %s", $detail->field, $detail->oldValue, $detail->newValue); + } + } + + +Create domain +------------- + +The first thing you will need to do is instantiate a new object and set the +primary ``A`` record for the DNS domain, like so: + +.. code-block:: php + + // get empty object + $domain = $service->domain(); + + // add A record + $aRecord = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $domain->addRecord($aRecord); + +You also have the option of adding more types of DNS records such as ``CNAME``, +``MX`` and ``NS`` records. This step is completely optional and depends on +your requirements: + +.. code-block:: php + + // add CNAME record + $cRecord = $domain->record(array( + 'type' => 'CNAME', + 'name' => 'www.example.com', + 'data' => 'example.com', + 'ttl' => 3600 + )); + $domain->addRecord($cRecord); + + // add MX record + $mxRecord = $domain->record(array( + 'type' => 'MX', + 'data' => 'mail.example.com', + 'name' => 'example.com', + 'ttl' => 3600, + 'priority' => 5 + )); + $domain->addRecord($mxRecord); + + // add NS record + $nsRecord = $domain->record(array( + 'type' => 'NS', + 'data' => 'dns1.stabletransit.com', + 'name' => 'example.com', + 'ttl' => 5400 + )); + $domain->addRecord($nsRecord); + +You can also add sub-domains to your new DNS domain. Again, this is completely +optional: + +.. code-block:: php + + $subdomain = $domain->subdomain(array( + 'emailAddress' => 'foo@example.com', + 'name' => 'dev.example.com', + 'comment' => 'Dev portal' + )); + $domain->addSubdomain($subdomain); + +Once you've finished configuring how your DNS domain will work, you're ready +to specify the essential details and send it to the API for creation: + +.. code-block:: php + + $domain->create(array( + 'emailAddress' => 'webmaster@example.com', + 'ttl' => 3600, + 'name' => 'example.com', + 'comment' => 'Optional comment' + )); + + +Clone domain +------------ + +This call will duplicate an existing domain under a new name. By default, all +records and, optionally, subdomains are duplicated as well. + +The method signature you will need to use is: + +.. function:: cloneDomain( $newDomainName[, $subdomains[, $comments[, $email[, $records]]]] ) + + Clone a domain + + :param string $newDomainName: The name of the new domain entry + :param bool $subdomains: Set to ``true`` to clone all the subdomains for this domain + :param bool $comments: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in comments on the cloned (new) domain. + :param bool $email: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + :param bool $records: Set to ``true`` to replace occurrences of the reference domain name with the new domain name in data fields (of records) on the cloned (new) domain. Does not affect NS records. + + +For example: + +.. code-block:: php + + $asyncResponse = $domain->cloneDomain('new-name.com', true, false, true, false); + + +Export domain +------------- + +This call provides access to the `BIND `_ +(Berkeley Internet Name Domain) 9 for the requested domain. This call is for a +single domain only, and as such, does not traverse up or down the domain +hierarchy for details: + +.. code-block:: php + + $asyncResponse = $domain->export(); + + $body = $asyncResponse->waitFor('COMPLETED'); + echo $body['contents']; + + +Import domain +------------- + +This operation will create a new DNS domain according to a `BIND `_ +(Berkeley Internet Name Domain) 9 formatted value. + +In order for the BIND value to be considered valid, it needs to adhere to the +following rules: + +* Each record starts on a new line and on the first column. If a record will + not fit on one line, use the BIND\_9 line continuation convention where you put + a left parenthesis and continue the one record on the next line and put a right + parenthesis when the record ends. For example: + + example2.net. 3600 IN SOA dns1.stabletransit.com. (sample@rackspace.com. 1308874739 3600 3600 3600 3600) + +* The attribute values of a record must be separated by a single blank or tab. + No other white space characters. + +* If there are any NS records, the data field should not be + ``dns1.stabletransit.com`` or ``dns2.stabletransit.com``. They will result in + "duplicate record" errors. + +For example: + +.. code-block:: php + + $bind9Data = <<import($bind9Data); + + +Modify domain +------------- + +Only the TTL, email address and comment attributes of a domain can be modified. +Records cannot be added, modified, or removed through this API operation - you +will need to use the `add records `__, `modify records +`__ or `remove records `__ +operations respectively. + +.. code-block:: php + + $domain->update(array( + 'ttl' => ($domain->ttl + 100), + 'emailAddress' => 'new_dev@foo.com' + )); + + +Delete domain +------------- + +.. code-block:: php + + $domain->delete(); diff --git a/doc/_build/html/_sources/services/dns/index.txt b/doc/_build/html/_sources/services/dns/index.txt new file mode 100644 index 000000000..39db3d675 --- /dev/null +++ b/doc/_build/html/_sources/services/dns/index.txt @@ -0,0 +1,59 @@ +DNS v1 +====== + +.. include:: ../common/rs-only.sample.rst + +DNS service +~~~~~~~~~~~ + +Now to instantiate the DNS service: + +.. code-block:: php + + $service = $client->dnsService(); + + +Operations +---------- + +.. toctree:: + + records + domains + limits + reverse-dns + + +Glossary +-------- + + domain + A domain is an entity/container of all DNS-related information containing + one or more records. + + record + A DNS record belongs to a particular domain and is used to specify + information about the domain. There are several types of DNS records. Each + record type contains particular information used to describe that record's + purpose. Examples include mail exchange (MX) records, which specify the + mail server for a particular domain, and name server (NS) records, which + specify the authoritative name servers for a domain. + + subdomain + Subdomains are domains within a parent domain, and subdomains cannot be + registered. Subdomains allow you to delegate domains. Subdomains can + themselves have subdomains, so third-level, fourth-level, fifth-level, and + deeper levels of nesting are possible. + + pointer records + DNS usually determines an IP address associated with a domain name. + Reverse DNS is the opposite process: resolving a domain name from an IP + address. This is usually achieved with a domain name pointer. + + +Further Links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/dns/limits.txt b/doc/_build/html/_sources/services/dns/limits.txt new file mode 100644 index 000000000..289c3888d --- /dev/null +++ b/doc/_build/html/_sources/services/dns/limits.txt @@ -0,0 +1,57 @@ +Limits +====== + +List all limits +--------------- + +This call provides a list of all applicable limits for the specified account. + +.. code:: php + + $limits = $service->limits(); + + +Absolute limits +~~~~~~~~~~~~~~~ + +There are some absolute limits imposed on your account - such as how +many domains you can create and how many records you can create for each +domain: + +.. code:: php + + $absoluteLimits = $limits->absolute; + + // Domain limit + echo $absoluteLimits->domains; + + // Record limit per domain + echo $absoluteLimits->{'records per domain'}; + + +List limit types +---------------- + +To find out the different limit types you can query, run: + +.. code:: php + + $limitTypes = $service->limitTypes(); + +will return: + +:: + + array(3) { + [0] => string(10) "RATE_LIMIT" + [1] => string(12) "DOMAIN_LIMIT" + [2] => string(19) "DOMAIN_RECORD_LIMIT" + } + +Query a specific limit +---------------------- + +.. code:: php + + $limit = $service->limits('DOMAIN_LIMIT'); + echo $limit->absolute->limits->value; diff --git a/doc/_build/html/_sources/services/dns/records.txt b/doc/_build/html/_sources/services/dns/records.txt new file mode 100644 index 000000000..26e17ee5e --- /dev/null +++ b/doc/_build/html/_sources/services/dns/records.txt @@ -0,0 +1,113 @@ +Records +======= + +Setup +----- + +In order to interact with the functionality of records, you must first +retrieve the details of the domain itself. To do this, you must substitute +`{domainId}` for your domain's ID: + +.. code-block:: php + + $domain = $service->domain('{domainId}'); + + +Get record +---------- + +In order to retrieve details for a specific DNS record, you will need +its **id**: + +.. code:: php + + $record = $domain->record('{recordId}'); + +If you do not have this ID at your disposal, you can traverse the record +collection and do a string comparison (detailed below). + + +List records +------------ + +This call lists all records configured for the specified domain. + +.. code:: php + + $records = $domain->recordList(); + + foreach ($records as $record) { + printf("Record name: %s, ID: %s, TTL: %s\n", $record->name, $record->id, $record->ttl); + } + + +Query parameters +~~~~~~~~~~~~~~~~ + +You can pass in an array of query parameters for greater control over +your search: + ++------------+--------------+------------------------+ +| Name | Data type | Description | ++============+==============+========================+ +| ``type`` | ``string`` | The record type | ++------------+--------------+------------------------+ +| ``name`` | ``string`` | The record name | ++------------+--------------+------------------------+ +| ``data`` | ``string`` | Data for this record | ++------------+--------------+------------------------+ + + +Find a record ID from its name +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For example: + +.. code:: php + + $records = $domain->recordList(array( + 'name' => 'imap.example.com', + 'type' => 'MX' + )); + + foreach ($records as $record) { + $recordId = $record->id; + } + + +Add record +---------- + +This call adds a new record to the specified domain: + +.. code:: php + + $record = $domain->record(array( + 'type' => 'A', + 'name' => 'example.com', + 'data' => '192.0.2.17', + 'ttl' => 3600 + )); + + $record->create(); + + +Please be aware that records that are added with a different hostname +than the parent domain might fail silently. + +Modify record +------------- + +.. code:: php + + $record = $domain->record('{recordId}'); + $record->ttl -= 100; + $record->update(); + + +Delete record +------------- + +.. code:: php + + $record->delete(); diff --git a/doc/_build/html/_sources/services/dns/reverse-dns.txt b/doc/_build/html/_sources/services/dns/reverse-dns.txt new file mode 100644 index 000000000..4d0eb671c --- /dev/null +++ b/doc/_build/html/_sources/services/dns/reverse-dns.txt @@ -0,0 +1,92 @@ +Reverse DNS +=========== + + +Get PTR record +-------------- + +PTR records refer to a parent device: either a Cloud Server or a Cloud +Load Balancer with a public virtual IP address. You must supply a fully +formed resource object in order to retrieve either one's PTR record: + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptr = $service->ptrRecord(array( + 'parent' => $parent + )); + +So, in the above example, the ``$parent`` object could be an instance of +``OpenCloud\Compute\Resource\Server`` or +``OpenCloud\LoadBalancer\Resource\LoadBalancer`` - because they both +implement ``OpenCloud\DNS\Resource\HadPtrRecordsInterface``. Please +consult the `server documentation <../compute>`__ and `load +balancer documentation <../load-balancer>`__ for more +detailed usage instructions. + + +List PTR records +---------------- + +.. code:: php + + /** @param $parent OpenCloud\DNS\Resource\HasPtrRecordsInterface */ + + $ptrRecords = $service->ptrRecordList($parent); + + foreach ($ptrRecords as $ptrRecord) { + + } + + +Add PTR record +-------------- + +.. code:: php + + $parent = $computeService->server('foo-server-id'); + + $ptr = $dnsService->ptrRecord(array( + 'parent' => $parent, + 'ttl' => 3600, + 'name' => 'example.com', + 'type' => 'PTR', + 'data' => '192.0.2.7' + )); + + $ptr->create(); + +Here is a table that explains the above attributes: + ++-----------+------------------------------------------------------------------------------------+------------+ +| Name | Description | Required | ++===========+====================================================================================+============+ +| type | Specifies the record type as "PTR". | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| name | Specifies the name for the domain or subdomain. Must be a valid domain name. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| data | The data field for PTR records must be a valid IPv4 or IPv6 IP address. | Yes | ++-----------+------------------------------------------------------------------------------------+------------+ +| ttl | If specified, must be greater than 300. Defaults to 3600 if no TTL is specified. | No | ++-----------+------------------------------------------------------------------------------------+------------+ +| comment | If included, its length must be less than or equal to 160 characters. | No | ++-----------+------------------------------------------------------------------------------------+------------+ + + +Modify PTR record +----------------- + +.. code:: php + + $ptr->update(array( + 'ttl' => $ptr->ttl * 2 + )); + + +Delete PTR record +----------------- + +.. code:: php + + $ptr->delete(); diff --git a/doc/_build/html/_sources/services/identity/Roles.md.txt b/doc/_build/html/_sources/services/identity/Roles.md.txt new file mode 100644 index 000000000..96a56488f --- /dev/null +++ b/doc/_build/html/_sources/services/identity/Roles.md.txt @@ -0,0 +1,92 @@ +Roles +===== + +Intro +----- + +A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user's roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service. + +Setup +----- + +Role objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++---------------+------------------------+------------------------+ +| Property | Getter | Setter | ++===============+========================+========================+ +| id | ``getId()`` | ``setId()`` | ++---------------+------------------------+------------------------+ +| name | ``getName()`` | ``setName()`` | ++---------------+------------------------+------------------------+ +| description | ``getDescription()`` | ``setDescription()`` | ++---------------+------------------------+------------------------+ + +List roles +---------- + +This call lists the global roles available within a specified service. + +.. code:: php + + $roles = $service->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get role +-------- + +This call lists detailed information (id, name, description) for a +specified role. + +.. code:: php + + $roleId = '123abc'; + $role = $service->getRole($roleId); + +Add/delete user roles +--------------------- + +To add/remove user roles, you must first instantiate a +`user `__ object: + +.. code:: php + + $roleId = '123abc'; + + // add role to user + $user->addRole($roleId); + + // remove role from user + $user->removeRole($roleId); + +List user global roles +---------------------- + +This call returns a list of global roles associated with a user: + +.. code:: php + + $roles = $user->getRoles(); + + foreach ($roles as $role) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/_build/html/_sources/services/identity/Service.md.txt b/doc/_build/html/_sources/services/identity/Service.md.txt new file mode 100644 index 000000000..a89f19358 --- /dev/null +++ b/doc/_build/html/_sources/services/identity/Service.md.txt @@ -0,0 +1,13 @@ +Identity service +================ + +Intro +----- + +The Identity service is regionless, so you do not need to specify a +region when instantiating the service object. Although this was +primarily based on Rackspace's implementation of Cloud Identity, it +should also work for OpenStack Keystone. + +A note on object creation +------------------------- diff --git a/doc/_build/html/_sources/services/identity/Tenants.md.txt b/doc/_build/html/_sources/services/identity/Tenants.md.txt new file mode 100644 index 000000000..9b58efd1a --- /dev/null +++ b/doc/_build/html/_sources/services/identity/Tenants.md.txt @@ -0,0 +1,29 @@ +Tenants +======= + +Intro +----- + +A tenant is a container used to group or isolate resources and/or +identity objects. Depending on the service operator, a tenant may map to +a customer, account, organization, or project. + +Setup +----- + +Tenant objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +List tenants +------------ + +.. code:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. diff --git a/doc/_build/html/_sources/services/identity/Tokens.md.txt b/doc/_build/html/_sources/services/identity/Tokens.md.txt new file mode 100644 index 000000000..c42ce1573 --- /dev/null +++ b/doc/_build/html/_sources/services/identity/Tokens.md.txt @@ -0,0 +1,105 @@ +Tokens +====== + +Intro +----- + +A token is an opaque string that represents an authorization to access +cloud resources. Tokens may be revoked at any time and are valid for a +finite duration. + +Setup +----- + +Token objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| Property | Description | Getter | Setter | ++============+===========================================+========================================+====================+ +| id | The unique ID of the token | ``getId()`` | ``setId()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ +| expires | Timestamp of when the token will expire | ``getExpires()`` or ``hasExpired()`` | ``setExpires()`` | ++------------+-------------------------------------------+----------------------------------------+--------------------+ + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +Request body for Rackspace's generate token operation: + +.. code:: json + + { + "auth": { + "RAX-KSKEY:apiKeyCredentials": { + "username": "foo", + "apiKey": "aaaaa-bbbbb-ccccc-12345678" + }, + "tenantId": "1100111" + } + } + +Request body for Keystone's generate token operation: + +.. code:: json + + { + "auth": { + "passwordCredentials":{ + "username":"demoauthor", + "password":"theUsersPassword" + }, + "tenantId": "12345678" + } + } + +The only real differences you'll notice is the name of the object key +(``RAX-KSKEY:apiKeyCredentials``/``passwordCredentials``) and the secret +(``apiKey``/``password``). The ``tenantId`` property in both templates +are optional. You can also add ``tenantName`` too. + +.. code:: php + + use OpenCloud\Common\Http\Message\Formatter; + + $template = sprintf( + '{"auth": {"RAX-KSKEY:apiKeyCredentials":{"username": "%s", "apiKey": "%s"}}}', + 'my_username', + 'my_api_key' + ); + + $response = $service->generateToken($template); + + $body = Formatter::decode($response); + + // service catalog + $catalog = $body->access->serviceCatalog; + + // token + $token = $body->access->token; + + // user + $user = $body->access->user; + +As you will notice, these variables will be stdClass objects - for fully +fledged functionality, let the client authenticate by itself because it +ends up stocking the necessary models for you. + +To see the response body structure, consult the `official +docs `__. + +Revoke token (destroy session) +------------------------------ + +.. code:: php + + $tokenId = '1234567'; + $service->revokeToken($tokenId); + diff --git a/doc/_build/html/_sources/services/identity/Users.md.txt b/doc/_build/html/_sources/services/identity/Users.md.txt new file mode 100644 index 000000000..e8c2e6d63 --- /dev/null +++ b/doc/_build/html/_sources/services/identity/Users.md.txt @@ -0,0 +1,170 @@ +Users +===== + +Intro +----- + +A user is a digital representation of a person, system, or service who +consumes cloud services. Users have credentials and may be assigned +tokens; based on these credentials and tokens, the authentication +service validates that incoming requests are being made by the user who +claims to be making the request, and that the user has the right to +access the requested resources. Users may be directly assigned to a +particular tenant and behave as if they are contained within that +tenant. + +Setup +----- + +User objects are instantiated from the Identity service. For more +details, see the `Service `__ docs. + +Useful object properties/methods +-------------------------------- + ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| Property | Description | Getter | Setter | ++=================+===============================================================================================================================================================================================================================================================================================================================+============================================+===============================================================================================================+ +| id | The unique ID for this user | ``getId()`` | ``setId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| username | Username for this user | ``getUsername()`` | ``setUsername()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| email | User's email address | ``getEmail()`` | ``setEmail()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| enabled | Whether or not this user can consume API functionality | ``getEnabled()`` or ``isEnabled()`` | ``setEnabled()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| password | Either a user-defined string, or an automatically generated one, that provides security when authenticating. | ``getPassword()`` only valid on creation | ``setPassword()`` to set local property only. To set password on API (retention), use ``updatePassword()``. | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| defaultRegion | Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region. | ``getDefaultRegion()`` | ``setDefaultRegion()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| domainId | Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account. | ``getDomainId()`` | ``setDomainId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ + +List users +---------- + +.. code:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } + +For more information about how to use iterators, see the +`documentation <../Iterators.md>`__. + +Get user +-------- + +There are various ways to get a specific user: by name, ID and email +address. + +.. code:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + // Get user by name + $user1 = $service->getUser('jamie'); + + // Get user by ID + $user2 = $service->getUser(123456, UserConst::MODE_ID); + + // Get user by email + $user3 = $service->getUser('jamie.hannaford@rackspace.com', UserConst::MODE_EMAIL); + +Create user +----------- + +There are a few things to remember when creating a user: + +- This operation is available only to users who hold the + ``identity:user-admin`` role. This admin can create a user who holds + the ``identity:default`` user role. + +- The created user **will** have access to APIs but **will not** have + access to the Cloud Control Panel. + +- Within an account, a maximum of 100 account users can be added. + +- If you attempt to add a user who already exists, an HTTP error 409 + results. + +The ``username`` and ``email`` properties are required for creating a +user. Providing a ``password`` is optional; if omitted, one will be +automatically generated and provided in the response. + +.. code:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + // execute operation + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + } catch (ClientErrorResponseException $e) { + // catch 4xx HTTP errors + echo $e->getResponse()->toString(); + } + + // show generated password + echo $user->getPassword(); + +Update user +----------- + +When updating a user, specify which attribute/property you want to +update: + +.. code:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); + +Updating a user password +~~~~~~~~~~~~~~~~~~~~~~~~ + +Updating a user password requires calling a distinct method: + +.. code:: php + + $user->updatePassword('password123'); + +Delete user +----------- + +.. code:: php + + $user->delete(); + +List credentials +---------------- + +This operation allows you to see your non-password credential types for +all authentication methods available. + +.. code:: php + + $creds = $user->getOtherCredentials(); + +Get user API key +---------------- + +.. code:: php + + echo $user->getApiKey(); + +Reset user API key +------------------ + +When resetting an API key, a new one will be automatically generated for +you: + +.. code:: php + + $user->resetApiKey(); + echo $user->getApiKey(); + diff --git a/doc/_build/html/_sources/services/identity/index.txt b/doc/_build/html/_sources/services/identity/index.txt new file mode 100644 index 000000000..6cb4dfdf4 --- /dev/null +++ b/doc/_build/html/_sources/services/identity/index.txt @@ -0,0 +1,55 @@ +Identity v2 +=========== + +.. include:: ../common/clients.sample.rst + +Identity service +~~~~~~~~~~~~~~~~ + +Now to instantiate the Identity service: + +.. code-block:: php + + $service = $client->identityService(); + + +Operations +---------- + +.. toctree:: + + tokens + users + tenants + +Glossary +-------- + +.. glossary:: + + token + A token is an opaque string that represents an authorization to access + cloud resources. Tokens may be revoked at any time and are valid for a + finite duration. + + tenant + A tenant is a container used to group or isolate resources and/or + identity objects. Depending on the service operator, a tenant may map to + a customer, account, organization, or project. + + user + A user is a digital representation of a person, system, or service who + consumes cloud services. Users have credentials and may be assigned + tokens; based on these credentials and tokens, the authentication + service validates that incoming requests are being made by the user who + claims to be making the request, and that the user has the right to + access the requested resources. Users may be directly assigned to a + particular tenant and behave as if they are contained within that + tenant. + + +Further Links +------------- + +- `Quickstart for the API `_ +- `API Developer Guide `_ diff --git a/doc/_build/html/_sources/services/identity/roles.txt b/doc/_build/html/_sources/services/identity/roles.txt new file mode 100644 index 000000000..614518b9b --- /dev/null +++ b/doc/_build/html/_sources/services/identity/roles.txt @@ -0,0 +1,78 @@ +Roles +===== + +A role is a personality that a user assumes when performing a specific +set of operations. A role includes a set of rights and privileges. A +user assuming a role inherits the rights and privileges associated with +the role. A token that is issued to a user includes the list of roles +the user can assume. When a user calls a service, that service +determines how to interpret a user's roles. A role that grants access to +a list of operations or resources within one service may grant access to +a completely different list when interpreted by a different service. + +Useful object properties/methods +-------------------------------- + ++---------------+------------------------+------------------------+ +| Property | Getter | Setter | ++===============+========================+========================+ +| id | ``getId()`` | ``setId()`` | ++---------------+------------------------+------------------------+ +| name | ``getName()`` | ``setName()`` | ++---------------+------------------------+------------------------+ +| description | ``getDescription()`` | ``setDescription()`` | ++---------------+------------------------+------------------------+ + +List roles +---------- + +This call lists the global roles available within a specified service. + +.. code-block:: php + + $roles = $service->getRoles(); + + foreach ($roles as $role) { + // ... + } + + +Get role +-------- + +This call lists detailed information (id, name, description) for a +specified role. + +.. code-block:: php + + $role = $service->getRole('{roleId}'); + + +Add/delete user roles +--------------------- + +.. code-block:: php + + $user = $service->getUser('{userId}'); + + $roleId = '{roleId}'; + + // add role to user + $user->addRole($roleId); + + // remove role from user + $user->removeRole($roleId); + + +List user global roles +---------------------- + +This call returns a list of global roles associated with a user: + +.. code-block:: php + + $roles = $user->getRoles(); + + foreach ($roles as $role) { + // ... + } diff --git a/doc/_build/html/_sources/services/identity/tenants.txt b/doc/_build/html/_sources/services/identity/tenants.txt new file mode 100644 index 000000000..a27417cca --- /dev/null +++ b/doc/_build/html/_sources/services/identity/tenants.txt @@ -0,0 +1,26 @@ +Tenants +======= + +List tenants +------------ + +.. code-block:: php + + $tenants = $service->getTenants(); + + foreach ($tenants as $tenant) { + // ... + } + +Tenant object properties and methods +------------------------------------ + +Once you have a ``OpenCloud\Identity\Resource\Tenant`` object, you can retrieve +information like so: + +.. code-block:: php + + $tenant->getId(); + $tenant->getName(); + $tenant->getDescription(); + $tenant->isEnabled(); diff --git a/doc/_build/html/_sources/services/identity/tokens.txt b/doc/_build/html/_sources/services/identity/tokens.txt new file mode 100644 index 000000000..e4601c7c4 --- /dev/null +++ b/doc/_build/html/_sources/services/identity/tokens.txt @@ -0,0 +1,101 @@ +Tokens +====== + +Create token (authenticate) +--------------------------- + +In order to generate a token, you must pass in the JSON template that is +sent to the API. This is because Rackspace's operation expects a +slightly different entity body than OpenStack Keystone. + +To do this, and then generate a token: + +.. code-block:: php + + $json = $client->getCredentials(); + + /** @var $response Guzzle\Http\Message\Response */ + $response = $service->generateToken($json); + $jsonBody = $response->json(); + +When a token is generated by the API, there are a few things returned: + +* a `service catalog `_ + outlining all of the services you can interact with, + including their names, service types, and endpoint URLs. Which services + make up your catalog, and how your catalog is structured, will depend on + your service provider. + +* details about your token, such as its ID, created and expiration date + +* details about your user account + +* details about your tenant + +Interacting with the service catalog +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once you have the ``$jsonBody``, you can construct a ``Catalog`` object for +easier interaction: + +.. code-block:: php + + $data = $jsonBody->access->serviceCatalog; + $catalog = OpenCloud\Common\Service\Catalog::factory($data); + + foreach ($catalog->getItems() as $service) { + /** @param $service OpenCloud\Common\Service\CatalogItem */ + printf("Catalog item: Name [%s] Type [%s]\n", $service->getName(), $service->getType()); + + foreach ($service->getEndpoints() as $endpoint) { + printf(" Endpoint provided: Region [%s] PublicURL [%s] PrivateURL [%s]\n", + $endpoint->getRegion(), $endpoint->getPublicUrl(), $endpoint->getPrivateUrl()); + } + } + +Interacting with tokens +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->token; + $token = $service->resource('Token', $data); + + printf("Token ID: %s - Token expiry %s", $token->getId(), $token->getExpires()); + + if ($token->hasExpired()) { + // ... + } + +Interacting with users +~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->user; + $user = $service->resource('User', $data); + +To see which methods you can call on ``$user`` (which implements +``OpenCloud\Identity\Resource\User``), see our :doc:`user documentation ` +which accompanies this guide. + + +Interacting with tenants +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $data = $jsonBody->access->tenant; + $tenant = $service->resource('Tenant', $data); + +To see which methods you can call on ``$tenant`` (which implements +``OpenCloud\Identity\Resource\Tenant``), see our :doc:`user documentation ` +which accompanies this guide. + + +Revoke token (destroy session) +------------------------------ + +.. code-block:: php + + $service->revokeToken('{tokenId}'); diff --git a/doc/_build/html/_sources/services/identity/users.txt b/doc/_build/html/_sources/services/identity/users.txt new file mode 100644 index 000000000..944888902 --- /dev/null +++ b/doc/_build/html/_sources/services/identity/users.txt @@ -0,0 +1,175 @@ +Users +===== + + +Object properties/methods +------------------------- + ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| Property | Description | Getter | Setter | ++=================+===============================================================================================================================================================================================================================================================================================================================+============================================+===============================================================================================================+ +| id | The unique ID for this user | ``getId()`` | ``setId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| username | Username for this user | ``getUsername()`` | ``setUsername()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| email | User's email address | ``getEmail()`` | ``setEmail()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| enabled | Whether or not this user can consume API functionality | ``getEnabled()`` or ``isEnabled()`` | ``setEnabled()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| password | Either a user-defined string, or an automatically generated one, that provides security when authenticating. | ``getPassword()`` only valid on creation | ``setPassword()`` to set local property only. To set password on API (retention), use ``updatePassword()``. | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| defaultRegion | Default region associates a user with a specific regional datacenter. If a default region has been assigned for this user and that user has **NOT** explicitly specified a region when creating a service object, the user will obtain the service from the default region. | ``getDefaultRegion()`` | ``setDefaultRegion()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ +| domainId | Domain ID associates a user with a specific domain which was assigned when the user was created or updated. A domain establishes an administrative boundary for a customer and a container for a customer's tenants (accounts) and users. Generally, a domainId is the same as the primary tenant id of your cloud account. | ``getDomainId()`` | ``setDomainId()`` | ++-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------+ + +List users +---------- + +.. code-block:: php + + $users = $service->getUsers(); + + foreach ($users as $user) { + // ... + } + +`Get the executable PHP script for this example `_ + + +Retrieve a user by username +--------------------------- + +.. code-block:: php + + $user = $service->getUser('jamie'); + +`Get the executable PHP script for this example `_ + + +Retrieve a user by user ID +-------------------------- + +.. code-block:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{userId}', UserConst::MODE_ID); + +`Get the executable PHP script for this example `_ + + +Retrieve a user by email address +-------------------------------- + +.. code-block:: php + + use OpenCloud\Identity\Constants\User as UserConst; + + $user = $service->getUser('{emailAddress}', UserConst::MODE_EMAIL); + +`Get the executable PHP script for this example `_ + + +Create user +----------- + +There are a few things to bear in mind when creating a user: + +* This operation is available only to users who hold the + ``identity:user-admin`` role. This admin can create a user who holds + the ``identity:default`` user role. + +* The created user **will** have access to APIs but **will not** have + access to the Cloud Control Panel. + +* A maximum of 100 account users can be added per account. + +* If you attempt to add a user who already exists, an HTTP error 409 + results. + + +The ``username`` and ``email`` properties are required for creating a +user. Providing a ``password`` is optional; if omitted, one will be +automatically generated and provided in the response. + + +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + $user = $service->createUser(array( + 'username' => 'newUser', + 'email' => 'foo@bar.com' + )); + + // show generated password + echo $user->getPassword(); + +`Get the executable PHP script for this example `_ + + +Update user +----------- + +When updating a user, specify which attribute/property you want to +update: + +.. code-block:: php + + $user->update(array( + 'email' => 'new_email@bar.com' + )); + + +Updating a user password +------------------------ + +Updating a user password requires calling a distinct method: + +.. code-block:: php + + $user->updatePassword('password123'); + + +Delete user +----------- + +.. code-block:: php + + $user->delete(); + +`Get the executable PHP script for this example `_ + + +List credentials +---------------- + +This operation allows you to see your non-password credential types for +all authentication methods available. + +.. code-block:: php + + $creds = $user->getOtherCredentials(); + + +Get user API key +---------------- + +.. code-block:: php + + echo $user->getApiKey(); + + +Reset user API key +------------------ + +When resetting an API key, a new one will be automatically generated for +you: + +.. code-block:: php + + $user->resetApiKey(); + echo $user->getApiKey(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/image/Images.md.txt b/doc/_build/html/_sources/services/image/Images.md.txt new file mode 100644 index 000000000..ef187f083 --- /dev/null +++ b/doc/_build/html/_sources/services/image/Images.md.txt @@ -0,0 +1,107 @@ +Images +====== + +A virtual machine image is a single file which contains a virtual disk +that has an installed bootable operating system. In the Cloud Images +API, an image is represented by a JSON-encoded data structure (the image +schema) and its raw binary data (the image file). + +An Image is represented by the ``OpenCloud\Image\Resource\Image`` class. + +Setup +----- + +You instantiate an Image object from its ``OpenCloud\Image\Service`` +class, which is available from the OpenStack/Rackspace client: + +.. code:: php + + $service = $client->imageService('cloudImages', 'IAD'); + +View the guides for more information about `clients <../Clients.md>`__ +or `services <../Services.md>`__. + +List images +----------- + +.. code:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Get image details +----------------- + +.. code:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage(''); + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and +``OpenCloud\Image\Resource\Member`` extend the +``AbstractSchemaResource`` abstract class, which offers some unique +functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a way +different than conventional getter/setter methods, and even class +properties. For this reason, they implement SPL's native +```ArrayAccess`` `__ +interface which allows you to access their state as a conventional +array: + +.. code:: php + + $image = $service->getImage(''); + + $id = $image['id']; + $tags = $image['tags']; + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +- If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +- If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +- If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code:: php + + $image->delete(); + diff --git a/doc/_build/html/_sources/services/image/Schemas.md.txt b/doc/_build/html/_sources/services/image/Schemas.md.txt new file mode 100644 index 000000000..8f792c4c0 --- /dev/null +++ b/doc/_build/html/_sources/services/image/Schemas.md.txt @@ -0,0 +1,170 @@ +JSON schemas +============ + +The Cloud Images API supplies json documents describing the JSON-encoded +data structures that represent domain objects, so that a client knows +exactly what to expect in an API response. + +A JSON Schema is represented by the +``OpenCloud\Image\Resource\Schema\Schema`` class. + +Schema types +------------ + +There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema. + +Example response from the API +----------------------------- + +A sample response from the API, for an Images schema might be: + +.. code:: json + + { + "name": "images", + "properties": { + "images": { + "items": { + "type": "array", + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + }, + "schema": {"type": "string"}, + "next": {"type": "string"}, + "first": {"type": "string"} + }, + "links": [ + {"href": "{first}", "rel": "first"}, + {"href": "{next}", "rel": "next"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + +The top-level schema is called ``images``, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level ``images`` object. So we know that it will +take this form: + +.. code:: json + + { + "images": [something...] + } + +Within this object, we can see that it contains an array of anonymous +objects, each of which is called ``image`` and has its own set of nested +properties: + +.. code:: json + + { + "images": [ + { + [object 1...] + }, + { + [object 2...] + }, + { + [object 3...] + } + ] + } + +The structure of these nested objects are defined as another schema - +i.e. a *subschema*. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +``private`` or ``public``), etc. + +.. code:: json + + { + "images": [ + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + } + ] + } + +Each nested property of a schema is represented by the +``OpenCloud\Image\Resource\Schema\Property`` class. + +If you would like to find out more about schemas, Guzzle has good +documentation about `service +descriptions `__, +which is fairly analogous. + +JSON Patch +---------- + +The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in `RFC +6902 `__. + +Requests need to use the +``application/openstack-images-v2.1-json-patch`` content-type. + +In order for the operation to occur, the request entity body needs to +contain a very particular structure: + +:: + + [ + {"op": "replace", "path": "/name", "value": "Fedora 17"}, + {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} + ] + +The ``op`` key refers to the type of Operation (see +``OpenCloud\Image\Enum\OperationType`` for a full list). + +The ``path`` key is a JSON pointer to the document property you want to +modify or insert. JSON pointers are defined in `RFC +6901 `__. + +The ``value`` key is the value. + +Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the `official +documentation `__ +for additional information. diff --git a/doc/_build/html/_sources/services/image/Sharing.md.txt b/doc/_build/html/_sources/services/image/Sharing.md.txt new file mode 100644 index 000000000..b1a1ea382 --- /dev/null +++ b/doc/_build/html/_sources/services/image/Sharing.md.txt @@ -0,0 +1,129 @@ +Sharing images +============== + +Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers: + ++-------------+-----------------+-----------------+ +| Operation | Producer can? | Consumer can? | ++=============+=================+=================+ +| Created | Yes | No | ++-------------+-----------------+-----------------+ +| Deleted | Yes | No | ++-------------+-----------------+-----------------+ +| Updated | No | Yes | ++-------------+-----------------+-----------------+ +| Listed | Yes | Yes | ++-------------+-----------------+-----------------+ + +The producer shares an image with the consumer by making the consumer a +*member* of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer's image list. + +Typical workflow +---------------- + +1. The producer posts the availability of specific images on a public + website. + +2. A potential consumer provides the producer with his/her tenant ID and + email address. + +3. The producer `creates a new Image Member <>`__ with the consumer's + details + +4. The producer notifies the consumer via email that the image has been + shared and provides the image's ID. + +5. If the consumer wishes the image to appear in his/her image list, the + consumer `updates their own Member status <>`__ to ``ACCEPTED``. + +Additional notes +~~~~~~~~~~~~~~~~ + +- If the consumer subsequently wishes to hide the image, the consumer + can change their Member status to ``REJECTED``. + +- If the consumer wishes to hide the image, but is open to the + possibility of being reminded by the producer that the image is + available, the consumer can change their Member status to + ``PENDING``. + +- Image producers add or remove image members, but may not modify the + member status of an image member. + +- Image consumers change their own member status, but may not add or + remove themselves as an image member. + +- Image consumers can boot from any image shared by the image producer, + regardless of the member status, as long as the image consumer knows + the image ID. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +List image members +------------------ + +This operation is available for both producers and consumers. + +.. code:: php + + $members = $image->listMembers(); + + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } + +For more information about working with iterators, please see the +`iterators documentation <../Iterators.md>`__. + +Create image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember($tenantId); + +Delete image member +------------------- + +This operation is only available for producers. + +.. code:: php + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->delete(); + +Update image member status +-------------------------- + +This operation is only available for consumers. + +.. code:: php + + use OpenCloud\Images\Enum\MemberStatus; + + $tenantId = 12345; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember($tenantId); + + $member->updateStatus(MemberStatus::ACCEPTED); + +The acceptable states you may pass in are made available to you through +the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` +class. diff --git a/doc/_build/html/_sources/services/image/Tags.md.txt b/doc/_build/html/_sources/services/image/Tags.md.txt new file mode 100644 index 000000000..af5baf157 --- /dev/null +++ b/doc/_build/html/_sources/services/image/Tags.md.txt @@ -0,0 +1,28 @@ +Image tags +========== + +An image tag is a string of characters used to identify a specific image +or images. + +Setup +----- + +All member operations are executed against an `Image `__, so +you will need to set this up first. + +Add image tag +------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); + diff --git a/doc/_build/html/_sources/services/image/images.txt b/doc/_build/html/_sources/services/image/images.txt new file mode 100644 index 000000000..ec1a17c34 --- /dev/null +++ b/doc/_build/html/_sources/services/image/images.txt @@ -0,0 +1,84 @@ +Images +====== + +List images +----------- + +.. code-block:: php + + $images = $service->listImages(); + + foreach ($images as $image) { + /** @param $image OpenCloud\Image\Resource\Image */ + } + + +Get image details +----------------- + +.. code-block:: php + + /** @param $image OpenCloud\Image\Resource\Image */ + $image = $service->getImage('{imageId}'); + + +A note on schema classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``OpenCloud\Image\Resource\Image`` and ``OpenCloud\Image\Resource\Member`` +extend the ``AbstractSchemaResource`` class, which offers some unique functionality. + +Because these resources are inherently dynamic - i.e. they are modelled +on dynamic JSON schema - you need to access their state in a different way +than conventional getter/setter methods, and even class properties. For this +reason, they implement SPL's native +`ArrayAccess `_ +interface which allows you to access their state as a conventional +array: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + $id = $image['id']; + $tags = $image['tags']; + + +Update image +------------ + +You can only update your own custom images - you cannot update or delete +base images. The way in which you may update your image is dictated by +its `schema `__. + +Although you should be able to add new and replace existing properties, +always prepare yourself for a situation where it might be forbidden: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\ForbiddenOperationException; + + try { + $image->update(array( + 'name' => 'foo', + 'newProperty' => 'bar' + )); + } catch (ForbiddenOperationException $e) { + // A 403 Forbidden was returned + } + +There are three operations that can take place for each Image property: + +* If a ``false`` or ``null`` value is provided, a ``REMOVE`` operation + will occur, removing the property from the JSON document +* If a non-false value is provided and the property does not exist, an + ``ADD`` operation will add it to the document +* If a non-false value is provided and the property does exist, a + ``REPLACE`` operation will modify the property in the document + +Delete image +------------ + +.. code-block:: php + + $image->delete(); diff --git a/doc/_build/html/_sources/services/image/index.txt b/doc/_build/html/_sources/services/image/index.txt new file mode 100644 index 000000000..a7331b430 --- /dev/null +++ b/doc/_build/html/_sources/services/image/index.txt @@ -0,0 +1,50 @@ +Images v1 +========= + +.. include:: ../common/clients.sample.rst + +Images service +~~~~~~~~~~~~~~ + +Now to instantiate the Images service: + +.. code-block:: php + + $service = $client->imageService(null, '{region}'); + + +Operations +---------- + +.. toctree:: + + images + schemas + sharing + tags + + +Glossary +-------- + + image + A virtual machine image is a single file which contains a virtual disk + that has an installed bootable operating system. In the Cloud Images + API, an image is represented by a JSON-encoded data structure (the image + schema) and its raw binary data (the image file). + + schema + The Cloud Images API supplies JSON documents describing the JSON-encoded + data structures that represent domain objects, so that a client knows + exactly what to expect in an API response. + + tag + An image tag is a string of characters used to identify a specific image + or images. + + +Further Links +------------- + + - `Getting Started Guide for the API `_ + - `API Developer Guide `_ diff --git a/doc/_build/html/_sources/services/image/schemas.txt b/doc/_build/html/_sources/services/image/schemas.txt new file mode 100644 index 000000000..c1b85b8de --- /dev/null +++ b/doc/_build/html/_sources/services/image/schemas.txt @@ -0,0 +1,163 @@ +JSON schemas +============ + +Schema types +------------ + +There are currently four types of schema: Images schema, Image schema, +Members schema, and Member schema. + +Example response from the API +----------------------------- + +A sample response from the API, for an Images schema might be: + +.. code-block:: json + + { + "name": "images", + "properties": { + "images": { + "items": { + "type": "array", + "name": "image", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "visibility": {"enum": ["public", "private"]}, + "status": {"type": "string"}, + "protected": {"type": "boolean"}, + "tags": { + "type": "array", + "items": {"type": "string"} + }, + "checksum": {"type": "string"}, + "size": {"type": "integer"}, + "created_at": {"type": "string"}, + "updated_at": {"type": "string"}, + "file": {"type": "string"}, + "self": {"type": "string"}, + "schema": {"type": "string"} + }, + "additionalProperties": {"type": "string"}, + "links": [ + {"href": "{self}", "rel": "self"}, + {"href": "{file}", "rel": "enclosure"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + }, + "schema": {"type": "string"}, + "next": {"type": "string"}, + "first": {"type": "string"} + }, + "links": [ + {"href": "{first}", "rel": "first"}, + {"href": "{next}", "rel": "next"}, + {"href": "{schema}", "rel": "describedby"} + ] + } + +The top-level schema is called ``images``, and contains an array of +links and a properties object. Inside this properties object we see the +structure of this top-level ``images`` object. So we know that it will +take this form: + +.. code-block:: json + + { + "images": [something...] + } + +Within this object, we can see that it contains an array of anonymous +objects, each of which is called ``image`` and has its own set of nested +properties: + +.. code-block:: json + + { + "images": [ + { + [object 1...] + }, + { + [object 2...] + }, + { + [object 3...] + } + ] + } + +The structure of these nested objects are defined as another schema - +i.e. a *subschema*. We know that each object has an ID property +(string), a name property (string), a visibility property (can either be +``private`` or ``public``), etc. + +.. code-block:: json + + { + "images": [ + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + }, + { + "id": "foo", + "name": "bar", + "visibility": "private", + // etc. + } + ] + } + +Each nested property of a schema is represented by the +``OpenCloud\Image\Resource\Schema\Property`` class. + +If you would like to find out more about schemas, Guzzle has good +documentation about `service +descriptions `__, +which is fairly analogous. + +JSON Patch +---------- + +The Glance API has a unique way of updating certain dynamic resources: +they use JSON Patch method, as outlined in `RFC +6902 `__. + +Requests need to use the +``application/openstack-images-v2.1-json-patch`` content-type. + +In order for the operation to occur, the request entity body needs to +contain a very particular structure: + +.. code-block:: json + + [ + {"op": "replace", "path": "/name", "value": "Fedora 17"}, + {"op": "replace", "path": "/tags", "value": ["fedora", "beefy"]} + ] + +* The ``op`` key refers to the type of Operation (see + ``OpenCloud\Image\Enum\OperationType`` for a full list). + +* The ``path`` key is a JSON pointer to the document property you want to + modify or insert. JSON pointers are defined in `RFC + 6901 `__. + +* The ``value`` key is the value. + +Because this is all handled for you behind the scenes, we will not go +into exhaustive depth about how this operation is handled. You can +browse the source code, consult the various RFCs and the `official +documentation `__ +for additional information. diff --git a/doc/_build/html/_sources/services/image/sharing.txt b/doc/_build/html/_sources/services/image/sharing.txt new file mode 100644 index 000000000..5edf63ab4 --- /dev/null +++ b/doc/_build/html/_sources/services/image/sharing.txt @@ -0,0 +1,127 @@ +Sharing images +============== + +Images can be created and deleted by image producers, updated by image +consumers, and listed by both image producers and image consumers: + ++-------------+-----------------+-----------------+ +| Operation | Producer can? | Consumer can? | ++=============+=================+=================+ +| Created | Yes | No | ++-------------+-----------------+-----------------+ +| Deleted | Yes | No | ++-------------+-----------------+-----------------+ +| Updated | No | Yes | ++-------------+-----------------+-----------------+ +| Listed | Yes | Yes | ++-------------+-----------------+-----------------+ + +The producer shares an image with the consumer by making the consumer a +*member* of that image. The consumer then accepts or rejects the image +by changing the member status. Once accepted, the image appears in the +consumer's image list. + +Typical workflow +---------------- + +1. The producer posts the availability of specific images on a public + website. + +2. A potential consumer provides the producer with his/her tenant ID and + email address. + +3. The producer `creates a new Image Member <>`__ with the consumer's + details + +4. The producer notifies the consumer via email that the image has been + shared and provides the image's ID. + +5. If the consumer wishes the image to appear in his/her image list, the + consumer `updates their own Member status <>`__ to ``ACCEPTED``. + +Additional notes +~~~~~~~~~~~~~~~~ + +- If the consumer subsequently wishes to hide the image, the consumer + can change their Member status to ``REJECTED``. + +- If the consumer wishes to hide the image, but is open to the + possibility of being reminded by the producer that the image is + available, the consumer can change their Member status to + ``PENDING``. + +- Image producers add or remove image members, but may not modify the + member status of an image member. + +- Image consumers change their own member status, but may not add or + remove themselves as an image member. + +- Image consumers can boot from any image shared by the image producer, + regardless of the member status, as long as the image consumer knows + the image ID. + +Setup +----- + +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + +List image members +------------------ + +This operation is available for both producers and consumers. + +.. code-block:: php + + $members = $image->listMembers(); + + foreach ($members as $member) { + /** @param $member OpenCloud\Image\Resource\Member */ + } + + +Create image member +------------------- + +This operation is only available for producers. + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->createMember('{tenantId}'); + + +Delete image member +------------------- + +This operation is only available for producers. + +.. code-block:: php + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); + $member->delete(); + + +Update image member status +-------------------------- + +This operation is only available for consumers. + +.. code-block:: php + + use OpenCloud\Images\Enum\MemberStatus; + + /** @param $member OpenCloud\Image\Resource\Member */ + $member = $image->getMember('{tenantId}'); + + $member->updateStatus(MemberStatus::ACCEPTED); + +The acceptable states you may pass in are made available to you through +the constants defined in the ``OpenCloud\Images\Enum\MemberStatus`` +class. diff --git a/doc/_build/html/_sources/services/image/tags.txt b/doc/_build/html/_sources/services/image/tags.txt new file mode 100644 index 000000000..0a376e6de --- /dev/null +++ b/doc/_build/html/_sources/services/image/tags.txt @@ -0,0 +1,29 @@ +Image tags +========== + +Setup +----- + +All member operations are executed against an `Image `__, so you will +need to set one up first: + +.. code-block:: php + + $image = $service->getImage('{imageId}'); + + +Add image tag +------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->addTag('jamie_dev'); + +Delete image tag +---------------- + +.. code-block:: php + + /** @param $response Guzzle\Http\Message\Response */ + $response = $image->deleteTag('jamie_dev'); diff --git a/doc/_build/html/_sources/services/index.txt b/doc/_build/html/_sources/services/index.txt new file mode 100644 index 000000000..52a449084 --- /dev/null +++ b/doc/_build/html/_sources/services/index.txt @@ -0,0 +1,4 @@ +.. toctree:: + :maxdepth: 2 + + autoscale/index.rst diff --git a/doc/_build/html/_sources/services/load-balancer/README.md.txt b/doc/_build/html/_sources/services/load-balancer/README.md.txt new file mode 100644 index 000000000..862cf20d2 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/README.md.txt @@ -0,0 +1,92 @@ +Load Balancers +============== + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Getting started +--------------- + +1. Instantiate a Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Retrieve the server instances you want to add as nodes of the load balancer. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $computeService = $client->computeService('cloudServersOpenStack', 'DFW'); + + $serverOne = $computeService->server('e836fc4e-056d-4447-a80e-fefcaa640216'); + $serverTwo = $computeService->server('5399cd36-a23f-41a6-bdf7-20902aec0e74'); + +The example above uses two server instances that have already been +created. It retrieves the server instances using their IDs. See also: +`creating server instances <>`__. + +3. Obtain a Load Balancer service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This object will be used to first define the load balancer nodes and +later create the load balancer itself. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +4. Define a load balancer node for each server. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + +In the example above, each node runs a service that listens on port +8080. Further, each node will start out as ``ENABLED``, which means it +will be ready to receive network traffic from the load balancer as soon +as it is created. + +5. Create the load balancer with the two nodes. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +In the example above, the load balancer will have a virtual IP address +accessible from the public Internet. Also notice that the port the load +balancer listens on (80) does not need to match the ports of its nodes +(8080). + +Next steps +---------- + +Once you have created a load balancer, there is a lot you can do with +it. See the `complete user guide for load balancers `__. diff --git a/doc/_build/html/_sources/services/load-balancer/USERGUIDE.md.txt b/doc/_build/html/_sources/services/load-balancer/USERGUIDE.md.txt new file mode 100644 index 000000000..788651f2b --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/USERGUIDE.md.txt @@ -0,0 +1,840 @@ +The Complete User Guide to Load Balancers +========================================= + +Prerequisites +------------- + +Client +~~~~~~ + +To use the load balancers service, you must first instantiate a +``Rackspace`` client object. + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Load Balancer Service +~~~~~~~~~~~~~~~~~~~~~ + +All operations on load balancers are done via a load balancer service +object. + +.. code:: php + + $loadBalancerService = $client->loadBalancerService('cloudLoadBalancers', 'DFW'); + +Cloud Servers +~~~~~~~~~~~~~ + +Many of the examples in this document use two cloud servers as nodes for +the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer +to these two cloud servers. + +Load Balancers +-------------- + +A **load balancer** is a device that distributes incoming network +traffic amongst multiple back-end systems. These back-end systems are +called the **nodes** of the load balancer. + +Create Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer(); + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My smart load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode) + )); + +List Load Balancer Details +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single load balancer's details by using its ID. + +.. code:: php + + $loadBalancer = $loadBalancerService->loadBalancer('254889'); + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + +List Load Balancers +~~~~~~~~~~~~~~~~~~~ + +You can retrieve a list of all your load balancers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $loadBalancers = $loadBalancerService->loadBalancerList(); + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + +Update Load Balancer Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +Updating a single attribute of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name' + )); + +Updating multiple attributes of a load balancer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it. + +.. code:: php + + $loadBalancer->delete(); + +Nodes +----- + +A **node** is a backend device that provides a service on specified IP +and port. An example of a load balancer node might be a web server +serving HTTP traffic on port 8080. + +A load balancer typically has multiple nodes attached to it so it can +distribute incoming network traffic amongst them. + +List Nodes +~~~~~~~~~~ + +You can list the nodes attached to a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodes = $loadBalancer->nodeList(); + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + +Add Nodes +~~~~~~~~~ + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| --------------\| -------------- \| +----------------- \| +|  1 \| IP address of node \| String \| Yes \| - \| +|  2 \| Port used by node's service \| Integer \| Yes \| - \| +|  3 \| Starting condition of node: +|  4 \| Type of node to add: +|  5 \| Weight, between 1 and 100, given to node when distributing +traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the +``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. \| Integer \| +No \| 1 \| + +Modify Nodes +~~~~~~~~~~~~ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +Modifying a single attribute of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + + $node->update(array( + 'condition' => NodeCondition::DISABLED + )); + +Modifying multiple attributes of a node +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + +Remove Nodes +~~~~~~~~~~~~ + +There are two ways to remove a node. + +Given an ``OpenCloud\LoadBalancer\Resource\Node`` instance +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $node->delete(); + +Given an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` instance and a node ID +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: php + + $loadBalancer->removeNode(490639); + +The ``removeNode`` method, as shown above, accepts the following +arguments: + ++------------+---------------+-------------+-------------+-----------------+ +| Position | Description | Data type | Required? | Default value | ++============+===============+=============+=============+=================+ +| 1 | ID of node | Integer | Yes | - | ++------------+---------------+-------------+-------------+-----------------+ + +View Node Service Events +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can view events associated with the activity between a node and a +load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } + +Virtual IPs +----------- + +A **virtual IP (VIP)** makes a load balancer accessible by clients. The +load balancing service supports either a public VIP address +(``PUBLIC``), routable on the public Internet, or a ServiceNet VIP +address (``SERVICENET``), routable only within the region in which the +load balancer resides. + +List Virtual IPs +~~~~~~~~~~~~~~~~ + +You can list the VIPs associated with a load balancer. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $vips = $loadBalancer->virtualIpList(); + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + +Add Virtual IPv6 +~~~~~~~~~~~~~~~~ + +You can add additional IPv6 VIPs to a load balancer. + +.. code:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +The ``addVirtualIp`` method, as shown above, accepts the following +arguments: + +| Position \| Description \| Data type \| Required? \| Default value \| +| ----------- \| --------------- \| -------------- \|-------------- \| +----------------- \| +|  1 \| Type of VIP: +|  2 \| IP version: Must be ``6`` \| Integer \| Yes \| - \| + +Remove Virtual IPs +~~~~~~~~~~~~~~~~~~ + +You can remove a VIP from a load balancer. + +.. code:: php + + $vip->remove(); + +Please note that a load balancer must have at least one VIP associated +with it. If you try to remove a load balancer's last VIP, a +``ClientErrorResponseException`` will be thrown. + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported load balancing algorithms using a load +balancer service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $algorithms = $loadBalancerService->algorithmList(); + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols using a load balancer +service object. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $protocols = $loadBalancerService->protocolList(); + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + +Session Persistence +------------------- + +**Session persistence** is a feature of the load balancing service that +forces multiple requests, of the same protocol, from clients to be +directed to the same node. This is common with many web applications +that do not inherently share application state between back-end servers. + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +List Session Persistence Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistenceType = $sessionPersistence->persistenceType; + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + +Enable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + +Disable Session Persistence +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); + +Connection Logging +------------------ + +The **connection logging** feature allows logs to be delivered to a +Cloud Files account every hour. For HTTP-based protocol traffic, these +are Apache-style access logs. For all other traffic, this is connection +and transfer logging. + +Check Logging Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $connectionLogging bool **/ + + $connectionLogging = $loadBalancer->hasConnectionLogging(); + +In the example above: + +- If connection logging is enabled, the value of ``$connectionLogging`` + is ``true``. +- If connection logging is disabled, the value of + ``$connectionLogging`` is ``false``. + +Enable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(true); + +Disable Connection Logging +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableConnectionLogging(false); + +Error Page +---------- + +An **error page** is the html file that is shown to the end user when an +error in the service has been thrown. By default every virtual server is +provided with the default error file. It is also possible to set a +custom error page for a load balancer. + +View Error Page Content +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + +Set Custom Error Page +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + +Delete Custom Error Page +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); + +Allowed Domains +--------------- + +**Allowed domains** are a restricted set of domain names that are +allowed to add load balancer nodes. + +List Allowed Domains +~~~~~~~~~~~~~~~~~~~~ + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code:: php + + $allowedDomains = $loadBalancerService->allowedDomainList(); + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + +Access Lists +------------ + +**Access Lists** allow fine-grained network access to a load balancer's +VIP. Using access lists, network traffic to a load balancer's VIP can be +allowed or denied from a single IP address, multiple IP addresses or +entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` +network items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +View Access List +~~~~~~~~~~~~~~~~ + +You can view a load balancer's access list. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $accessList = $loadBalancer->accessList(); + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + +Add Network Items To Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can add network items to a load balancer's access list very easily: + +.. code:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + +Remove Network Item From Access List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You an remove a network item from a load balancer's access list. + +.. code:: php + + $networkItem->delete(); + +Content Caching +--------------- + +When **content caching** is enabled on a load balancer, +recently-accessed files are stored on the load balancer for easy +retrieval by web clients. Requests to the load balancer for these files +are serviced by the load balancer itself, which reduces load off its +back-end nodes and improves response times as well. + +Check Content Caching Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $contentCaching bool **/ + + $contentCaching = $loadBalancer->hasContentCaching(); + +In the example above: + +- If content caching is enabled, the value of ``$contentCaching`` is + ``true``. +- If content caching is disabled, the value of ``$contentCaching`` is + ``false``. + +Enable Content Caching +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(true); + +Disable Content Caching +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $loadBalancer->enableContentCaching(false); + +SSL Termination +--------------- + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +View current SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + + $sslConfig = $loadBalancer->SSLTermination(); + +Update SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Delete SSL termination config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $sslConfig->delete(); + +Metadata +-------- + +Metadata can be associated with each load balancer and each node for the +client's personal use. It is defined using key-value pairs where the key +and value consist of alphanumeric characters. A key is unique per load +balancer. + +List metadata +~~~~~~~~~~~~~ + +.. code:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + +Please consult the `iterator +documentation `__ for more information +about iterators. + +Add metadata +~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + +Modify metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + +Remove metadata +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadataItem->delete(); + +Monitors +-------- + +The load balancing service includes a health monitoring operation which +periodically checks your back-end nodes to ensure they are responding +correctly. If a node is not responding, it is removed from rotation +until the health monitor determines that the node is functional. In +addition to being performed periodically, the health check also is +performed against every node that is added to ensure that the node is +operating properly before allowing it to service traffic. Only one +health monitor is allowed to be enabled on a load balancer at a time. + +.. code:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +Update or delete +~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + // Delete + $healthMonitor->delete(); + +Statistics +---------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect\_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load + balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive\_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + + $stats = $loadBalancer->stats(); + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } + diff --git a/doc/_build/html/_sources/services/load-balancer/access.txt b/doc/_build/html/_sources/services/load-balancer/access.txt new file mode 100644 index 000000000..42cd68315 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/access.txt @@ -0,0 +1,84 @@ +Allowed Domains +=============== + +List Allowed Domains +-------------------- + +You can list all allowed domains using a load balancer service object. +An instance of ``OpenCloud\Common\Collection\PaginatedIterator`` is +returned. + +.. code-block:: php + + $allowedDomains = $service->allowedDomainList(); + + foreach ($allowedDomains as $allowedDomain) { + /** @var $allowedDomain OpenCloud\LoadBalancer\Resource\AllowedDomain **/ + } + + +Access Lists +============ + +Access Lists allow fine-grained network access to a load balancer's VIP. Using +access lists, network traffic to a load balancer's VIP can be allowed or denied +from a single IP address, multiple IP addresses or entire network subnets. + +Note that ``ALLOW`` network items will take precedence over ``DENY`` network +items in an access list. + +To reject traffic from all network items except those with the ``ALLOW`` +type, add a ``DENY`` network item with the address of ``0.0.0.0/0``. + +.. include:: lb-setup.sample.rst + + +View Access List +---------------- + +You can view a load balancer's access list: + +.. code-block:: php + + $accessList = $loadBalancer->accessList(); + + foreach ($accessList as $networkItem) { + /** @var $networkItem OpenCloud\LoadBalancer\Resource\Access **/ + } + + +Add Network Items To Access List +-------------------------------- + +You can add network items to a load balancer's access list very easily: + +.. code-block:: php + + $loadBalancer->createAccessList(array( + (object) array( + 'type' => 'ALLOW', + 'address' => '206.160.165.1/24' + ), + (object) array( + 'type' => 'DENY', + 'address' => '0.0.0.0/0' + ) + )); + +In the above example, we allowed access for 1 IP address, and used the +"0.0.0.0" wildcard to blacklist all other traffic. + +Get the executable PHP scripts for this example: + +* `Blacklist IP range `_ +* `Limit access to 1 IP `_ + + +Remove Network Item From Access List +------------------------------------ + +You an remove a network item from a load balancer's access list: + +.. code-block:: php + + $networkItem->delete(); diff --git a/doc/_build/html/_sources/services/load-balancer/caching.txt b/doc/_build/html/_sources/services/load-balancer/caching.txt new file mode 100644 index 000000000..6678e048a --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/caching.txt @@ -0,0 +1,35 @@ +Content Caching +=============== + +When content caching is enabled on a load balancer, recently-accessed files are +stored on the load balancer for easy retrieval by web clients. Requests to the +load balancer for these files are serviced by the load balancer itself, which +reduces load off its back-end nodes and improves response times as well. + + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $contentCaching = $loadBalancer->hasContentCaching(); + + +Enable Content Caching +---------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(true); + + +Disable Content Caching +----------------------- + +.. code-block:: php + + $loadBalancer->enableContentCaching(false); diff --git a/doc/_build/html/_sources/services/load-balancer/errors.txt b/doc/_build/html/_sources/services/load-balancer/errors.txt new file mode 100644 index 000000000..041c19d45 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/errors.txt @@ -0,0 +1,44 @@ +Error Pages +=========== + +.. include:: lb-setup.sample.rst + +An error page is the html file that is shown to the end user when an error in +the service has been thrown. By default every virtual server is provided with +the default error file. It is also possible to set a custom error page for a +load balancer. + + +View Error Page Content +----------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPageContent = $errorPage->content; + + /** @var $errorPageContent string **/ + +In the example above the value of ``$errorPageContent`` is the HTML for +that page. This could either be the HTML of the default error page or of +your custom error page. + + +Set Custom Error Page +--------------------- + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->update(array( + 'content' => '' + )); + + +Delete Custom Error Page +------------------------ + +.. code-block:: php + + $errorPage = $loadBalancer->errorPage(); + $errorPage->delete(); diff --git a/doc/_build/html/_sources/services/load-balancer/index.txt b/doc/_build/html/_sources/services/load-balancer/index.txt new file mode 100644 index 000000000..9ea7614ad --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/index.txt @@ -0,0 +1,96 @@ +Load Balancer v1 +================ + +.. include:: ../common/rs-only.sample.rst + + +Load Balancer service +~~~~~~~~~~~~~~~~~~~~~ + +Now to instantiate the Load Balancer service: + +.. code-block:: php + + $service = $client->loadBalancerService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + load-balancer + nodes + virtual-ips + access + caching + errors + logging + monitors + metadata + sessions + ssl + stats + + +Glossary +-------- + + allowed domain + Allowed domains are a restricted set of domain names that are allowed to add + load balancer nodes. + + content caching + When content caching is enabled on a load balancer, recently-accessed files + are stored on the load balancer for easy retrieval by web clients. Requests to + the load balancer for these files are serviced by the load balancer itself, + which reduces load off its back-end nodes and improves response times as well. + + health monitor + The load balancing service includes a health monitoring operation which + periodically checks your back-end nodes to ensure they are responding + correctly. If a node is not responding, it is removed from rotation until the + health monitor determines that the node is functional. In addition to being + performed periodically, the health check also is performed against every node + that is added to ensure that the node is operating properly before allowing it + to service traffic. Only one health monitor is allowed to be enabled on a load + balancer at a time. + + load balancer + A load balancer is a device that distributes incoming network + traffic amongst multiple back-end systems. These back-end systems are + called the nodes of the load balancer. + + metadata + Metadata can be associated with each load balancer and each node for the + client's personal use. It is defined using key-value pairs where the key + and value consist of alphanumeric characters. A key is unique per load + balancer. + + node + A node is a backend device that provides a service on specified IP and port. + An example of a load balancer node might be a web server serving HTTP + traffic on port 8080. A load balancer typically has multiple nodes attached + to it so it can distribute incoming network traffic amongst them. + + session persistence + Session persistence is a feature of the load balancing service that forces + multiple requests, of the same protocol, from clients to be directed to the + same node. This is common with many web applications that do not inherently + share application state between back-end servers. + + virtual IP + A virtual IP (VIP) makes a load balancer accessible by clients. The + load balancing service supports either a public VIP address + (``PUBLIC``), routable on the public Internet, or a ServiceNet VIP + address (``SERVICENET``), routable only within the region in which the + load balancer resides. + + +Further Links +------------- + +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/load-balancer/lb-setup.sample.txt b/doc/_build/html/_sources/services/load-balancer/lb-setup.sample.txt new file mode 100644 index 000000000..d82c80a1f --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/lb-setup.sample.txt @@ -0,0 +1,9 @@ +Setup +----- + +In order to interact with this feature you must first retrieve a particular +load balancer, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer('{id}'); diff --git a/doc/_build/html/_sources/services/load-balancer/load-balancer.txt b/doc/_build/html/_sources/services/load-balancer/load-balancer.txt new file mode 100644 index 000000000..d7867755d --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/load-balancer.txt @@ -0,0 +1,171 @@ +Load Balancer +============= + +.. note:: + + Many of the examples in this document use two cloud servers as nodes for + the load balancer. The variables ``$serverOne`` and ``$serverTwo`` refer + to these two cloud servers. + + +Create Load Balancer +-------------------- + +The first step is to instantiate an empty object, like so: + +.. code-block:: php + + $loadBalancer = $service->loadBalancer(); + +In essence, all a load balancer does is evenly distribute traffic between +various back-end nodes - which can be Compute or Database instances. So we will +need to add a few when creating our load balancer: + +.. code-block:: php + + $serverOneNode = $loadBalancer->node(); + $serverOneNode->address = $serverOne->addresses->private[0]->addr; + $serverOneNode->port = 8080; + $serverOneNode->condition = 'ENABLED'; + + $serverTwoNode = $loadBalancer->node(); + $serverTwoNode->address = $serverTwo->addresses->private[0]->addr; + $serverTwoNode->port = 8080; + $serverTwoNode->condition = 'ENABLED'; + + +All that remains is apply final configuration touches, such as name and the +port number, before submitting to the API: + +.. code-block:: php + + $loadBalancer->addVirtualIp('PUBLIC'); + $loadBalancer->create(array( + 'name' => 'My load balancer', + 'port' => 80, + 'protocol' => 'HTTP', + 'nodes' => array($serverOneNode, $serverTwoNode), + 'algorithm' => 'ROUND_ROBIN', + )); + +For a full list of available `protocols <#protocols>`_ and `algorithms <#algorithms>`_ +please see the sections below. + +`Get the executable PHP script for this example `_ + + +Get Load Balancer Details +------------------------- + +You can retrieve a single load balancer's details by using its ID: + +.. code-block:: php + + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + $loadBalancer = $service->loadBalancer('{loadBalancerId}'); + + +List Load Balancers +------------------- + +You can retrieve a list of all your load balancers: + +.. code-block:: php + + $loadBalancers = $service->loadBalancerList(); + + foreach ($loadBalancers as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + } + +`Get the executable PHP script for this example `_ + + +Update a Load Balancer +---------------------- + +You can update one or more of the following load balancer attributes: + +- ``name``: The name of the load balancer +- ``algorithm``: The algorithm used by the load balancer to distribute + traffic amongst its nodes. See also: `Load balancing + algorithms <#algorithms>`__. +- ``protocol``: The network protocol used by traffic coming in to the + load balancer. See also: `Protocols <#protocols>`__. +- ``port``: The network port on which the load balancer listens for + incoming traffic. +- ``halfClosed``: Enable or Disable Half-Closed support for the load + balancer. +- ``timeout``: The timeout value for the load balancer to communicate + with its nodes. +- ``httpsRedirect``: Enable or disable HTTP to HTTPS redirection for + the load balancer. When enabled, any HTTP request will return status + code 301 (Moved Permanently), and the requestor will be redirected to + the requested URL via the HTTPS protocol on port 443. For example, + http://example.com/page.html would be redirected to https:// + example.com/page.html. Only available for HTTPS protocol (``port`` = + 443), or HTTP Protocol with a properly configured SSL Termination + (\`secureTrafficOnly=true, securePort=443). See also: `SSL + Termination <#ssl-termination>`__. + +.. code-block:: php + + $loadBalancer->update(array( + 'name' => 'New name', + 'algorithm' => 'ROUND_ROBIN' + )); + + +Remove Load Balancer +~~~~~~~~~~~~~~~~~~~~ + +When you no longer have a need for the load balancer, you can remove it: + +.. code-block:: php + + $loadBalancer->delete(); + +`Get the executable PHP script for this example `_ + + +Protocols +--------- + +When a load balancer is created a network protocol must be specified. +This network protocol should be based on the network protocol of the +back-end service being load balanced. Common protocols are ``HTTP``, ``HTTPS`` +and ``MYSQL``. A full list is available `here `_. + +List Load Balancing Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all supported network protocols like so: + +.. code-block:: php + + $protocols = $service->protocolList(); + + foreach ($protocols as $protocol) { + /** @var $protocol OpenCloud\LoadBalancer\Resource\Protocol **/ + } + + +Algorithms +---------- + +Load balancers use an **algorithm** to determine how incoming traffic is +distributed amongst the back-end nodes. A full list is available `here +`_. + +List Load Balancing Algorithms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can programmatically list all supported load balancing algorithms: + +.. code-block:: php + + $algorithms = $service->algorithmList(); + + foreach ($algorithms as $algorithm) { + /** @var $algorithm OpenCloud\LoadBalancer\Resource\Algorithm **/ + } diff --git a/doc/_build/html/_sources/services/load-balancer/logging.txt b/doc/_build/html/_sources/services/load-balancer/logging.txt new file mode 100644 index 000000000..29c37aee2 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/logging.txt @@ -0,0 +1,33 @@ +Connection Logging +================== + +The connection logging feature allows logs to be delivered to a Cloud Files +account every hour. For HTTP-based protocol traffic, these are Apache-style +access logs. For all other traffic, this is connection and transfer logging. + +.. include:: lb-setup.sample.rst + + +Check Configuration +------------------- + +.. code-block:: php + + // TRUE if enabled, FALSE if not + $connectionLogging = $loadBalancer->hasConnectionLogging(); + + +Enable Connection Logging +------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(true); + + +Disable Connection Logging +-------------------------- + +.. code-block:: php + + $loadBalancer->enableConnectionLogging(false); diff --git a/doc/_build/html/_sources/services/load-balancer/metadata.txt b/doc/_build/html/_sources/services/load-balancer/metadata.txt new file mode 100644 index 000000000..2b38de129 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/metadata.txt @@ -0,0 +1,46 @@ +Metadata +======== + +.. include:: lb-setup.sample.rst + +List metadata +------------- + +.. code-block:: php + + $metadataList = $loadBalancer->metadataList(); + + foreach ($metadataList as $metadataItem) { + printf("Key: %s, Value: %s", $metadataItem->key, $metadataItem->value); + } + + +Add metadata +------------ + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata(); + $metadataItem->create(array( + 'key' => 'foo', + 'value' => 'bar' + )); + + +Modify metadata +--------------- + +.. code-block:: php + + $metadataItem = $loadBalancer->metadata('foo'); + $metadataItem->update(array( + 'value' => 'baz' + )); + + +Remove metadata +--------------- + +.. code-block:: php + + $metadataItem->delete(); diff --git a/doc/_build/html/_sources/services/load-balancer/monitors.txt b/doc/_build/html/_sources/services/load-balancer/monitors.txt new file mode 100644 index 000000000..9c463f270 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/monitors.txt @@ -0,0 +1,43 @@ +Health Monitors +=============== + +.. include:: lb-setup.sample.rst + +Retrieve monitor details +------------------------ + +.. code-block:: php + + /** @var $healthMonitor OpenCloud\LoadBalancer\Resource\HealthMonitor **/ + + $healthMonitor = $loadBalancer->healthMonitor(); + + printf( + "Monitoring type: %s, delay: %s, timeout: %s, attempts before deactivation: %s", + $healthMonitor->type, $healthMonitor->delay, $healthMonitor->timeout + ); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + + +Update monitor +-------------- + +.. code-block:: php + + $healthMonitor->update(array( + 'delay' => 120, + 'timeout' => 60, + 'type' => 'CONNECT' + 'attemptsBeforeDeactivation' => 3 + )); + + +Delete monitor +-------------- + +.. code-block:: php + + $healthMonitor->delete(); diff --git a/doc/_build/html/_sources/services/load-balancer/nodes.txt b/doc/_build/html/_sources/services/load-balancer/nodes.txt new file mode 100644 index 000000000..dfaadc404 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/nodes.txt @@ -0,0 +1,124 @@ +Nodes +===== + +.. include:: lb-setup.sample.rst + +List Nodes +---------- + +You can list the nodes attached to a load balancer: + +.. code-block:: php + + $nodes = $loadBalancer->nodeList(); + + foreach ($nodes as $node) { + /** @var $node OpenCloud\LoadBalancer\Resource\Node **/ + } + + +Add Nodes +--------- + +You can attach additional nodes to a load balancer. Assume +``$loadBalancer`` already has two nodes attached to it - ``$serverOne`` +and ``$serverTwo`` - and you want to attach a third node to it, say +``$serverThree``, which provides a service on port 8080. + +**Important:** Remember to call ``$loadBalancer->addNodes()`` after all +the calls to ``$loadBalancer->addNode()`` as shown below. + +.. code-block:: php + + $address = $serverThree->addresses->private[0]->addr; + $loadBalancer->addNode($address, 8080); + $loadBalancer->addNodes(); + + +The signature for ``addNodes`` is as follows: + +.. function:: addNodes($address, $port[, $condition = 'ENABLED'[, $type = null[, $weight = null]]]) + + Add a node to a load balancer + + :param string $address: the IP address of the node + :param integer $port: the port number of the node + :param string $condition: the initial condition of the code. Defaults to ``ENABLED`` + :param string $type: either ``PRIMARY`` or ``SECONDARY`` + :param integer $weight: the node weight (for round-robin algorithm) + +The ``addNode`` method accepts three more optional parameters, in +addition to the two shown above: + +Modify Nodes +------------ + +You can modify one or more of the following node attributes: + +- ``condition``: The condition of the load balancer: + + - ``ENABLED`` – Node is ready to receive traffic from the load + balancer. + - ``DISABLED`` – Node should not receive traffic from the load + balancer. + - ``DRAINING`` – Node should process any traffic it is already + receiving but should not receive any further traffic from the load + balancer. + +- ``type``: The type of the node: + + - ``PRIMARY`` – Nodes defined as PRIMARY are in the normal rotation + to receive traffic from the load balancer. + - ``SECONDARY`` – Nodes defined as SECONDARY are only in the + rotation to receive traffic from the load balancer when all the + primary nodes fail. + +- ``weight``: The weight, between 1 and 100, given to node when + distributing traffic using either the ``WEIGHTED_ROUND_ROBIN`` or the + ``WEIGHTED_LEAST_CONNECTIONS`` load balancing algorithm. + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\NodeCondition; + use OpenCloud\LoadBalancer\Enum\NodeType; + + $node->update(array( + 'condition' => NodeCondition::DISABLED, + 'type' => NodeType::SECONDARY + )); + + +Remove Nodes +------------ + +There are two ways to remove a node. The first way is on an +``OpenCloud\LoadBalancer\Resource\Node`` instance, like so: + + +.. code-block:: php + + $node->delete(); + +The second is with an ``OpenCloud\LoadBalancer\Resource\LoadBalancer`` +instance and the node's ID, like so: + +.. code-block:: php + + $loadBalancer->removeNode('{nodeId}'); + +where '{nodeId}' is the integer ID of the node itself - this is a required value. + + +View Node Service Events +------------------------ + +You can view events associated with the activity between a node and a +load balancer: + +.. code-block:: php + + $nodeEvents = $loadBalancer->nodeEventList(); + + foreach ($nodeEvents as $nodeEvent) { + /** @var $nodeEvent OpenCloud\LoadBalancer\Resource\NodeEvent **/ + } diff --git a/doc/_build/html/_sources/services/load-balancer/sessions.txt b/doc/_build/html/_sources/services/load-balancer/sessions.txt new file mode 100644 index 000000000..3d7f71cfe --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/sessions.txt @@ -0,0 +1,53 @@ +Session Persistence +=================== + +There are two types (or modes) of session persistence: + ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++===================+===================================================================================================================================================================================================================================+ +| ``HTTP_COOKIE`` | A session persistence mechanism that inserts an HTTP cookie and is used to determine the destination back-end node. This is supported for HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``SOURCE_IP`` | A session persistence mechanism that will keep track of the source IP address that is mapped and is able to determine the destination back-end node. This is supported for HTTPS pass-through and non-HTTP load balancing only. | ++-------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. include:: lb-setup.sample.rst + + +List Session Persistence Configuration +-------------------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + + /** @var $sessionPersistenceType null | 'HTTP_COOKIE' | 'SOURCE_IP' **/ + $sessionPersistenceType = $sessionPersistence->persistenceType; + +In the example above: + +- If session persistence is enabled, the value of + ``$sessionPersistenceType`` is the type of session persistence: + either ``HTTP_COOKIE`` or ``SOURCE_IP``. +- If session persistence is disabled, the value of + ``$sessionPersistenceType`` is ``null``. + + +Enable Session Persistence +-------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->update(array( + 'persistenceType' => 'HTTP_COOKIE' + )); + + +Disable Session Persistence +--------------------------- + +.. code-block:: php + + $sessionPersistence = $loadBalancer->sessionPersistence(); + $sessionPersistence->delete(); diff --git a/doc/_build/html/_sources/services/load-balancer/ssl.txt b/doc/_build/html/_sources/services/load-balancer/ssl.txt new file mode 100644 index 000000000..413bdc612 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/ssl.txt @@ -0,0 +1,54 @@ +SSL Termination +=============== + +The SSL Termination feature allows a load balancer user to terminate SSL +traffic at the load balancer layer versus at the web server layer. A +user may choose to configure SSL Termination using a key and an SSL +certificate or an (Intermediate) SSL certificate. + +When SSL Termination is configured on a load balancer, a secure shadow +server is created that listens only for secure traffic on a +user-specified port. This shadow server is only visible to and +manageable by the system. Existing or updated attributes on a load +balancer with SSL Termination will also apply to its shadow server. For +example, if Connection Logging is enabled on an SSL load balancer, it +will also be enabled on the shadow server and Cloud Files logs will +contain log files for both. + +.. include:: lb-setup.sample.rst + + +View configuration +------------------ + +.. code-block:: php + + /** @var $sslConfig OpenCloud\LoadBalancer\Resource\SSLTermination **/ + $sslConfig = $loadBalancer->SSLTermination(); + + +Update configuration +-------------------- + +.. code-block:: php + + $sslConfig->update(array( + 'enabled' => true, + 'securePort' => 443, + 'privateKey' => $key, + 'certificate' => $cert + )); + +For a full list, with explanations, of required and optional attributes, +please consult the `official +documentation `__ + +`Get the executable PHP script for this example `_ + + +Delete configuration +-------------------- + +.. code-block:: php + + $sslConfig->delete(); diff --git a/doc/_build/html/_sources/services/load-balancer/stats.txt b/doc/_build/html/_sources/services/load-balancer/stats.txt new file mode 100644 index 000000000..65077643f --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/stats.txt @@ -0,0 +1,59 @@ +Statistics and Usage Reports +============================ + +.. include:: lb-setup.sample.rst + +Retrieve LB stats +----------------- + +You can retrieve detailed stats about your load balancer, including the +following information: + +- ``connectTimeOut`` – Connections closed by this load balancer because + the 'connect_timeout' interval was exceeded. +- ``connectError`` – Number of transaction or protocol errors in this + load balancer. +- ``connectFailure`` – Number of connection failures in this load balancer. +- ``dataTimedOut`` – Connections closed by this load balancer because + the 'timeout' interval was exceeded. +- ``keepAliveTimedOut`` – Connections closed by this load balancer + because the 'keepalive_timeout' interval was exceeded. +- ``maxConn`` – Maximum number of simultaneous TCP connections this + load balancer has processed at any one time. + +.. code-block:: php + + /** @var $stats OpenCloud\LoadBalancer\Resource\Stats **/ + $stats = $loadBalancer->stats(); + + +Usage Reports +------------- + +The load balancer usage reports provide a view of all transfer activity, +average number of connections, and number of virtual IPs associated with +the load balancing service. Current usage represents all usage recorded +within the preceding 24 hours. Values for both incomingTransfer and +outgoingTransfer are expressed in bytes transferred. + +The optional startTime and endTime parameters can be used to filter all +usage. If the startTime parameter is supplied but the endTime parameter +is not, then all usage beginning with the startTime will be provided. +Likewise, if the endTime parameter is supplied but the startTime +parameter is not, then all usage will be returned up to the endTime +specified. + +.. code-block:: php + + # View billable LBs + $billable = $service->billableLoadBalancerList(); + + foreach ($billable as $loadBalancer) { + /** @var $loadBalancer OpenCloud\LoadBalancer\Resource\LoadBalancer **/ + + # View usage + /** @var $usage OpenCloud\LoadBalancer\Resource\UsageRecord **/ + $usage = $loadBalancer->usage(); + + echo $usage->averageNumConnections, PHP_EOL; + } diff --git a/doc/_build/html/_sources/services/load-balancer/virtual-ips.txt b/doc/_build/html/_sources/services/load-balancer/virtual-ips.txt new file mode 100644 index 000000000..fa0add613 --- /dev/null +++ b/doc/_build/html/_sources/services/load-balancer/virtual-ips.txt @@ -0,0 +1,74 @@ +Virtual IPs +=========== + +.. include:: lb-setup.sample.rst + + +List Virtual IPs +---------------- + +You can list the VIPs associated with a load balancer like so: + +.. code-block:: php + + $vips = $loadBalancer->virtualIpList(); + + foreach ($vips as $vip) { + /** @var $vip of OpenCloud\LoadBalancer\Resource\VirtualIp **/ + } + + +Get existing VIP +---------------- + +To retrieve the details of an existing VIP on a load balancer, you will need +its ID: + +.. code-block:: + + $vip = $loadBalancer->virtualIp('{virtualIpId}'); + + +Add Virtual IPv6 +---------------- + +You can add additional IPv6 VIPs to a load balancer using the following method: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 6); + +the first argument is the type of network your IP address will server traffic in +- and can either be ``PUBLIC`` or ``PRIVATE``. The second argument is the version +of IP address, either ``4`` or ``6``. + + +Add Virtual IPv4 +---------------- + +Similar to above: + +.. code-block:: php + + use OpenCloud\LoadBalancer\Enum\IpType; + + $loadBalancer->addVirtualIp(IpType::PUBLIC, 4); + + +Remove Virtual IP +----------------- + +You can remove a VIP from a load balancer. + +.. code-block:: php + + $vip->remove(); + + +.. note:: + + A load balancer must have at least one VIP associated with it. If you try to + remove a load balancer's last VIP, a ``ClientErrorResponseException`` will be + thrown. diff --git a/doc/_build/html/_sources/services/monitoring/Agents.md.txt b/doc/_build/html/_sources/services/monitoring/Agents.md.txt new file mode 100644 index 000000000..943983f7e --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Agents.md.txt @@ -0,0 +1,202 @@ +Agents +====== + +Intro +----- + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Setup +----- + +.. code:: php + + $agentId = '00-agent.example.com'; + $agent = $service->getAgent($agentId); + +You can view the `service page `__ for more information +about setting up the Cloud Monitoring service. + +List agents +----------- + +.. code:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +List connections +---------------- + +.. code:: php + + $connections = $agent->getConnections(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Get connection +-------------- + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('cntl4qsIbA'); + +Agent Connection properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------+---------------+--------+---------------------------+ +| Name | Description | Type | Method | ++====================+===============+========+===========================+ +| id | - | - | ``getId()`` | ++--------------------+---------------+--------+---------------------------+ +| guid | - | - | ``getGuid()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_id | - | - | ``getAgentId()`` | ++--------------------+---------------+--------+---------------------------+ +| endpoint | - | - | ``getEndpoint()`` | ++--------------------+---------------+--------+---------------------------+ +| process\_version | - | - | ``getProcessVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| bundle\_version | - | - | ``getBundleVersion()`` | ++--------------------+---------------+--------+---------------------------+ +| agent\_ip | - | - | ``getAgentIp()`` | ++--------------------+---------------+--------+---------------------------+ + +Agent tokens +============ + +Intro +----- + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Setup +----- + +.. code:: php + + $tokenId = '4c5e28f0-0b3f-11e1-860d-c55c4705a286:1234'; + $agentToken = $service->getAgentToken($tokenId); + +Create agent token +------------------ + +.. code:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + +List agent tokens +----------------- + +.. code:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + +Please consult the `iterator doc `__ for +more information about iterators. + +Update and delete Agent Token +----------------------------- + +.. code:: php + + // Update + $token->update(array( + 'label' => 'New label' + )); + + // Delete + $token->delete(); + +Agent Host Information +====================== + +Info +---- + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code:: php + + $host = $monitoringService->getAgentHost(); + +Get some metrics +---------------- + +.. code:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Info +---- + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } + diff --git a/doc/_build/html/_sources/services/monitoring/Alarms.md.txt b/doc/_build/html/_sources/services/monitoring/Alarms.md.txt new file mode 100644 index 000000000..2918637e9 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Alarms.md.txt @@ -0,0 +1,81 @@ +Alarms +====== + +Info +---- + +Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (``OK``, +``WARNING`` or ``CRITICAL``) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the `reference +documentation `__. + +Setup +----- + +Alarms are sub-resources of Entities: + +.. code:: php + + $alarmId = 'alAAAA'; + $alarm = $check->getAlarm(); + +For more information about working with Checks, please see the +`appropriate documentation `__. + +Attributes +---------- + ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| Name | Description | Required? | Method | ++==========================+=============================================================================+=============+=================================+ +| check\_id | The ID of the check to alert on. | Required | ``getCheckId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| notification\_plan\_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| disabled | Disable processing and alerts on this alarm | Optional | ``isDisabled()`` <``bool``\ > | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| label | A friendly label for an alarm. | Optional | ``getLabel()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + +Create Alarm +------------ + +.. code:: php + + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); + +List Alarms +----------- + +.. code:: php + + $alarms = $entity->getAlarms(); + + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } + +Update and delete Alarm +----------------------- + +.. code:: php + + // Update + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + // Delete + $alarm->delete(); + diff --git a/doc/_build/html/_sources/services/monitoring/Changelogs.md.txt b/doc/_build/html/_sources/services/monitoring/Changelogs.md.txt new file mode 100644 index 000000000..2e9059e26 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Changelogs.md.txt @@ -0,0 +1,21 @@ +Changelogs +========== + +Info +---- + +The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information. + +Working with Changelogs +----------------------- + +.. code:: php + + $changelog = $service->getChangelog(); + + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } + diff --git a/doc/_build/html/_sources/services/monitoring/Checks.md.txt b/doc/_build/html/_sources/services/monitoring/Checks.md.txt new file mode 100644 index 000000000..0e8e902a5 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Checks.md.txt @@ -0,0 +1,211 @@ +Checks +====== + +Info +---- + +A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information: + +- A name for the check +- The check's parent entity +- The type of check you're creating +- Details of the check +- The monitoring zones that will launch the check + +The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate). + +Setup +----- + +Checks are sub-resources of Entities: + +.. code:: php + + $checkId = 'chAAAA'; + $check = $entity->getCheck($checkId); + +Attributes +---------- + ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| Name | Description | Required? | Data type | ++============+=============================================================================================================+=============+==========================================+ +| type | The type of check. | Required | Valid check type. String (1..25 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| details | Details specific to the check type. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| disabled | Disables the check. | Optional | Boolean | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| label | A friendly label for a check. | Optional | String (1..255 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| period | The period in seconds for a check. The value must be greater than the minimum period set on your account. | Optional | Integer (30..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ + +Attributes used for remote Checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| Name | Description | Required? | Data type | ++===========================+========================================================================================================================================================+=============+============================================================+ +| monitoring\_zones\_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target\_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ + +Test parameters (before create) +------------------------------- + +.. code:: php + + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); + + // You can do a test to see what would happen + // if a Check is launched with these params + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + +Create a Check +-------------- + +.. code:: php + + $entity->createCheck($params); + +Test existing Check +------------------- + +.. code:: php + + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; + +List Checks +----------- + +.. code:: php + + $checks = $entity->getChecks(); + + foreach ($checks as $check) { + echo $check->getId(); + } + +Update and delete Check +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + // Update + $check->update(array('period' => 500)); + + // Delete + $check->delete(); + +Check types +=========== + +Info +---- + +Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. **Note:** Users cannot create, update or delete +check types. + +Check types for commonly encountered web protocols, such as HTTP +(``remote.http``), IMAP (``remote.imap-banner``) , SMTP +(``remote.stmp``), and DNS (``remote.dns``) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(``remote.mysql-banner``) and PostgreSQL (``remote.postgresql-banner``) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the 'extract' attribute to define the format. + +In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +`official API +documentation `__. + +Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check's parameters. For example, +using the 'extract' attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided. + +Setup +----- + +If you want to see the type for an existing Check: + +.. code:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + +Alternatively, you can retrieve a specific type based on its ID: + +.. code:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); + +Attributes +---------- + ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| Name | Description | Data type | Method | ++========================+==========================================================================================================================================================================================+=============+===============================+ +| type | The name of the supported check type. | String | ``getType()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| fields | Check type fields. | Array | ``getFields()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| supported\_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ + +List all possible check types +----------------------------- + +.. code:: php + + $checkTypes = $service->getCheckTypes(); + + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } + diff --git a/doc/_build/html/_sources/services/monitoring/Entities.md.txt b/doc/_build/html/_sources/services/monitoring/Entities.md.txt new file mode 100644 index 000000000..068aec122 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Entities.md.txt @@ -0,0 +1,77 @@ + Entities +========= + +Info +---- + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Setup +----- + +.. code:: php + + $entity = $service->getEntity(); + +For more information about setting up the ``$service`` object, please +see the userguide tutorial for `services `__. + +Attributes +---------- + ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| Name | Description | Required? | Data type | Method | ++=================+=========================================================================+=============+=====================================================+========================+ +| label | Defines a name for the entity. | Required | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| agent\_id | Agent to which this entity is bound to. | Optional | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| ip\_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Optional | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | Optional | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-------------+-----------------------------------------------------+------------------------+ + +Create Entity +------------- + +.. code:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + +Update and delete Entity +------------------------ + +.. code:: php + + // Update + $entity->update(array( + 'label' => 'New label for my entity' + )); + + // Delete + $entity->delete(); + diff --git a/doc/_build/html/_sources/services/monitoring/Metrics.md.txt b/doc/_build/html/_sources/services/monitoring/Metrics.md.txt new file mode 100644 index 000000000..4231cf914 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Metrics.md.txt @@ -0,0 +1,131 @@ + Metrics +======== + +Info +---- + +When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points. + +Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time. + +Data Granularity +---------------- + +Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. + +When you fetch metrics data points, you specify several parameters to +control the granularity of data returned: + +- A time range for the points +- Either the number of points you want returned OR the resolution of + the data you want returned + +When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution. + +**Note:** Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request. + +Consider that you want to query data for a 48-hour time range between +the timestamps ``from=1354647221000`` and ``to=1358794421000`` ( +**specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970** ). The following table shows the number +of points that the API returns for a given resolution. + +Specifying resolution to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-----------------------------+-------------------------+ +| You specify resolution... | API returns points... | ++=============================+=========================+ +| FULL | 5760 | ++-----------------------------+-------------------------+ +| MIN5 | 576 | ++-----------------------------+-------------------------+ +| MIN20 | 144 | ++-----------------------------+-------------------------+ +| MIN60 | 48 | ++-----------------------------+-------------------------+ +| MIN240 | 12 | ++-----------------------------+-------------------------+ +| MIN1440 | 2 | ++-----------------------------+-------------------------+ + +Specifying number of points to retrieve data in 48 hour period +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++--------------------------------------+-----------------------------+ +| You specify points in the range... | API calculates resolution | ++======================================+=============================+ +| 3168-∞ | FULL | ++--------------------------------------+-----------------------------+ +| 360-3167 | MIN5 | ++--------------------------------------+-----------------------------+ +| 96-359 | MIN20 | ++--------------------------------------+-----------------------------+ +| 30-95 | MIN60 | ++--------------------------------------+-----------------------------+ +| 7-29 | MIN240 | ++--------------------------------------+-----------------------------+ +| 0-6 | MIN1440 | ++--------------------------------------+-----------------------------+ + +Data Point Expiration +^^^^^^^^^^^^^^^^^^^^^ + +Cloud Monitoring expires data points according to the following +schedule: + ++--------------+--------------+ +| Resolution | Expiration | ++==============+==============+ +| FULL | 2 days | ++--------------+--------------+ +| MIN5 | 7 days | ++--------------+--------------+ +| MIN20 | 15 days | ++--------------+--------------+ +| MIN60 | 30 days | ++--------------+--------------+ +| MIN240 | 60 days | ++--------------+--------------+ +| MIN1440 | 365 days | ++--------------+--------------+ + + Setup +------ + +Metrics are sub-resources of Checks. For more information about working +with Checks, please see the `relevant documentation `__. + +List all metrics +---------------- + +.. code:: php + + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } + +Fetch data points +----------------- + +.. code:: php + + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); + diff --git a/doc/_build/html/_sources/services/monitoring/Notifications.md.txt b/doc/_build/html/_sources/services/monitoring/Notifications.md.txt new file mode 100644 index 000000000..f361e3c9c --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Notifications.md.txt @@ -0,0 +1,281 @@ +Notifications +============= + +Info +---- + +A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time. + +For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from ``OK`` -> ``CRITICAL`` and so on). + +Setup +----- + +.. code:: php + + $id = 'ntAAAA'; + $notification = $service->getNotification($id); + +Attributes +---------- + ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| Name | Description | Data type | Method | ++===========+===========================================================================+===========================================================+====================+ +| details | A hash of notification specific details based on the notification type. | Array | ``getDetails()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| label | Friendly name for the notification. | String (1..255 chars) | ``getLabel()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ + +Test parameters +--------------- + +.. code:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + // Test it + $response = $notification->testParams($params); + + if ($response->status == 'Success') { + echo $response->message; + } + +Create Notification +------------------- + +.. code:: php + + $notification->create($params); + +Test existing notification +-------------------------- + +.. code:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; + +List Notifications +------------------ + +.. code:: php + + $notifications = $service->getNotifications(); + + foreach ($notifications as $notification) { + echo $notification->getId(); + } + +Update and delete Notifications +------------------------------- + +.. code:: php + + // Update + $notification->update(array( + 'label' => 'New notification label' + )); + + // Delete + $notification->delete(); + +Notification types +================== + +Info +---- + +Pretty self-explanatory. Rackspace Cloud Monitoring currently supports +the following notification types: + +Webhook +^^^^^^^ + +Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes: + ++-----------+------------------------------------------+---------------+ +| Name | Description | Data type | ++===========+==========================================+===============+ +| address | Email address to send notifications to | Valid email | ++-----------+------------------------------------------+---------------+ + +Email +^^^^^ + +Email alerts where the message is delivered to a specified address. It +has these attributes: + ++--------+-----------------------------------+-------------+ +| Name | Description | Data type | ++========+===================================+=============+ +| url | An HTTP or HTTPS URL to POST to | Valid URL | ++--------+-----------------------------------+-------------+ + +Setup +----- + +If you've already set up a main Notification object, and want to access +functionality for this Notification's particular Notification Type, you +can access its property: + +.. code:: php + + $type = $notification->getNotificationType(); + +Alternatively, you can retrieve an independent resource using the ID: + +.. code:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); + +List all possible notification types +------------------------------------ + +.. code:: php + + $types = $service->getNotificationTypes(); + + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } + +Notification plans +================== + +Info +---- + +A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications. + +Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states: + +- Critical +- Warning +- OK + +A notification plan, ``npTechnicalContactsEmail``, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change. + +Setup +----- + +.. code:: php + + $planId = 'npAAAA'; + $plan = $service->getNotificationPlan(); + +Attributes +---------- + ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| Name | Description | Required? | Data type | Method | ++===================+====================================================================+=============+=========================+==========================+ +| label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| critical\_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| ok\_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| warning\_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ + +Create Notification Plan +------------------------ + +.. code:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + +Update and delete Notification Plan +----------------------------------- + +.. code:: php + + // Update + $plan->update(array( + 'label' => 'New label for my plan' + )); + + // Delete + $plan->delete(); + +Alarm Notification History +========================== + +Info +---- + +The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm's statuses. + +Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information. + + Setup +------ + +Notification History is a sub-resource of an Alarm. For more information +about working with Alarms, please consult the relevant +`documentation `__. + +Discover which Checks have a Notification History +------------------------------------------------- + +This operation list checks for which alarm notification history is +available: + +.. code:: php + + $checks = $alarm->getRecordedChecks(); + +List Alarm Notification History for a particular Check +------------------------------------------------------ + +.. code:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); + +Get a particular Notification History item +------------------------------------------ + +.. code:: php + + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); + diff --git a/doc/_build/html/_sources/services/monitoring/Service.md.txt b/doc/_build/html/_sources/services/monitoring/Service.md.txt new file mode 100644 index 000000000..09c25c21d --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Service.md.txt @@ -0,0 +1,23 @@ +Cloud Monitoring Service +======================== + +Initializing the Cloud Monitoring is easy - and can be done in a similar +way to all other Rackspace services: + +1. Create client and pass in auth details. For more information about + creating clients, please consult the `Client + documentation <../Clients.md>`__. +2. Use the factory method, specifying additional parameters where + necessary: + +.. code:: php + + $service = $client->cloudMonitoringService('cloudMonitoring', 'ORD', 'publicURL'); + +All three parameters are optional - if not specified, it will revert to +the service's default values which are: + +- Name = ``cloudMonitoring`` +- Region = ``DFW`` +- URL type = ``publicURL`` + diff --git a/doc/_build/html/_sources/services/monitoring/Views.md.txt b/doc/_build/html/_sources/services/monitoring/Views.md.txt new file mode 100644 index 000000000..a5b7245fa --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Views.md.txt @@ -0,0 +1,27 @@ +Views +===== + +Info +---- + +Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint. + +List all Views +-------------- + +\`\`\`php $views = $service->getViews(); + +foreach ($views as $view) { $entity = $view->getEntity(); + +:: + + echo $view->getTimestamp(); + +} \`\`\` + +Please consult the `iterator doc `__ for +more information about iterators. diff --git a/doc/_build/html/_sources/services/monitoring/Zones.md.txt b/doc/_build/html/_sources/services/monitoring/Zones.md.txt new file mode 100644 index 000000000..ffe4bba1e --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/Zones.md.txt @@ -0,0 +1,67 @@ +Zones +===== + +Info +---- + +A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". +It is an abstraction for a general location from which data is +collected. + +An "endpoint," also known as a "collector," collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range. + +A check references a list of monitoring zones it should be run from. + + Setup +------ + +.. code:: php + + $zoneId = 'mzAAAAA'; + $zone = $monitoringService->getMonitoringZone($zoneId); + +Attributes +---------- + ++-----------------+------------------+-----------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+==================+===================================+========================+ +| country\_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| label | Label | String | ``getLabel()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| source\_ips | Source IP list | Array | ``getSourceIps()`` | ++-----------------+------------------+-----------------------------------+------------------------+ + + List all zones +--------------- + +.. code:: php + + $zones = $service->getMonitoringZones(); + +Please consult the `iterator doc `__ for +more information about iterators. + +Perform a traceroute +-------------------- + +.. code:: php + + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); + + // How many hops? + echo count($traceroute); + + // What was the first hop's IP? + echo $traceroute[0]->ip; + diff --git a/doc/_build/html/_sources/services/monitoring/agents.txt b/doc/_build/html/_sources/services/monitoring/agents.txt new file mode 100644 index 000000000..f4c673608 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/agents.txt @@ -0,0 +1,188 @@ +Agents +====== + +The Monitoring Agent resides on the host server being monitored. The +agent allows you to gather on-host metrics based on agent checks and +push them to Cloud Monitoring where you can analyze them, use them with +the Cloud Monitoring infrastructure (such as alarms), and archive them. + +For more information about this feature, including a brief overview of +its core design principles and security layers, see the `official API +documentation `__. + +Retrieve details about an agent +------------------------------- + +.. code-block:: php + + $agent = $service->getAgent('{agentId}'); + + +List agents +----------- + +.. code-block:: php + + $agents = $service->getAgents(); + + foreach ($agents as $agent) { + echo $agent->getLastConnected(); + } + + +List connections +---------------- + +.. code-block:: php + + $connections = $agent->getConnections(); + + +Get connection +-------------- + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\AgentConnection */ + $connection = $agent->getConnection('{connectionId}'); + + +Once you have access to an agent's ``OpenCloud\CloudMonitoring\Resource\AgentConnection`` +object, these are the attributes you can access: + ++--------------------+---------------------------+ +| Name | Method | ++====================+===========================+ +| id | ``getId()`` | ++--------------------+---------------------------+ +| guid | ``getGuid()`` | ++--------------------+---------------------------+ +| agent_id | ``getAgentId()`` | ++--------------------+---------------------------+ +| endpoint | ``getEndpoint()`` | ++--------------------+---------------------------+ +| process_version | ``getProcessVersion()`` | ++--------------------+---------------------------+ +| bundle_version | ``getBundleVersion()`` | ++--------------------+---------------------------+ +| agent_ip | ``getAgentIp()`` | ++--------------------+---------------------------+ + +Agent tokens +============ + +Agent tokens are used to authenticate Monitoring agents to the +Monitoring Service. Multiple agents can share a single token. + +Retrieve an agent token +----------------------- + +.. code-block:: php + + $agentToken = $service->getAgentToken('{tokenId}'); + + +Create agent token +------------------ + +.. code-block:: php + + $newToken = $service->getAgentToken(); + $newToken->create(array('label' => 'Foobar')); + + +List agent tokens +----------------- + +.. code-block:: php + + $agentTokens = $service->getAgentTokens(); + + foreach ($agentTokens as $token) { + echo $token->getLabel(); + } + + +Update agent token +------------------ + +.. code-block:: php + + $token->update(array( + 'label' => 'New label' + )); + + +Update agent token +------------------ + +.. code-block:: php + + $token->delete(); + + +Agent Host Information +====================== + +An agent can gather host information, such as process lists, network +configuration, and memory usage, on demand. You can use the +host-information API requests to gather this information for use in +dashboards or other utilities. + +Setup +----- + +.. code-block:: php + + $host = $service->getAgentHost(); + + +Get some metrics +---------------- + +.. code-block:: php + + $cpuInfo = $host->info('cpus'); + $diskInfo = $host->info('disks'); + $filesystemInfo = $host->info('filesystems'); + $memoryInfo = $host->info('memory'); + $networkIntInfo = $host->info('network_interfaces'); + $processesInfo = $host->info('processes'); + $systemInfo = $host->info('system'); + $userInfo = $host->info('who'); + + // What CPU models do we have? + foreach ($cpuInfo as $cpuMetric) { + echo $cpuMetric->model, PHP_EOL; + } + + // How many disks do we have? + echo $diskInfo->count(); + + // What's the available space on our ext4 filesystem? + foreach ($filesystemInfo as $filesystemMetric) { + if ($filesystemMetric->sys_type_name == 'ext4') { + echo $filesystemMetric->avail; + } + } + +Agent targets +============= + +Each agent check type gathers data for a related set of target devices +on the server where the agent is installed. For example, +``agent.network`` gathers data for network devices. The actual list of +target devices is specific to the configuration of the host server. By +focusing on specific targets, you can efficiently narrow the metric data +that the agent gathers. + +List agent targets +------------------ + +.. code-block:: php + + $targets = $service->getAgentTargets(); + + foreach ($targets as $target) { + echo $target->getType(); + } diff --git a/doc/_build/html/_sources/services/monitoring/alarms.txt b/doc/_build/html/_sources/services/monitoring/alarms.txt new file mode 100644 index 000000000..297a57407 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/alarms.txt @@ -0,0 +1,99 @@ +Alarms +====== + +Alarms bind alerting rules, entities, and notification plans into a +logical unit. Alarms are responsible for determining a state (``OK``, +``WARNING`` or ``CRITICAL``) based on the result of a Check, and +executing a notification plan whenever that state changes. You create +alerting rules by using the alarm DSL. For information about using the +alarm language, refer to the `reference +documentation `__. + +Setup +----- + +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + +Retrieve alarm +-------------- + +.. code-block:: php + + $alarm = $check->getAlarm('{alarmId}'); + + +Once you have access to a ``OpenCloud\Monitoring\Resource\Alarm`` object, these +are the attributes you can access: + ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| Name | Description | Required? | Method | ++==========================+=============================================================================+=============+=================================+ +| check_id | The ID of the check to alert on. | Required | ``getCheckId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| notification_plan_id | The ID of the notification plan to execute when the state changes. | Optional | ``getNotificationPlanId()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| criteria | The alarm DSL for describing alerting conditions and their output states. | Optional | ``getCriteria()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| disabled | Disable processing and alerts on this alarm | Optional | ``isDisabled()`` <``bool``\ > | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| label | A friendly label for an alarm. | Optional | ``getLabel()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | ``getMetadata()`` | ++--------------------------+-----------------------------------------------------------------------------+-------------+---------------------------------+ + + +Create Alarm +------------ + +.. code-block:: php + + $alarm = $check->getAlarm(); + $alarm->create(array( + 'check_id' => 'chAAAA', + 'criteria' => 'if (metric["duration"] >= 2) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);', + 'notification_plan_id' => 'npAAAAA' + )); + + +List Alarms +----------- + +.. code-block:: php + + $alarms = $entity->getAlarms(); + + foreach ($alarms as $alarm) { + echo $alarm->getId(); + } + + +Update Alarm +------------ + +.. code-block:: php + + $alarm->update(array( + 'criteria' => 'if (metric["duration"] >= 5) { return new AlarmStatus(OK); } return new AlarmStatus(CRITICAL);' + )); + + +Delete alarm +------------ + +.. code-block:: php + + $alarm->delete(); diff --git a/doc/_build/html/_sources/services/monitoring/changelogs.txt b/doc/_build/html/_sources/services/monitoring/changelogs.txt new file mode 100644 index 000000000..a6b26af25 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/changelogs.txt @@ -0,0 +1,18 @@ +Changelogs +========== + +The monitoring service records changelogs for alarm statuses. Changelogs +are accessible as a Time Series Collection. By default the API queries +the last 7 days of changelog information. + + +View Changelog +-------------- + +.. code-block:: php + + $changelog = $service->getChangelog(); + + foreach ($changelog as $item) { + $entity = $item->getEntityId(); + } diff --git a/doc/_build/html/_sources/services/monitoring/checks.txt b/doc/_build/html/_sources/services/monitoring/checks.txt new file mode 100644 index 000000000..73474c170 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/checks.txt @@ -0,0 +1,228 @@ +Checks +====== + + +A check is one of the foundational building blocks of the monitoring +system. The check determines the parts or pieces of the entity that you +want to monitor, the monitoring frequency, how many monitoring zones are +originating the check, and so on. When you create a new check in the +monitoring system, you specify the following information: + +- A name for the check +- The check's parent entity +- The type of check you're creating +- Details of the check +- The monitoring zones that will launch the check + +The check, as created, will not trigger alert messages until you create +an alarm to generate notifications, to enable the creation of a single +alarm that acts upon multiple checks (e.g. alert if any of ten different +servers stops responding) or multiple alarms off of a single check. +(e.g. ensure both that a HTTPS server is responding and that it has a +valid certificate). + +Create a check +-------------- + +There are various attributes available to you when creating a new monitoring +check: + +.. code-block:: php + + $params = array( + 'type' => 'remote.http', + 'details' => array( + 'url' => 'http://example.com', + 'method' => 'GET' + ), + 'monitoring_zones_poll' => array('mzlon'), + 'period' => '100', + 'timeout' => '30', + 'target_alias' => 'default', + 'label' => 'Website check 1' + ); + +For a full list of available attributes, consult the list below. + +Attributes +~~~~~~~~~~ + ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| Name | Description | Required? | Data type | ++============+=============================================================================================================+=============+==========================================+ +| type | The type of check. | Required | Valid check type. String (1..25 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| details | Details specific to the check type. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| disabled | Disables the check. | Optional | Boolean | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| label | A friendly label for a check. | Optional | String (1..255 chars) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| metadata | Arbitrary key/value pairs. | Optional | Array | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| period | The period in seconds for a check. The value must be greater than the minimum period set on your account. | Optional | Integer (30..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ +| timeout | The timeout in seconds for a check. This has to be less than the period. | Optional | Integer (2..1800) | ++------------+-------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------+ + +Optional attributes to be used with remote checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| Name | Description | Required? | Data type | ++===========================+========================================================================================================================================================+=============+============================================================+ +| monitoring_zones_poll | List of monitoring zones to poll from. Note: This argument is only required for remote (non-agent) checks | Optional | Array | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target_alias | A key in the entity's ``ip_addresses`` hash used to resolve this check to an IP address. This parameter is mutually exclusive with target\_hostname. | Optional | String (1..64 chars) | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target_hostname | The hostname this check should target. This parameter is mutually exclusive with ``target_alias``. | Optional | Valid FQDN, IPv4 or IPv6 address. String (1..256 chars). | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ +| target_resolver | Determines how to resolve the check target. | Optional | ``IPv4`` or ``IPv6`` | ++---------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+------------------------------------------------------------+ + + +Test parameters +--------------- + +Sometimes it can be useful to test out the parameters before sending them as a +create call. To do this, pass in the ``$params`` like so: + +.. code-block:: php + + $response = $entity->testNewCheckParams($params); + + echo $response->timestamp; // When was it executed? + echo $response->available; // Was it available? + echo $response->status; // Status code + + +Send parameters +~~~~~~~~~~~~~~~ + +Once you are satisfied with your configuration parameters, you can complete the +operation and send it to the API like so: + +.. code-block:: php + + $entity->createCheck($params); + + +Test existing Check +------------------- + +.. code-block:: php + + // Set arg to TRUE for debug information + $response = $check->test(true); + + echo $response->debug_info; + + +List Checks +----------- + +.. code-block:: php + + $checks = $entity->getChecks(); + + foreach ($checks as $check) { + echo $check->getId(); + } + + +Update Check +------------ + +.. code-block:: php + + $check->update(array('period' => 500)); + + +Delete check +------------ + +.. code-block:: php + + $check->delete(); + + +Check types +=========== + +Each check within the Rackspace Cloud Monitoring has a designated check +type. The check type instructs the monitoring system how to check the +monitored resource. **Note:** Users cannot create, update or delete +check types. + +Check types for commonly encountered web protocols, such as HTTP +(``remote.http``), IMAP (``remote.imap-banner``) , SMTP +(``remote.stmp``), and DNS (``remote.dns``) are provided. Monitoring +commonly encountered infrastructure servers like MySQL +(``remote.mysql-banner``) and PostgreSQL (``remote.postgresql-banner``) +are also available. Monitoring custom server uptime can be accomplished +with the remote.tcp banner check to check for a protocol-defined banner +at the beginning of a connection. Gathering metrics from server software +to create alerts against can be accomplished using the remote.http check +type and the 'extract' attribute to define the format. + +In addition to the standard Cloud Monitoring check types, you can also +use agent check types if the Monitoring Agent is installed on the server +you are monitoring. For a list of available check types, see the +`official API +documentation `__. + +Checks generate metrics that alarms will alert based upon. The metrics +generated often times depend on the check's parameters. For example, +using the 'extract' attribute on the remote.http check, however the +default metrics will always be present. To determine the exact metrics +available, the Test Check API is provided. + +Find an existing check's type +----------------------------- + +If you want to see the type for an existing Check resource: + +.. code-block:: php + + /** @var \OpenCloud\CloudMonitoring\Resource\CheckType */ + $checkType = $check->getCheckType(); + + +List all possible check types +----------------------------- + +.. code-block:: php + + $checkTypes = $service->getCheckTypes(); + + foreach ($checkTypes as $checkType) { + echo $checkType->getId(); + } + + +Retrieve details about a Type by its ID +--------------------------------------- + +Alternatively, you can retrieve a specific type based on its ID: + +.. code-block:: php + + $checkTypeId = 'remote.dns'; + $checkType = $service->getCheckType($checkTypeId); + + +Attributes +---------- + +Once you have access to a ``OpenCloud\CloudMonitoring\Resource\CheckType`` object, +you can query these attributes: + ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| Name | Description | Data type | Method | ++========================+==========================================================================================================================================================================================+=============+===============================+ +| type | The name of the supported check type. | String | ``getType()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| fields | Check type fields. | Array | ``getFields()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ +| supported_platforms | Platforms on which an agent check type is supported. This is advisory information only - the check may still work on other platforms, or report that check execution failed at runtime | Array | ``getSupportedPlatforms()`` | ++------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------------------------------+ diff --git a/doc/_build/html/_sources/services/monitoring/entities.txt b/doc/_build/html/_sources/services/monitoring/entities.txt new file mode 100644 index 000000000..7e4132af1 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/entities.txt @@ -0,0 +1,77 @@ + Entities +========= + +An entity is the target of what you are monitoring. For example, you can +create an entity to monitor your website, a particular web service, or +your Rackspace server. Note that an entity represents only one item in +the monitoring system -- if you wanted to monitor each server in a +cluster, you would create an entity for each of the servers. You would +not create a single entity to represent the entire cluster. + +An entity can have multiple checks associated with it. This allows you +to check multiple services on the same host by creating multiple checks +on the same entity, instead of multiple entities each with a single +check. + +Create Entity +------------- + +.. code-block:: php + + $service->createEntity(array( + 'label' => 'Brand New Entity', + 'ip_addresses' => array( + 'default' => '127.0.0.4', + 'b' => '127.0.0.5', + 'c' => '127.0.0.6', + 'test' => '127.0.0.7' + ), + 'metadata' => array( + 'all' => 'kinds', + 'of' => 'stuff', + 'can' => 'go', + 'here' => 'null is not a valid value' + ) + )); + + +Retrive an entity +----------------- + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + + +Attributes +~~~~~~~~~~ + ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+=========================================================================+=====================================================+========================+ +| label | Defines a name for the entity. | String (1..255 chars) | ``getLabel()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| agent_id | Agent to which this entity is bound to. | String matching the regex: ``/^[-\.\w]{1,255}$/`` | ``getAgentId()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| ip_addresses | Hash of IP addresses that can be referenced by checks on this entity. | Array | ``getIpAddresses()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ +| metadata | Arbitrary key/value pairs that are passed during the alerting phase. | ``OpenCloud\Common\Metadata`` | ``getMetadata()`` | ++-----------------+-------------------------------------------------------------------------+-----------------------------------------------------+------------------------+ + + +Update an entity +---------------- + +.. code-block:: php + + $entity->update(array( + 'label' => 'New label for my entity' + )); + + +Delete entity +------------- + +.. code-block:: php + + $entity->delete(); diff --git a/doc/_build/html/_sources/services/monitoring/index.txt b/doc/_build/html/_sources/services/monitoring/index.txt new file mode 100644 index 000000000..0a7225e36 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/index.txt @@ -0,0 +1,97 @@ +Monitoring v1 +============= + +.. include:: ../common/rs-only.sample.rst + +Monitoring service +~~~~~~~~~~~~~~~~~~ + +Now to instantiate the Monitoring service: + +.. code-block:: php + + $service = $client->monitoringService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + +Operations +---------- + +.. toctree:: + + entities + checks + alarms + agents + changelogs + metrics + notifications + views + zones + +Glossary +-------- + +.. glossary:: + + agent + A monitoring daemon that resides on the server being monitored. The agent + gathers metrics based on agent checks and pushes them to Cloud Monitoring. + The agent provides insight into your servers with checks for information + such as load average and network usage. The agent acts as a single small + service that runs scheduled checks and pushes metrics to the rest of Cloud + Monitoring so the metrics can be analyzed, trigger alerts, and be archived. + These metrics are gathered via checks using agent check types, and can be + used with the other Cloud Monitoring primitives such as alarms. + + agent token + An authentication token used to identify the agent when it communicates + with Cloud Monitoring. + + alarm + An alarm contains a set of rules that determine when the monitoring system + sends a notification. You can create multiple alarms for the different + checks types associated with an entity. For example, if your entity is a + web server that hosts your company's website, you can create one alarm to + monitor the server itself, and another alarm to monitor the website. + + check + Checks explicitly specify how you want to monitor an entity. Once you've + created an entity, you can configure one or more checks for it. A check is + the foundational building block of the monitoring system, and is always + associated with an entity. The check specifies the parts or pieces of the + entity that you want to monitor, the monitoring frequency, how many + monitoring zones are launching the check, and so on. It contains the + specific details of how you are monitoring the entity. + + entity + The object or resource that you want to monitor. It can be any object or + device that you want to monitor. It's commonly a web server, but it might + also be a website, a web page or a web service. + + monitoring zone + A monitoring zone is the "launch point" of a check. When you create a + check, you specify which monitoring zone(s) you want to launch the check + from. This concept of a monitoring zone is similar to that of a datacenter, + however in the monitoring system, you can think of it more as a geographical + region. + + notification + A notification is an informational message sent to one or more addresses + by the monitoring system when an alarm is triggered. You can set up + notifications to alert a single individual or an entire team. Rackspace + Cloud Monitoring currently supports webhooks and email for sending + notifications. + + notification plan + A notification plan contains a set of notification rules to execute when an + alarm is triggered. A notification plan can contain multiple notifications + for each of the following states: + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/monitoring/metrics.txt b/doc/_build/html/_sources/services/monitoring/metrics.txt new file mode 100644 index 000000000..0b412a6a3 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/metrics.txt @@ -0,0 +1,143 @@ + Metrics +======== + +When Monitoring checks run, they generate metrics. These metrics are +stored as full resolution data points in the Cloud Monitoring system. +Full resolution data points are periodically rolled up (condensed) into +coarser data points. + +Depending on your needs, you can use the metrics API to fetch individual +data points (fine-grained) or rolled up data points (coarse-grained) +over a period of time. + + +Data Granularity +---------------- + +Cloud Monitoring supports several granularities of data: full resolution +data and rollups computed at 5, 20, 60, 240 and 1440 minute intervals. + +When you fetch metrics data points, you specify several parameters to +control the granularity of data returned: + +- A time range for the points +- Either the number of points you want returned OR the resolution of + the data you want returned + +When you query by points, the API selects the resolution that will +return you the number of points you requested. The API makes the +assumption of a 30 second frequency, performs the calculation, and +selects the appropriate resolution. + +**Note:** Because the API performs calculations to determine the points +returned for a particular resolution, the number of points returned may +differ from the specific number of points you request. + +Consider that you want to query data for a 48-hour time range between +the timestamps ``from=1354647221000`` and ``to=1358794421000`` ( +**specified in Unix time, based on the number of milliseconds that have +elapsed since January 1, 1970** ). The following table shows the number +of points that the API returns for a given resolution. + +Specifying resolution to retrieve data in 48 hour period +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++-----------------------------+-------------------------+ +| You specify resolution... | API returns points... | ++=============================+=========================+ +| FULL | 5760 | ++-----------------------------+-------------------------+ +| MIN5 | 576 | ++-----------------------------+-------------------------+ +| MIN20 | 144 | ++-----------------------------+-------------------------+ +| MIN60 | 48 | ++-----------------------------+-------------------------+ +| MIN240 | 12 | ++-----------------------------+-------------------------+ +| MIN1440 | 2 | ++-----------------------------+-------------------------+ + +Specifying number of points to retrieve data in 48 hour period +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ++--------------------------------------+-----------------------------+ +| You specify points in the range... | API calculates resolution | ++======================================+=============================+ +| 3168-∞ | FULL | ++--------------------------------------+-----------------------------+ +| 360-3167 | MIN5 | ++--------------------------------------+-----------------------------+ +| 96-359 | MIN20 | ++--------------------------------------+-----------------------------+ +| 30-95 | MIN60 | ++--------------------------------------+-----------------------------+ +| 7-29 | MIN240 | ++--------------------------------------+-----------------------------+ +| 0-6 | MIN1440 | ++--------------------------------------+-----------------------------+ + +Data Point Expiration +~~~~~~~~~~~~~~~~~~~~~ + +Cloud Monitoring expires data points according to the following +schedule: + ++--------------+--------------+ +| Resolution | Expiration | ++==============+==============+ +| FULL | 2 days | ++--------------+--------------+ +| MIN5 | 7 days | ++--------------+--------------+ +| MIN20 | 15 days | ++--------------+--------------+ +| MIN60 | 30 days | ++--------------+--------------+ +| MIN240 | 60 days | ++--------------+--------------+ +| MIN1440 | 365 days | ++--------------+--------------+ + +Setup +------ + +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + + +List all metrics +---------------- + +.. code-block:: php + + $metrics = $check->getMetrics(); + + foreach ($metrics as $metric) { + echo $metric->getName(); + } + + +Fetch data points +----------------- + +.. code-block:: php + + $data = $check->fetchDataPoints('mzdfw.available', array( + 'resolution' => 'FULL', + 'from' => 1369756378450, + 'to' => 1369760279018 + )); diff --git a/doc/_build/html/_sources/services/monitoring/notifications.txt b/doc/_build/html/_sources/services/monitoring/notifications.txt new file mode 100644 index 000000000..d7e289ad5 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/notifications.txt @@ -0,0 +1,319 @@ +Notifications +============= + +A notification is a destination to send an alarm; it can be a variety of +different types, and will evolve over time. + +For instance, with a webhook type notification, Cloud Monitoring posts +JSON formatted data to a user-specified URL on an alert condition (Check +goes from ``OK`` -> ``CRITICAL`` and so on). + +Get notification +---------------- + +.. code-block:: php + + $notification = $service->getNotification('{id}'); + +Once you have access to a ``OpenCloud\Monitoring\Resource\Notification`` object, +these are the attributes available for use: + ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| Name | Description | Data type | Method | ++===========+===========================================================================+===========================================================+====================+ +| details | A hash of notification specific details based on the notification type. | Array | ``getDetails()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| label | Friendly name for the notification. | String (1..255 chars) | ``getLabel()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ +| type | The notification type to send. | String. Either ``webhook``, ``email``, or ``pagerduty`` | ``getType()`` | ++-----------+---------------------------------------------------------------------------+-----------------------------------------------------------+--------------------+ + +Creating notifications +---------------------- + +The first thing to do when creating a new notification is configure the +parameters which will define the behaviour of your resource: + +.. code-block:: php + + $params = array( + 'label' => 'My webhook #1', + 'type' => 'webhook', + 'details' => array( + 'url' => 'http://example.com' + ) + ); + + +Test parameters +~~~~~~~~~~~~~~~ + +Once this is done, it is often useful to test them out to check whether they +will result in a successful creation: + +.. code-block:: php + + // Test it + $response = $notification->testParams($params); + + if ($response->status == 'Success') { + echo $response->message; + } + + +Send parameters +~~~~~~~~~~~~~~~ + +When you're happy with the parameters you've defined, you can complete the +operation by sending them to the API like so: + +.. code-block:: php + + $notification->create($params); + + +Test existing notification +-------------------------- + +.. code-block:: php + + $response = $notification->testExisting(true); + echo $response->debug_info; + + +List Notifications +------------------ + +.. code-block:: php + + $notifications = $service->getNotifications(); + + foreach ($notifications as $notification) { + echo $notification->getId(); + } + + +Update a Notification +--------------------- + +.. code-block:: php + + $notification->update(array( + 'label' => 'New notification label' + )); + + +Delete a Notification +--------------------- + +.. code-block:: php + + $notification->delete(); + + +Notification types +================== + +Rackspace Cloud Monitoring currently supports the following notification types: + +Webhook +~~~~~~~ + +Industry-standard web hooks, where JSON is posted to a configurable URL. +It has these attributes: + ++-----------+------------------------------------------+---------------+ +| Name | Description | Data type | ++===========+==========================================+===============+ +| address | Email address to send notifications to | Valid email | ++-----------+------------------------------------------+---------------+ + +Email +~~~~~ + +Email alerts where the message is delivered to a specified address. It +has these attributes: + ++--------+-----------------------------------+-------------+ +| Name | Description | Data type | ++========+===================================+=============+ +| url | An HTTP or HTTPS URL to POST to | Valid URL | ++--------+-----------------------------------+-------------+ + + +Setup +----- + +If you've already set up a main Notification object, and want to access +functionality for this Notification's particular Notification Type, you +can access its property: + +.. code-block:: php + + $type = $notification->getNotificationType(); + +Alternatively, you can retrieve an independent resource using the ID: + +.. code-block:: php + + $typeId = 'pagerduty'; + $type = $service->getNotificationType($typeId); + + +List all possible notification types +------------------------------------ + +.. code-block:: php + + $types = $service->getNotificationTypes(); + + foreach ($types as $type) { + echo sprintf('%s %s', $type->getName(), $type->getDescription()); + } + + +Notification plans +================== + +A notification plan contains a set of notification actions that +Rackspace Cloud Monitoring executes when triggered by an alarm. +Rackspace Cloud Monitoring currently supports webhook and email +notifications. + +Each notification state can contain multiple notification actions. For +example, you can create a notification plan that hits a webhook/email to +notify your operations team if a warning occurs. However, if the warning +escalates to an Error, the notification plan could be configured to hit +a different webhook/email that triggers both email and SMS messages to +the operations team. The notification plan supports the following +states: + +- Critical +- Warning +- OK + +A notification plan, ``npTechnicalContactsEmail``, is provided by +default which will email all of the technical contacts on file for an +account whenever there is a state change. + +Get a notification plan +----------------------- + +.. code-block:: php + + $plan = $service->getNotificationPlan('{planId}'); + +Once you have access to a ``OpenCloud\\Monitoring\\Resource\\NotificationPlan`` +object, you can access these resources: + ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| Name | Description | Required? | Data type | Method | ++===================+====================================================================+=============+=========================+==========================+ +| label | Friendly name for the notification plan. | Required | String (1..255 chars) | ``getLabel()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| critical_state | The notification list to send to when the state is ``CRITICAL``. | Optional | Array | ``getCriticalState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| ok_state | The notification list to send to when the state is ``OK``. | Optional | Array | ``getOkState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ +| warning_state | The notification list to send to when the state is ``WARNING``. | Optional | Array | ``getWarningState()`` | ++-------------------+--------------------------------------------------------------------+-------------+-------------------------+--------------------------+ + +Create Notification Plan +------------------------ + +.. code-block:: php + + $plan->create(array( + 'label' => 'New Notification Plan', + 'critical_state' => array('ntAAAA'), + 'ok_state' => array('ntBBBB'), + 'warning_state' => array('ntCCCC') + )); + + +Update notification plan +------------------------ + +.. code-block:: php + + $plan->update(array( + 'label' => 'New label for my plan' + )); + + +Delete notification plan +------------------------ + +.. code-block:: php + + $plan->delete(); + + +Alarm Notification History +========================== + +The monitoring service keeps a record of notifications sent for each +alarm. This history is further subdivided by the check on which the +notification occurred. Every attempt to send a notification is recorded, +making this history a valuable tool in diagnosing issues with unreceived +notifications, in addition to offering a means of viewing the history of +an alarm's statuses. + +Alarm notification history is accessible as a Time Series Collection. By +default alarm notification history is stored for 30 days and the API +queries the last 7 days of information. + +Setup +------ + +In order to interact with this feature, you must first retrieve an entity by +its ID: + +.. code-block:: php + + $entity = $service->getEntity('{entityId}'); + +and then a particular check, about which you can configure alarms: + +.. code-block:: php + + $check = $entity->getCheck('{checkId}'); + +and finally, retrieve the alarm: + +.. code-block:: php + + $alarm = $check->getAlarm('{alarmId}'); + +For more information about these resource types, please consult the documentation +about `entities `_ and `checks `_. + + +Discover which Checks have a Notification History +------------------------------------------------- + +This operation list checks for which alarm notification history is +available: + +.. code-block:: php + + $checks = $alarm->getRecordedChecks(); + + +List Alarm Notification History for a particular Check +------------------------------------------------------ + +.. code-block:: php + + $checkHistory = $alarm->getNotificationHistoryForCheck('chAAAA'); + + +Get a particular Notification History item +------------------------------------------ + +.. code-block:: php + + $checkId = 'chAAAA'; + $itemUuid = '646ac7b0-0b34-11e1-a0a1-0ff89fa2fa26'; + + $singleItem = $history->getNotificationHistoryItem($checkId, $itemUuid); diff --git a/doc/_build/html/_sources/services/monitoring/views.txt b/doc/_build/html/_sources/services/monitoring/views.txt new file mode 100644 index 000000000..e83b97154 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/views.txt @@ -0,0 +1,21 @@ +Views +===== + +Views contain a combination of data that usually includes multiple, +different objects. The primary purpose of a view is to save API calls +and make data retrieval more efficient. Instead of doing multiple API +calls and then combining the result yourself, you can perform a single +API call against the view endpoint. + + +List all Views +-------------- + +.. code-block:: php + + $views = $service->getViews(); + + foreach ($views as $view) { + $entity = $view->getEntity(); + echo $view->getTimestamp(); + } diff --git a/doc/_build/html/_sources/services/monitoring/zones.txt b/doc/_build/html/_sources/services/monitoring/zones.txt new file mode 100644 index 000000000..bb588a303 --- /dev/null +++ b/doc/_build/html/_sources/services/monitoring/zones.txt @@ -0,0 +1,57 @@ +Zones +===== + +A monitoring zone is a location that Rackspace Cloud Monitoring collects +data from. Examples of monitoring zones are "US West", "DFW1" or "ORD1". +It is an abstraction for a general location from which data is +collected. + +An "endpoint," also known as a "collector," collects data from the +monitoring zone. The endpoint is mapped directly to an individual +machine or a virtual machine. A monitoring zone contains many endpoints, +all of which will be within the IP address range listed in the response. +The opposite is not true, however, as there may be unallocated IP +addresses or unrelated machines within that IP address range. + +A check references a list of monitoring zones it should be run from. + +Get details about a zone +------------------------ + +.. code-block:: php + + $zone = $monitoringService->getMonitoringZone('{zoneId}'); + ++-----------------+------------------+-----------------------------------+------------------------+ +| Name | Description | Data type | Method | ++=================+==================+===================================+========================+ +| country_code | Country Code | String longer than 2 characters | ``getCountryCode()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| label | Label | String | ``getLabel()`` | ++-----------------+------------------+-----------------------------------+------------------------+ +| source_ips | Source IP list | Array | ``getSourceIps()`` | ++-----------------+------------------+-----------------------------------+------------------------+ + + List all zones +--------------- + +.. code-block:: php + + $zones = $service->getMonitoringZones(); + + +Perform a traceroute +-------------------- + +.. code-block:: php + + $traceroute = $zone->traceroute(array( + 'target' => 'http://test.com', + 'target_resolver' => 'IPv4' + )); + + // How many hops? + echo count($traceroute); + + // What was the first hop's IP? + echo $traceroute[0]->ip; diff --git a/doc/_build/html/_sources/services/networking/README.md.txt b/doc/_build/html/_sources/services/networking/README.md.txt new file mode 100644 index 000000000..605805fe1 --- /dev/null +++ b/doc/_build/html/_sources/services/networking/README.md.txt @@ -0,0 +1,108 @@ +Networking +========== + +**Networking** is a service that you can use to create virtual networks +and attach cloud devices such as servers to these networks. + +Concepts +-------- + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: A network is an isolated virtual layer-2 broadcast + domain that is typically reserved for the tenant who created it + unless you configure the network to be shared. The network is the + main entity in the Networking service. Ports and subnets are always + associated with a network. + +- **Subnet**: A subnet represents an IP address block that can be used + to assign IP addresses to virtual instances (such as servers created + using the Compute service). Each subnet must have a CIDR and must be + associated with a network. + +- **Port**: A port represents a virtual switch port on a logical + network switch. Virtual instances (such as servers created using the + Compute service) attach their interfaces into ports. The port also + defines the MAC address and the IP address(es) to be assigned to the + interfaces plugged into them. When IP addresses are associated to a + port, this also implies the port is associated with a subet, as the + IP address is taken from the allocation pool for a specific subnet. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Networking service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via an *networking service object*. +To instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance will be stored in the cloud region +specified by ``$region``. + +3. Create a network. +~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a network, there is more you can do with it. See +`complete user guide for networking `__. diff --git a/doc/_build/html/_sources/services/networking/USERGUIDE.md.txt b/doc/_build/html/_sources/services/networking/USERGUIDE.md.txt new file mode 100644 index 000000000..bfeb27774 --- /dev/null +++ b/doc/_build/html/_sources/services/networking/USERGUIDE.md.txt @@ -0,0 +1,582 @@ +Complete User Guide for the Networking Service +============================================== + +Networking is a service that you can use to create virtual networks and +attach cloud devices such as servers to these networks. + +This user guide introduces you the entities in the Networking service — +networks, subnets, and ports — and shows you how to create and manage +these entities. + +Table of contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ + + - `Client <#client>`__ + - `Networking service <#networking-service>`__ + +- `Networks <#networks>`__ + + - `Create a network <#create-a-network>`__ + - `Create multiple networks <#create-multiple-networks>`__ + - `List networks <#list-networks>`__ + - `Get a network <#get-a-network>`__ + - `Update a network <#update-a-network>`__ + - `Delete a network <#delete-a-network>`__ + +- `Subnets <#subnets>`__ + + - `Create a subnet <#create-a-subnet>`__ + - `Create multiple subnets <#create-multiple-subnets>`__ + - `List subnets <#list-subnets>`__ + - `Get a subnet <#get-a-subnet>`__ + - `Update a subnet <#update-a-subnet>`__ + - `Delete a subnet <#delete-a-subnet>`__ + +- `Ports <#ports>`__ + + - `Create a port <#create-a-port>`__ + - `Create multiple ports <#create-multiple-ports>`__ + - `List ports <#list-ports>`__ + - `Get a port <#get-a-port>`__ + - `Update a port <#update-a-port>`__ + - `Delete a port <#delete-a-port>`__ + +Concepts +-------- + +To use the Networking service effectively, you should understand the +following key concepts: + +- **Network**: An isolated virtual layer-2 broadcast domain that is + typically reserved for the tenant who created it unless it is + configured to be shared. The network is the main entity in the + Networking service. Ports and subnets are always associated with a + network. + +- **Subnet**: An IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the + Compute service). Each subnet must have a CIDR and must be associated + with a network. + +- **Port**: A virtual switch port on a logical network switch. Virtual + instances (such as servers created using the Compute service) attach + their interfaces into ports. The port also defines the MAC address + and the IP address or addresses to be assigned to the interfaces + plugged into them. When IP addresses are associated with a port, this + also implies the port is associated with a subnet because the IP + address is taken from the allocation pool for a specific subnet. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Networking service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate an + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Networking service +~~~~~~~~~~~~~~~~~~ + +All Networking operations are done via a *networking service object*. To +instantiate this object, call the ``networkingService`` method on the +``$client`` object. This method takes the following arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+=====================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudNetworks`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+---------------------+ + +.. code:: php + + $region = ''; + $networkingService = $client->networkingService(null, $region); + +Any networks, subnets, and ports created with this +``$networkingService`` instance are stored in the cloud region specified +by ``$region``. + +Networks +-------- + +A network is an isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it unless it is configured +to be shared. The network is the main entity in the Networking service. +Ports and subnets are always associated with a network. + +Create a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code:: php + + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple networks +~~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List networks +~~~~~~~~~~~~~ + +You can list all the networks to which you have access as shown in the +following example: + +.. code:: php + + $networks = $networkingService->listNetworks(); + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a network +~~~~~~~~~~~~~ + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code:: php + + $network = $networkingService->getNetwork('eb60583c-57ea-41b9-8d5c-8fab2d22224c'); + /** @var $network OpenCloud\Networking\Resource\Network **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a network +~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code:: php + + $network->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Subnets +------- + +A subnet represents an IP address block that can be used to assign IP +addresses to virtual instances (such as servers created using the +Compute service). Each subnet must have a CIDR and must be associated +with a network. + +Create a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code:: php + + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple subnets +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List subnets +~~~~~~~~~~~~ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a subnet +~~~~~~~~~~~~ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code:: php + + $subnet = $networkingService->getSubnet('d3f15879-fb11-49bd-a30b-7704fb98ab1e'); + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a subnet +~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a subnet +~~~~~~~~~~~~~~~ + +You can delete a subnet as shown in the following example: + +.. code:: php + + $subnet->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Ports +----- + +A port represents a virtual switch port on a logical network switch. +Virtual instances (such as servers created using the Compute service) +attach their interfaces into ports. The port also defines the MAC +address and the IP address or addresses to be assigned to the interfaces +plugged into them. When IP addresses are associated with a port, this +also implies the port is associated with a subnet because the IP address +is taken from the allocation pool for a specific subnet. + +Create a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code:: php + + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Create multiple ports +~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List ports +~~~~~~~~~~ + +You can list all the ports to which you have access as shown in the +following example: + +.. code:: php + + $ports = $networkingService->listPorts(); + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get a port +~~~~~~~~~~ + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code:: php + + $port = $networkingService->getPort('75906d20-6625-11e4-9803-0800200c9a66'); + /** @var $port OpenCloud\Networking\Resource\Port **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update a port +~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +[ `Get the executable PHP script for this +example `__ ] + +Delete a port +~~~~~~~~~~~~~ + +You can delete a port as shown in the following example: + +.. code:: php + + $port->delete(); + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/_build/html/_sources/services/networking/index.txt b/doc/_build/html/_sources/services/networking/index.txt new file mode 100644 index 000000000..335f6a10c --- /dev/null +++ b/doc/_build/html/_sources/services/networking/index.txt @@ -0,0 +1,68 @@ +Networking v2 +============= + +.. include:: ../common/clients.sample.rst + +Networking service +~~~~~~~~~~~~~~~~~~ + +Now to instantiate the Networking service: + +.. code-block:: php + + $service = $client->networkingService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + networks + subnets + ports + security-groups + security-group-rules + +Glossary +-------- + +.. glossary:: + + network + A network is an isolated virtual layer-2 broadcast domain that is typically + reserved for the tenant who created it unless you configure the network to + be shared. The network is the main entity in the Networking service. Ports + and subnets are always associated with a network. + + subnet + A subnet represents an IP address block that can be used to assign IP + addresses to virtual instances (such as servers created using the Compute + service). Each subnet must have a CIDR and must be associated with a network. + + port + A port represents a virtual switch port on a logical network switch. + Virtual instances (such as servers created using the Compute service) + attach their interfaces into ports. The port also defines the MAC address + and the IP address(es) to be assigned to the interfaces plugged into them. + When IP addresses are associated to a port, this also implies the port is + associated with a subet, as the IP address is taken from the allocation + pool for a specific subnet. + + security group + A security group is a named container for security group rules. + + security group rule + A security group rule provides users the ability to specify the types of + traffic that are allowed to pass through to and from ports on a virtual + server instance. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/networking/networks.txt b/doc/_build/html/_sources/services/networking/networks.txt new file mode 100644 index 000000000..b95d44eba --- /dev/null +++ b/doc/_build/html/_sources/services/networking/networks.txt @@ -0,0 +1,128 @@ +Networks +======== + +Create a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=======================================+==================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ +| ``tenantId`` | Owner of network. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the network | ``123456`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+---------------------------------------+----------------------------------+ + +You can create a network as shown in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->createNetwork(array( + 'name' => 'My private backend network' + )); + +`Get the executable PHP script for this example `__ + + +Create multiple networks +------------------------ + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-network>`__. + +You can create multiple networks as shown in the following example: + +.. code-block:: php + + $networks = $networkingService->createNetworks(array( + array( + 'name' => 'My private backend network #1' + ), + array( + 'name' => 'My private backend network #2' + ) + )); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + +`Get the executable PHP script for this example `_ + +List networks +------------- + +You can list all the networks to which you have access as shown in the +following example: + +.. code-block:: php + + $networks = $networkingService->listNetworks(); + + foreach ($networks as $network) { + /** @var $network OpenCloud\Networking\Resource\Network **/ + } + + +`Get the executable PHP script for this example `_ + + +Get a network +------------- + +You can retrieve a specific network by using that network's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $network OpenCloud\Networking\Resource\Network **/ + $network = $networkingService->getNetwork('{networkId}'); + +`Get the executable PHP script for this example `_ + + +Update a network +---------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++====================+===================================================================================================+=============+=============+=================+==========================================+ +| ``name`` | A human-readable name for the network. This name might not be unique. | String | No | ``null`` | ``My updated private backend network`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``adminStateUp`` | The administrative state of network. If ``false`` (down), the network does not forward packets. | Boolean | No | ``true`` | ``true`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ +| ``shared`` | Specifies whether the network resource can be accessed by any tenant. | Boolean | No | ``false`` | ``false`` | ++--------------------+---------------------------------------------------------------------------------------------------+-------------+-------------+-----------------+------------------------------------------+ + +You can update a network as shown in the following example: + +.. code-block:: php + + $network->update(array( + 'name' => 'My updated private backend network' + )); + +`Get the executable PHP script for this example `_ + + +Delete a network +~~~~~~~~~~~~~~~~ + +You can delete a network as shown in the following example: + +.. code-block:: php + + $network->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/networking/ports.txt b/doc/_build/html/_sources/services/networking/ports.txt new file mode 100644 index 000000000..8eeb07bf0 --- /dev/null +++ b/doc/_build/html/_sources/services/networking/ports.txt @@ -0,0 +1,150 @@ +Ports +===== + +Create a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+============================================================+=============+=========================================+===========================================================================================================+ +| ``networkId`` | Network this port is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``macAddress`` | MAC address to use on this port | String (MAC address in 6-octet form separated by colons) | No | Generated | ``0F:5A:6F:70:E9:5C`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.17'))`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the port. Only admin users can specify a tenant ID other than their own. | String | No | Same as the tenant creating the port | ``123456`` | ++----------------------+---------------------------------------------------------------------------------------------+------------------------------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can create a port as shown in the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->createPort(array( + 'name' => 'My port', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + )); + + +`Get the executable PHP script for this example `_ + + +Create multiple ports +--------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-port>`__. + +You can create multiple ports as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->createPorts(array( + array( + 'name' => 'My port #1', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ), + array( + 'name' => 'My port #2', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c' + ) + )); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +List ports +---------- + +You can list all the ports to which you have access as shown in the following example: + +.. code-block:: php + + $ports = $networkingService->listPorts(); + + foreach ($ports as $port) { + /** @var $port OpenCloud\Networking\Resource\Port **/ + } + +`Get the executable PHP script for this example `_ + + +Get a port +---------- + +You can retrieve a specific port by using that port's ID, as shown in +the following example: + +.. code-block:: php + + /** @var $port OpenCloud\Networking\Resource\Port **/ + $port = $networkingService->getPort('{portId}'); + +`Get the executable PHP script for this example `_ + + +Update a port +------------- + +This operation takes one parameter, an associative array, with the following keys: + ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+=============================================================================================+=======================================+=============+=========================================+===========================================================================================================+ +| ``name`` | A human-readable name for the port. This name might not be unique. | String | No | ``null`` | ``My port`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``adminStateUp`` | The administrative state of port. If ``false`` (down), the port does not forward packets. | Boolean | No | ``true`` | ``true`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``fixedIps`` | IP addresses for this port | Indexed array of associative arrays | No | Automatically allocated from the pool | ``array(array('subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', 'ipAddress' => '192.168.199.59'))`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceId`` | Identifies the device (for example, virtual server) using this port | String | No | ``null`` | ``5e3898d7-11be-483e-9732-b2f5eccd2b2e`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``deviceOwner`` | Identifies the entity (for example, DHCP agent) using this port | String | No | ``null`` | ``network:router_interface`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ +| ``securityGroups`` | Specifies the IDs of any security groups associated with this port | Indexed array of strings | No | Empty array | ``array('f0ac4394-7e4a-4409-9701-ba8be283dbc3')`` | ++----------------------+---------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------+ + +You can update a port as shown in the following example: + +.. code-block:: php + + $port->update(array( + 'fixedIps' => array( + array( + 'subnetId' => '75906d20-6625-11e4-9803-0800200c9a66', + 'ipAddress' => '192.168.199.59' + ) + ) + )); + +`Get the executable PHP script for this example `_ + + +Delete a port +------------- + +You can delete a port as shown in the following example: + +.. code-block:: php + + $port->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/networking/security-group-rules.txt b/doc/_build/html/_sources/services/networking/security-group-rules.txt new file mode 100644 index 000000000..1ca6041d1 --- /dev/null +++ b/doc/_build/html/_sources/services/networking/security-group-rules.txt @@ -0,0 +1,61 @@ +Security Group Rules +==================== + +Create a security group rule +---------------------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================================================================================================================================================================+=======================================+=============+=================+============================================+ +| ``securityGroupId`` | The security group ID to associate with this security group rule. | String | Yes | - | ``2076db17-a522-4506-91de-c6dd8e837028`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``direction`` | The direction in which the security group rule is applied. For a compute instance, an ingress security group rule is applied to incoming (ingress) traffic for that instance. An egress rule is applied to traffic leaving the instance. | String (``ingress`` or ``egress``) | Yes | - | ``ingress`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``ethertype`` | Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. | String (``IPv4`` or ``IPv6``) | No | ``IPv4`` | ``IPv6`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMin`` | The minimum port number in the range that is matched by the security group rule. If the protocol is TCP or UDP, this value must be less than or equal to the value of the ``portRangeMax`` attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``portRangeMax`` | The maximum port number in the range that is matched by the security group rule. The port\_range\_min attribute constrains the attribute. If the protocol is ICMP, this value must be an ICMP type. | Integer | No | ``null`` | ``80`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``protocol`` | The protocol that is matched by the security group rule. | String (``tcp``, ``udp``, ``icmp``) | No | ``null`` | ``tcp`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteGroupId`` | The remote group ID to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``85cc3048-abc3-43cc-89b3-377341426ac5`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ +| ``remoteIpPrefix`` | The remote IP prefix to be associated with this security group rule. You can specify either ``remoteGroupId`` or ``remoteGroupPrefix``. | String | Optional | ``null`` | ``192.168.5.0`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+-----------------+--------------------------------------------+ + +You can create a security group rule as shown in the following example: + +.. code:: php + + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + $securityGroupRule = $networkingService->createSecurityGroupRule(array( + 'securityGroupId' => '2076db17-a522-4506-91de-c6dd8e837028', + 'direction' => 'egress', + 'ethertype' => 'IPv4', + 'portRangeMin' => 80, + 'portRangeMax' => 80, + 'protocol' => 'tcp', + 'remoteGroupId' => '85cc3048-abc3-43cc-89b3-377341426ac5' + )); + +`Get the executable PHP script for this example `_ + + +List security group rules +------------------------- + +You can list all the security group rules to which you have access as +shown in the following example: + +.. code:: php + + $securityGroupRules = $networkingService->listSecurityGroupRules(); + foreach ($securityGroupRules as $securityGroupRule) { + /** @var $securityGroupRule OpenCloud\Networking\Resource\SecurityGroupRule **/ + } + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/networking/security-groups.txt b/doc/_build/html/_sources/services/networking/security-groups.txt new file mode 100644 index 000000000..cde5c9a2d --- /dev/null +++ b/doc/_build/html/_sources/services/networking/security-groups.txt @@ -0,0 +1,67 @@ +Security Groups +=============== + +Create a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+================================================================================+=============+=============+=================+=====================================+ +| ``name`` | A human-readable name for the security group. This name might not be unique. | String | Yes | - | ``new-webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ +| ``description`` | Description of the security group. | String | No | ``null`` | ``security group for webservers`` | ++-------------------+--------------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------------------+ + +You can create a security group as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->createSecurityGroup(array( + 'name' => 'new-webservers', + 'description' => 'security group for webservers' + )); + +`Get the executable PHP script for this example `_ + +List security groups +~~~~~~~~~~~~~~~~~~~~ + +You can list all the security groups to which you have access as shown +in the following example: + +.. code-block:: php + + $securityGroups = $networkingService->listSecurityGroups(); + foreach ($securityGroups as $securityGroup) { + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + } + +`Get the executable PHP script for this example `_ + +Get a security group +~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific security group by using that security +group’s ID, as shown in the following example: + +.. code-block:: php + + /** @var $securityGroup OpenCloud\Networking\Resource\SecurityGroup **/ + $securityGroup = $networkingService->getSecurityGroup('{secGroupId}'); + +`Get the executable PHP script for this example `_ + +Delete a security group +~~~~~~~~~~~~~~~~~~~~~~~ + +You can delete a security group as shown in the following example: + +.. code-block:: php + + $securityGroup->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/networking/subnets.txt b/doc/_build/html/_sources/services/networking/subnets.txt new file mode 100644 index 000000000..9a6c2a7b4 --- /dev/null +++ b/doc/_build/html/_sources/services/networking/subnets.txt @@ -0,0 +1,152 @@ +Subnets +======= + +Create a subnet +--------------- + +This operation takes one parameter, an associative array, with the following keys: + ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++=======================+===================================================================================================================+=======================================+=============+========================================================================+=================================================================================+ +| ``networkId`` | Network this subnet is associated with | String | Yes | - | ``eb60583c-57ea-41b9-8d5c-8fab2d22224c`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``ipVersion`` | IP version | Integer (``4`` or ``6``) | Yes | - | ``4`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``cidr`` | CIDR representing the IP address range for this subnet | String (CIDR) | Yes | - | ``192.168.199.0/25`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My subnet`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.199.128`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``allocationPools`` | Subranges of the CIDR available for dynamic allocation to ports | Indexed array of associative arrays | No | Every IP address in CIDR, excluding gateway IP address if configured | ``array(array('start' => '192.168.199.2', 'end' => '192.168.199.127'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP addresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.19.20'))`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ +| ``tenantId`` | Owner of the subnet. Only admin users can specify a tenant ID other than their own. | String | No | Same as tenant creating the subnet | ``123456`` | ++-----------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+------------------------------------------------------------------------+---------------------------------------------------------------------------------+ + +You can create a subnet as shown in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->createSubnet(array( + 'name' => 'My subnet', + 'networkId' => 'eb60583c-57ea-41b9-8d5c-8fab2d22224c', + 'ipVersion' => 4, + 'cidr' => '192.168.199.0/25' + )); + +`Get the executable PHP script for this example `_ + + +Create multiple subnets +----------------------- + +This operation takes one parameter, an indexed array. Each element of +this array must be an associative array with the keys shown in `the +preceding table <#create-a-subnet>`__. + +You can create multiple subnets as shown in the following example: + +.. code-block:: php + + $subnets = $networkingService->createSubnets(array( + array( + 'name' => 'My subnet #1' + ), + array( + 'name' => 'My subnet #2' + ) + )); + + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +List subnets +------------ + +You can list all the subnets to which you have access as shown in the +following example: + +.. code-block:: php + + $subnets = $networkingService->listSubnets(); + foreach ($subnets as $subnet) { + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + } + +`Get the executable PHP script for this example `_ + + +Get a subnet +------------ + +You can retrieve a specific subnet by using that subnet's ID, as shown +in the following example: + +.. code-block:: php + + /** @var $subnet OpenCloud\Networking\Resource\Subnet **/ + $subnet = $networkingService->getSubnet('{subnetId}'); + +`Get the executable PHP script for this example `_ + + +Update a subnet +--------------- + +This operation takes one parameter, an associative array, with the +following keys: + ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++======================+==================================================================================================================+=======================================+=============+============================+=================================================================================+ +| ``name`` | A human-readable name for the subnet. This name might not be unique. | String | No | ``null`` | ``My updated subnet`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``gatewayIp`` | IP address of the default gateway used by devices on this subnet | String (IP address) | No | First IP address in CIDR | ``192.168.62.155`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``dnsNameservers`` | DNS nameservers used by hosts in this subnet | Indexed array of strings | No | Empty array | ``array('4.4.4.4', '8.8.8.8')`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``hostRoutes`` | Routes that should be used by devices with IP adresses from this subnet (not including the local subnet route) | Indexed array of associative arrays | No | Empty array | ``array(array('destination' => '1.1.1.0/24', 'nexthop' => '192.168.17.19'))`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ +| ``enableDhcp`` | Specifies whether DHCP is enabled for this subnet | Boolean | No | ``true`` | ``false`` | ++----------------------+------------------------------------------------------------------------------------------------------------------+---------------------------------------+-------------+----------------------------+---------------------------------------------------------------------------------+ + +You can update a subnet as shown in the following example: + +.. code-block:: php + + $subnet->update(array( + 'name' => 'My updated subnet', + 'hostRoutes' => array( + array( + 'destination' => '1.1.1.0/24', + 'nexthop' => '192.168.17.19' + ) + ), + 'gatewayIp' => '192.168.62.155' + )); + +`Get the executable PHP script for this example `_ + + +Delete a subnet +--------------- + +You can delete a subnet as shown in the following example: + +.. code-block:: php + + $subnet->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/object-store/Access.md.txt b/doc/_build/html/_sources/services/object-store/Access.md.txt new file mode 100644 index 000000000..ee85a938e --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Access.md.txt @@ -0,0 +1,75 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles', 'IAD'); # Second argument is the region you want + +Temporary URLs +-------------- + +Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they're CDN-enabled. + +Set "temporary URL" metadata key +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You must set this "secret" value on your account, where it can be used +in a global state: + +.. code:: php + + $account = $service->getAccount(); + $account->setTempUrlSecret('my_secret'); + + echo $account->getTempUrlSecret(); + +The string argument of ``setTempUrlSecret()`` is optional - if left out, +the SDK will generate a random hashed secret for you. + +Create a temporary URL +~~~~~~~~~~~~~~~~~~~~~~ + +Once you've set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute: + +.. code:: php + + $object->getTemporaryUrl(60, 'GET'); + +To allow PUT access for 1 hour: + +.. code:: php + + $object->getTemporaryUrl(360, 'PUT'); + +Hosting websites on CloudFiles +------------------------------ + +To host a static (i.e. HTML) website on CloudFiles, you must follow +these steps: + +1. CDN-enable a container +2. Upload all HTML content. You can use nested directory structures. +3. Tell CloudFiles what to use for your default index page like this: + +.. code:: php + + $container->setStaticIndexPage('index.html'); + +4. (Optional) Tell CloudFiles which error page to use by default: + +.. code:: php + + $container->setStaticErrorPage('error.html'); + +Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object. diff --git a/doc/_build/html/_sources/services/object-store/Account.md.txt b/doc/_build/html/_sources/services/object-store/Account.md.txt new file mode 100644 index 000000000..a2d380b2a --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Account.md.txt @@ -0,0 +1,33 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +View Account Details +-------------------- + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +.. code:: php + + $account = $service->getAccount(); + + // Either return the full Metadata object + $details = $account->getDetails(); + + // or individual values + $account->getContainerCount(); + $account->getObjectCount(); + $account->getBytesUsed(); + diff --git a/doc/_build/html/_sources/services/object-store/Container.md.cdn.txt b/doc/_build/html/_sources/services/object-store/Container.md.cdn.txt new file mode 100644 index 000000000..2f1c8ea68 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Container.md.cdn.txt @@ -0,0 +1,90 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(RACKSPACE_US, array( + + )); + + $service = $client->objectStoreService('cloudFiles'); + +To access the CDN functionality of a particular container: + +.. code:: php + + $container = $service->getContainer('foo_bar'); + + $cdn = $container->getCdn(); + +List CDN-enabled container +-------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code:: php + + $cdnService = $service->getCdnService(); + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + + } + +CDN-enable and -disable a container +----------------------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code:: php + + $container->enableCdn(); + $container->disableCdn(); + +Serving containers through SSL +------------------------------ + +.. code:: php + + $cdn->getCdnSslUri(); + +Streaming CDN-enabled containers +-------------------------------- + +.. code:: php + + $cdn->getCdnStreamingUri(); + +iOS streaming +------------- + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code:: php + + $cdn->getIosStreamingUri(); + +CDN logging +----------- + +To enable and disable logging for your CDN: + +.. code:: php + + $cdn->enableCdnLogging(); + $cdn->disableCdnLogging(); + diff --git a/doc/_build/html/_sources/services/object-store/Container.md.storage.txt b/doc/_build/html/_sources/services/object-store/Container.md.storage.txt new file mode 100644 index 000000000..89798fdd0 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Container.md.storage.txt @@ -0,0 +1,218 @@ +Setup +----- + +.. code:: php + + use OpenCloud\Rackspace; + + // Create a client object to communicate with various Rackspace Cloud services. + $client = new Rackspace(RACKSPACE_US, array( + 'username' => 'Replace this with your Rackspace Cloud user name', + 'apiKey' => 'Replace this with your Rackspace Cloud API key' + )); + + // Create a service object to use the object store service. The sample code + // creates the object store in the 'DFW' region. + $service = $client->objectStoreService('cloudFiles', 'DFW'); + +Create container +---------------- + +To create a new container, you just need to define its name: + +.. code:: php + + $container = $service->createContainer('my_amazing_container'); + +If the response returned is ``FALSE``, there was an API error - most +likely due to the fact you have a naming collision. + +Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +List containers +--------------- + +Return a list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $containerList = $service->listContainers(); + + while ($container = $containerList->next()) { + // Do stuff; some examples below + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } + +Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite's +memcmp() function, regardless of text encoding. + +The list is limited to 10,000 containers at a time. See 1.3 for ways to +limit and navigate this list. + +Return a formatted list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently, the SDK only supports JSON-formatted responses. + +Controlling a large list of containers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may limit and control this list of results by using the ``marker`` +and ``end_marker`` parameters. The former parameter (``marker``) tells +the API where to begin the list, and the latter (``end_marker``) tells +it where to end the list. You may use either of them independently or +together. You may also use the ``limit`` parameter to fix the number of +containers returned. + +To list a set of containers between two fixed points: + +.. code:: php + + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); + +Or to return a limited set: + +.. code:: php + + $someContainers = $service->listContainers(array('limit' => 560)); + +Get container +------------- + +To retrieve a certain container, either to access its object or +metadata: + +.. code:: php + + $container = $service->getContainer('container_name'); + + echo $container->getObjectCount(); + echo $container->getBytesUsed(); + +Delete container +---------------- + +Deleting a container is easy: + +.. code:: php + + $container->delete(); + +Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +``$deleteObjects`` parameter to ``TRUE`` like so: + +.. code:: php + + $container->delete(TRUE); + +You can also do it manually: + +.. code:: php + + $container->deleteAllObjects(); + $container->delete(); + +Create or update container metadata +----------------------------------- + +.. code:: php + + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); + +Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To *append* values +to the current metadata: + +.. code:: php + + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); + +If you only want to set the metadata to the local object, and not +immediately retain these values on the API, you can use a standard +setter method - which can contribute to eventual actions like an update: + +.. code:: php + + $container->setMetadata(array('Foo' => 'Bar')); + +Container quotas +---------------- + +The container\_quotas middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check. + +To set quotas for a container: + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); + +And to retrieve them: + +.. code:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); + +Access log delivery +------------------- + +To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage). + +.. code:: php + + $container->enableLogging(); + $container->disableLogging(); + +Syncing containers +------------------ + +You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory: + +.. code:: php + + $container->uploadDirectory('/home/Jamie/blog'); + +There are four scenarios you should be aware of: + ++------------------------+-----------------------+----------------------+--------------------------------+ +| Local | Remote | Comparison | Action | ++========================+=======================+======================+================================+ +| File exists | File exists | Identical checksum | No action | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File exists | Different checksum | Local file overwrites remote | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File does not exist | - | Local file created in Swift | ++------------------------+-----------------------+----------------------+--------------------------------+ +| Files does not exist | File exists | - | Remote file deleted | ++------------------------+-----------------------+----------------------+--------------------------------+ + diff --git a/doc/_build/html/_sources/services/object-store/Migrating.md.storage.txt b/doc/_build/html/_sources/services/object-store/Migrating.md.storage.txt new file mode 100644 index 000000000..99738554b --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Migrating.md.storage.txt @@ -0,0 +1,101 @@ +Migrating containers (across regions) +===================================== + +Introduction +------------ + +Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a ``COPY`` +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality. + +You **will** be charged for bandwidth between regions, so it's advisable +to use ServiceNet where possible (which is free). + +Requirements +------------ + +- You must install the full Guzzle package, so that the process can + take advantage of Guzzle's batching functionality (it allows parallel + requests to be batched for greater efficiency). You can do this by + running: + +.. code:: bash + + php composer.phar install --dev + +- Depending on the size and number of transfer items, you will need to + raise PHP's memory limit: + +.. code:: php + + ini_set('memory_limit', '512M'); + +- You will need to enact some kind of backoff/retry strategy for rate + limits. Guzzle comes with a convenient feature that just needs to be + added as a normal subscriber: + +.. code:: php + + use Guzzle\Plugin\Backoff\BackoffPlugin; + + $client->addSubscriber(BackoffPlugin::getExponentialBackoff(10, array(500, 503, 408))); + +This tells the client to retry up to ``10`` times for failed requests +have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + +Setup +----- + +You can access all this functionality by executing: + +.. code:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); + + $iadService->migrateContainer($oldContainer, $newContainer); + +It's advisable to do this process in a Cloud Server in one of the two +regions you're migrating to/from. This allows you to use ``privateURL`` +as the third argument in the ``objectStoreService`` methods like this: + +.. code:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + +This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free. + +Options +------- + +You can pass in an array of arguments to the method: + +.. code:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); + + $iadService->migrateContainer($oldContainer, $newContainer, $options); + +Options explained +~~~~~~~~~~~~~~~~~ + ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| Name | Description | Default | ++========================+===============================================================================================================================================================================================================================================================================================================================================+===========+ +| ``read.pageLimit`` | When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional ``objectList`` method, which calls the ``PaginatedIterator``. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API) | 10,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``read.batchLimit`` | After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together. | 1,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ + diff --git a/doc/_build/html/_sources/services/object-store/Object.md.cdn.txt b/doc/_build/html/_sources/services/object-store/Object.md.cdn.txt new file mode 100644 index 000000000..64ac143b3 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Object.md.cdn.txt @@ -0,0 +1,25 @@ +Setup +----- + +You will need to instantiate the container object and access its CDN +functionality as `documented +here `__. + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); + diff --git a/doc/_build/html/_sources/services/object-store/Object.md.storage.txt b/doc/_build/html/_sources/services/object-store/Object.md.storage.txt new file mode 100644 index 000000000..16dab18f4 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/Object.md.storage.txt @@ -0,0 +1,330 @@ +Setup +----- + +Conceptually, a container contains objects (also known as files). In +order to work with objects, you will need to instantiate a container +object first as `documented +here `__. + +Note on object properties +------------------------- + +Please be aware that you cannot directly access the properties of +DataObject anymore, you **must** use appropriate getter/ setter methods: + ++----------------------+------------------------+ +| Property | Method | ++======================+========================+ +| Parent container | ``getContainer`` | ++----------------------+------------------------+ +| Name | ``getName`` | ++----------------------+------------------------+ +| Body of file | ``getContent`` | ++----------------------+------------------------+ +| Size of file | ``getContentLength`` | ++----------------------+------------------------+ +| Type of file | ``getContentType`` | ++----------------------+------------------------+ +| ETag checksum | ``getEtag`` | ++----------------------+------------------------+ +| Last modified date | ``getLastModified`` | ++----------------------+------------------------+ + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + + **Note:** Unlike previous versions, you do not need to manually + specify your object's content type. The API will do this for you. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +To upload a single/basic file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $data = fopen('/path/to/sample.mp3', 'r+'); + + // alternatively, you can pass in a string as the file contents `$data` argument (instead of a resource) + + $meta = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow' + ); + + $metaHeaders = DataObject::stockHeaders($meta); + $customHeaders = array(); + $allHeaders = $metaHeaders + $customHeaders; + + $container->uploadObject('sample.mp3', $data, $allHeaders); + +To upload multiple small-to-mid sized files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + +To upload large files +~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build your +transfer, upon which you can execute your upload functionality. For your +convenience, the Container resource object contains a simple method to +do this heavy lifting for you: + +.. code:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/jamie/video.mov', + 'metadata' => array( + 'Author' => 'Jamie' + ), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifest is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + // ... do something + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code:: php + + $container->objectList(array('prefix' => 'logFile_')); + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code:: php + + $file = $container->getObject('summer_vacation.mp4'); + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +:: + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + +Update an existing object +------------------------- + +Updating content is easy: + +.. code:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + +Copy object +----------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code:: php + + $object->copy('/container_2/new_object_name'); + +Delete object +------------- + +.. code:: php + + $object->delete(); + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code:: php + + $container->getPartialObject('summer_vacation.mp4'); + +In order to access the metadata on a partial or complete object, use: + +.. code:: php + + $object->getMetadata(); + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code:: php + + $object->refresh(); + +You can also update to get the latest metadata: + +.. code:: php + + $object->retrieveMetadata(); + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + +Bulk delete +----------- + +Bulk delete a set of paths: + +.. code:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); + diff --git a/doc/_build/html/_sources/services/object-store/README.md.txt b/doc/_build/html/_sources/services/object-store/README.md.txt new file mode 100644 index 000000000..01cf26733 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/README.md.txt @@ -0,0 +1,86 @@ +Object Store +============ + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Specifically, a cloud is made up of one or more regions. Each region can +have several **containers**, created by a user. Each container can +container several **objects** (sometimes referred to as files), uploaded +by the user. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Choose one of the following two options: + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Object Store service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +3. Create a container for your objects (also referred to as files). +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +4. Upload an object to the container. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +There is a lot more you can do with containers and objects. See the +`complete user guide to the Object Store service `__. diff --git a/doc/_build/html/_sources/services/object-store/USERGUIDE.md.txt b/doc/_build/html/_sources/services/object-store/USERGUIDE.md.txt new file mode 100644 index 000000000..d9732ea36 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/USERGUIDE.md.txt @@ -0,0 +1,900 @@ +The Complete User Guide to the Object Store Service +=================================================== + +**Object Store** is an object-based storage system that stores content +and metadata as objects in a cloud. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the object store service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with a vanilla OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as shown below. + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'apiKey' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as shown below. + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Object Store Service +~~~~~~~~~~~~~~~~~~~~ + +All operations on the object store are done via an object store service +object. + +.. code:: php + + $region = 'DFW'; + $objectStoreService = $client->objectStoreService(null, $region); + +In the example above, you are connecting to the ``DFW`` region of the +cloud. Any containers and objects created with this +``$objectStoreService`` instance will be stored in that cloud region. + +Containers +---------- + +A **container** defines a namespace for **objects**. An object with the +same name in two different containers represents two different objects. + +For example, you may create a container called ``logos`` to hold all the +image files for your blog. + +A container may contain zero or more objects in it. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Create Container +~~~~~~~~~~~~~~~~ + +.. code:: php + + $container = $objectStoreService->createContainer('logos'); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Details +~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a single container's details by using its name. An +instance of ``OpenCloud\ObjectStore\Resource\Container`` is returned. + +.. code:: php + + $container = $objectStoreService->getContainer('logos'); + + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + +[ `Get the executable PHP script for this +example `__ ] + +List Containers +~~~~~~~~~~~~~~~ + +You can retrieve a list of all your containers. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $containers = $objectStoreService->listContainers(); + foreach ($containers as $container) { + /** @var $container OpenCloud\ObjectStore\Resource\Container **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Set or Update Container Metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can set metadata on a container. + +.. code:: php + + $container->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Get Container Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a container. + +.. code:: php + + $containerMetadata = $container->getMetadata(); + +[ `Get the executable PHP script for this +example `__ ] + +Delete Container +~~~~~~~~~~~~~~~~ + +When you no longer have a need for the container, you can remove it. + +If the container is empty (that is, it has no objects in it), you can +remove it as shown below: + +.. code:: php + + $container->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +If the container is not empty (that is, it has objects in it), you have +two choices in how to remove it: + +- `Individually remove each object <#delete-object>`__ in the + container, then remove the container itself as shown above, or + +- Remove the container and all the objects within it as shown below: + + .. code:: php + + $container->delete(true); + + [ `Get the executable PHP script for this + example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in a container. + +.. code:: php + + $containerObjectCount = $container->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerObjectCount`` will contain the number +of objects in the container represented by ``$container``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by a container, in bytes. + +.. code:: php + + $containerSizeInBytes = $container->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$containerSizeInBytes`` will contain the space +used, in bytes, by the container represented by ``$container``. + +Container Quotas +~~~~~~~~~~~~~~~~ + +Set Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum number of objects that may be stored +in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = 25; + $container->setCountQuota($maximumNumberOfObjectsAllowedInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Set Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can set a quota for the maximum total space (in bytes) used by +objects in a container. + +.. code:: php + + use OpenCloud\Common\Constants\Size; + + $maximumTotalSizeOfObjectsInContainer = 5 * Size::GB; + $container->setBytesQuota($maximumTotalSizeOfObjectsInContainer); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Number of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum number of objects that may be +stored in a container. + +.. code:: php + + $maximumNumberOfObjectsAllowedInContainer = $container->getCountQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Quota for Total Size of Objects +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can retrieve the quota for the maximum total space (in bytes) used +by objects in a container. + +.. code:: php + + $maximumTotalSizeOfObjectsAllowedInContainer = $container->getBytesQuota(); + +[ `Get the executable PHP script for this +example `__ ] + +Objects +------- + +An **object** (sometimes referred to as a file) is the unit of storage +in an Object Store. An object is a combination of content (data) and +metadata. + +For example, you may upload an object named ``php-elephant.jpg``, a JPEG +image file, to the ``logos`` container. Further, you may assign metadata +to this object to indicate that the author of this object was someone +named Jane Doe. + + **Note:** when working with names that contain non-standard + alphanumerical characters (such as spaces or non-English + characters), you must ensure they are encoded with + ```urlencode`` `__ before passing them in + +Upload Object +~~~~~~~~~~~~~ + +Once you have created a container, you can upload objects to it. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +It is also possible to upload an object and associate metadata with it. + +.. code:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'php-elephant.jpg'; + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData, $allHeaders); + +[ `Get the executable PHP script for this +example `__ ] + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Pseudo-hierarchical Folders +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Although you cannot nest directories in an Object Store, you can +simulate a hierarchical structure within a single container by adding +forward slash characters (``/``) in the object name. + +.. code:: php + + $localFileName = '/path/to/local/php-elephant.jpg'; + $remoteFileName = 'languages/php/elephant.jpg'; + + $fileData = fopen($localFileName, 'r'); + $container->uploadObject($remoteFileName, $fileData); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, an image file from the local filesystem +(``/path/to/local/php-elephant.jpg``) is uploaded to a container in the +Object Store. Within that container, the filename is +``languages/php/elephant.jpg``, where ``languages/php/`` is a +pseudo-hierarchical folder hierarchy. + +Note that while we call ``fopen`` to open the file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``uploadObject`` call. + +Upload Multiple Objects +^^^^^^^^^^^^^^^^^^^^^^^ + +You can upload more than one object at a time to a container. + +.. code:: php + + $objects = array( + array( + 'name' => 'php-elephant.jpg', + 'path' => '/path/to/local/php-elephant.jpg' + ), + array( + 'name' => 'python-snake.jpg', + 'path' => '/path/to/local/python-snake.jpg' + ), + a + ); + + $container->uploadObjects($objects); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, the contents of two files present on the local +filesystem are uploaded as objects to the container referenced by +``$container``. + +Instead of specifying the ``path`` key in an element of the ``$objects`` +array, you can specify a ``body`` key whose value is a string or a +stream representation. + +Finally, you can pass headers as the second parameter to the +``uploadObjects`` method. These headers will be applied to every object +that is uploaded. + +:: + + $metadata = array('author' => 'Jane Doe'); + + $customHeaders = array(); + $metadataHeaders = DataObject::stockHeaders($metadata); + $allHeaders = $customHeaders + $metadataHeaders; + + $container->uploadObjects($objects, $allHeaders); + +[ `Get the executable PHP script for this +example `__ +] + +In the example above, every object referenced within the ``$objects`` +array will be uploaded with the same metadata. + +Large Objects +~~~~~~~~~~~~~ + +If you want to upload objects larger than 5GB in size, you must use a +different upload process. + +.. code:: php + + $options = array( + 'name' => 'san_diego_vacation_video.mp4', + 'path' => '/path/to/local/videos/san_diego_vacation.mp4' + ); + $objectTransfer = $container->setupObjectTransfer($options); + $objectTransfer->upload(); + +[ `Get the executable PHP script for this +example `__ ] + +The process shown above will automatically partition your large object +into small chunks and upload them concurrently to the container +represented by ``$container``. + +You can tune the parameters of this process by specifying additional +options in the ``$options`` array. Here is a complete listing of keys +that can be specified in the ``$options`` array: + ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++===================+================================================================================================================================================================================================================================================================================================================+=================================================+=================================================+==================================================+====================================================+ +| ``name`` | Name of large object in container | String | Yes | - | ``san_diego_vacation_video.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``path`` | Path to file containing object data on local filesystem | String | One of ``path`` or ``body`` must be specified | - | ``/path/to/local/videos/san_diego_vacation.mp4`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``body`` | String or stream representation of object data | String \| Stream | One of ``path`` or ``body`` must be specified | - | ``... lots of data ...`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``metadata`` | Metadata for the object | Associative array of metadata key-value pairs | No | ``array()`` | ``array( "Author" => "Jane Doe" )`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``partSize`` | The size, in bytes, of each chunk that the large object is partitioned into prior to uploading | Integer | No | ``1073741824`` (1GB) | ``52428800`` (50MB) | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``concurrency`` | The number of concurrent transfers to execute as part of the upload | Integer | No | ``1`` (no concurrency; upload chunks serially) | ``10`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ +| ``progress`` | A `callable function or method `__ which is called to report progress of the the upload. See ```CURLOPT_PROGRESSFUNCTION`` documentation `__ for details on parameters passed to this callable function or method. | String (callable function or method name) | No | None | ``reportProgress`` | ++-------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------+-------------------------------------------------+--------------------------------------------------+----------------------------------------------------+ + +Auto-extract Archive Files +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can upload a tar archive file and have the Object Store service +automatically extract it into a container. + +.. code:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $localArchiveFileName = '/path/to/local/image_files.tar.gz'; + $remotePath = 'images/'; + + $fileData = fopen($localArchiveFileName, 'r'); + $objectStoreService->bulkExtract($remotePath, $fileData, UrlType::TAR_GZ); + +[ `Get the executable PHP script for this +example `__ ] + +In the above example, a local archive file named ``image_files.tar.gz`` +is uploaded to an Object Store container named ``images`` (defined by +the ``$remotePath`` variable). + +Note that while we call ``fopen`` to open a file resource, we do not +call ``fclose`` at the end. The file resource is automatically closed +inside the ``bulkExtract`` call. + +The third parameter to ``bulkExtract`` is the type of the archive file +being uploaded. The acceptable values for this are: + +- ``UrlType::TAR`` for tar archive files, *or*, +- ``UrlType:TAR_GZ`` for tar archive files that are compressed with + gzip, *or* +- ``UrlType::TAR_BZ`` for tar archive file that are compressed with + bzip + +Note that the value of ``$remotePath`` could have been a +(pseudo-hierarchical folder)[#pseudo-hierarchical-folders] such as +``images/blog`` as well. + +List Objects in a Container +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all the objects stored in a container. An instance of +``OpenCloud\Common\Collection\PaginatedIterator`` is returned. + +.. code:: php + + $objects = $container->objectList(); + foreach ($objects as $object) { + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +You can list only those objects in the container whose names start with +a certain prefix. + +.. code:: php + + $options = array( + 'prefix' => 'php' + ); + + $objects = $container->objectList($options); + +[ `Get the executable PHP script for this +example `__ ] + +In general, the ``objectList()`` method described above takes an +optional parameter (``$options`` in the example above). This parameter +is an associative array of various options. Here is a complete listing +of keys that can be specified in the ``$options`` array: + ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| Key name | Description | Data Type | Required? | Default Value | Example | ++==================+=============================================================================+=============+=============+=================+=========================+ +| ``prefix`` | Given a string x, limits the results to object names beginning with x. | String | No | | ``php`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``limit`` | Given an integer n, limits the number of results to at most n values. | Integer | No | | 10 | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``marker`` | Given a string x, returns object names greater than the specified marker. | String | No | | ``php-elephant.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ +| ``end_marker`` | Given a string x, returns object names less than the specified marker. | String | No | | ``python-snakes.jpg`` | ++------------------+-----------------------------------------------------------------------------+-------------+-------------+-----------------+-------------------------+ + +Retrieve Object +~~~~~~~~~~~~~~~ + +You can retrieve an object and its metadata, given the object's +container and name. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getObject($objectName); + + /** @var $object OpenCloud\ObjectStore\Resource\DataObject **/ + + $objectContent = $object->getContent(); + + /** @var $objectContent Guzzle\Http\EntityBody **/ + + // Write object content to file on local filesystem. + $objectContent->rewind(); + $stream = $objectContent->getStream(); + $localFilename = tempnam("/tmp", 'php-opencloud-'); + file_put_contents($localFilename, $stream); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$object`` is the object named +``php-elephant.jpg`` in the container represented by ``$container``. +Further, ``$objectContent`` represents the contents of the object. It is +of type +```Guzzle\Http\EntityBody`` `__. + +Retrieve Object Metadata +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve just an object's metadata without retrieving its +contents. + +.. code:: php + + $objectName = 'php-elephant.jpg'; + $object = $container->getPartialObject($objectName); + $objectMetadata = $object->getMetadata(); + + /** @var $objectMetadata \OpenCloud\Common\Metadata **/ + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, while ``$object`` is an instance of +``OpenCloud\ObjectStore\Resource\DataObject``, that instance is only +partially populated. Specifically, only properties of the instance +relating to object metadata are populated. + +Temporary URLs +~~~~~~~~~~~~~~ + +The Temporary URL feature allows you to create limited-time Internet +addresses that allow you to grant limited access to your Object Store +account. Using this feature, you can allow others to retrieve or place +objects in your Object Store account for a specified amount of time. +Access to the temporary URL is independent of whether or not your +account is `CDN-enabled <#cdn-containers>`__. Even if you do not +CDN-enable a container, you can still grant temporary public access +through a temporary URL. + +First, you must set the temporary URL secret on your account. This is a +one-time operation; you only need to perform it the very first time you +wish to use the temporary URLs feature. + +.. code:: php + + $account->setTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation is carried out on ``$account``, which is an +instance of ``OpenCloud\ObjectStore\Resource\Account``, a class +representing `your object store account <#accounts>`__. + +The above operation will generate a random secret and set it on your +account. Instead of a random secret, if you wish to provide a secret, +you can supply it as a parameter to the ``setTempUrlSecret`` method. + +.. code:: php + + $account->setTempUrlSecret(''); + +[ `Get the executable PHP script for this +example `__ +] + +Once a temporary URL secret has been set on your account, you can +generate a temporary URL for any object in your Object Store. + +.. code:: php + + $expirationTimeInSeconds = 3600; // one hour from now + $httpMethodAllowed = 'GET'; + $tempUrl = $object->getTemporaryUrl($expirationTimeInSeconds, $httpMethodAllowed); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, a temporary URL for the object is generated. This +temporary URL will provide public access to the object for an hour (3600 +seconds), as specified by the ``$expirationTimeInSeconds`` variable. +Further, only GET HTTP methods will be allowed on this URL, as specified +by the ``$httpMethodAllowed`` variable. The other value allowed for the +``$httpMethodAllowed`` variable would be ``PUT``. + +You can also retrieve the temporary URL secret that has been set on your +account. + +.. code:: php + + $tempUrlSecret = $account->getTempUrlSecret(); + +[ `Get the executable PHP script for this +example `__ ] + +Update Object +~~~~~~~~~~~~~ + +You can update an object's contents (as opposed to `updating its +metadata <#update-object-metadata>`__) by simply re-\ `uploading the +object <#upload-object>`__ to its container using the same object name +as before. + +Update Object Metadata +~~~~~~~~~~~~~~~~~~~~~~ + +You can update an object's metadata after it has been uploaded to a +container. + +.. code:: php + + $object->saveMetadata(array( + 'author' => 'John Doe' + )); + +[ `Get the executable PHP script for this +example `__ ] + +Copy Object +~~~~~~~~~~~ + +You can copy an object from one container to another, provided the +destination container already exists. + +.. code:: php + + $object->copy('logos_copy/php.jpg'); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, both the name of the destination container +(``logos_copy``)and the name of the destination object (``php.jpg``) +have to be specified, separated by a ``/``. + +Delete Object +~~~~~~~~~~~~~ + +When you no longer need an object, you can delete it. + +.. code:: php + + $object->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Bulk Delete +~~~~~~~~~~~ + +While you can delete individual objects as shown above, you can also +delete objects and empty containers in bulk. + +.. code:: php + + $objectStoreService->bulkDelete(array( + 'logos/php-elephant.png', + 'logos/python-snakes.png', + 'some_empty_container' + )); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, two objects (``some_container/object_a.png``, +``some_other_container/object_z.png``) and one empty container +(``some_empty_container``) are all being deleted in bulk via a single +command. + +CDN Containers +-------------- + +Note: The functionality described in this section is available only on +the Rackspace cloud. It will not work as described when working with a +vanilla OpenStack cloud. + +Any container can be converted to a CDN-enabled container. When this is +done, the objects within the container can be accessed from anywhere on +the Internet via a URL. + +Enable CDN Container +~~~~~~~~~~~~~~~~~~~~ + +To take advantage of CDN capabilities for a container and its objects, +you must CDN-enable that container. + +.. code:: php + + $container->enableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Public URLs +~~~~~~~~~~~ + +Once you have CDN-enabled a container, you can retrieve a +publicly-accessible URL for any of its objects. There are four types of +publicly-accessible URLs for each object. Each type of URL is meant for +a different purpose. The sections below describe each of these URL types +and how to retrieve them. + +HTTP URL +^^^^^^^^ + +You can use this type of URL to access the object over HTTP. + +:: + + $httpUrl = $object->getPublicUrl(); + +[ `Get the executable PHP script for this +example `__ ] + +Secure HTTP URL +^^^^^^^^^^^^^^^ + +You can use this type of URL to access the object over HTTP + TLS/SSL. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $httpsUrl = $object->getPublicUrl(UrlType::SSL); + +[ `Get the executable PHP script for this +example `__ ] + +Streaming URL +^^^^^^^^^^^^^ + +You can use this type of URL to stream a video or audio object using +Adobe's HTTP Dynamic Streaming. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $streamingUrl = $object->getPublicUrl(UrlType::STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +IOS Streaming URL +^^^^^^^^^^^^^^^^^ + +You can use this type of URL to stream an audio or video object to an +iOS device. + +:: + + use OpenCloud\ObjectStore\Constants\UrlType; + + $iosStreamingUrl = $object->getPublicUrl(UrlType::IOS_STREAMING); + +[ `Get the executable PHP script for this +example `__ ] + +Update CDN Container TTL +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can update the TTL of a CDN-enabled container. + +.. code:: php + + $cdnContainer = $container->getCdn(); + $cdnContainer->setTtl(); + +[ `Get the executable PHP script for this +example `__ ] + +Disable CDN Container +~~~~~~~~~~~~~~~~~~~~~ + +If you no longer need CDN capabilities for a container, you can disable +them. + +.. code:: php + + $container->disableCdn(); + +[ `Get the executable PHP script for this +example `__ ] + +Accounts +-------- + +An **account** defines a namespace for **containers**. An account can +have zero or more containers in it. + +Retrieve Account +~~~~~~~~~~~~~~~~ + +You must retrieve the account before performing any operations on it. + +.. code:: php + + $account = $objectStoreService->getAccount(); + +Get Container Count +~~~~~~~~~~~~~~~~~~~ + +You can quickly find out how many containers are in your account. + +.. code:: php + + $accountContainerCount = $account->getContainerCount(); + +[ `Get the executable PHP script for this +example `__ ] + +Get Object Count +~~~~~~~~~~~~~~~~ + +You can quickly find out how many objects are in your account. + +.. code:: php + + $accountObjectCount = $account->getObjectCount(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountObjectCount`` will contain the number of +objects in the account represented by ``$account``. + +Get Bytes Used +~~~~~~~~~~~~~~ + +You can quickly find out the space used by your account, in bytes. + +.. code:: php + + $accountSizeInBytes = $account->getBytesUsed(); + +[ `Get the executable PHP script for this +example `__ ] + +In the example above, ``$accountSizeInBytes`` will contain the space +used, in bytes, by the account represented by ``$account``. diff --git a/doc/_build/html/_sources/services/object-store/access.txt b/doc/_build/html/_sources/services/object-store/access.txt new file mode 100644 index 000000000..40fe61670 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/access.txt @@ -0,0 +1,93 @@ +Temporary URLs +============== + +Temporary URLs allow you to create time-limited Internet addresses that +allow you to grant access to your Cloud Files account. Using Temporary +URL, you may allow others to retrieve or place objects in your +containers - regardless of whether they're CDN-enabled. + + +Set "temporary URL" metadata key +-------------------------------- + +You must set this "secret" value on your account, where it can be used +in a global state: + +.. code-block:: php + + $account = $service->getAccount(); + $account->setTempUrlSecret('my_secret'); + + echo $account->getTempUrlSecret(); + +The string argument of ``setTempUrlSecret()`` is optional - if left out, +the SDK will generate a random hashed secret for you. + +Get the executable PHP script for this example: + +* `Specify a URL secret `_ +* `Generate random URL secret `_ + + +Create a temporary URL +---------------------- + +Once you've set an account secret, you can create a temporary URL for +your object. To allow GET access to your object for 1 minute: + +.. code-block:: php + + $object->getTemporaryUrl(60, 'GET'); + + +To allow PUT access for 1 hour: + +.. code-block:: php + + $object->getTemporaryUrl(360, 'PUT'); + +`Get the executable PHP script for this example `_ + + +Hosting HTML sites on CDN +========================= + +.. include:: rs-only.rst + +To host a static (i.e. HTML) website on Cloud Files, you must follow +these steps: + +1. CDN-enable a container: + +.. code-block:: php + + $container = $service->getContainer('html_site'); + $container->enableCdn(); + +2. Upload all HTML content. You can use nested directory structures. + +.. code-block:: php + + $container->uploadObjects(array( + array('name' => 'index.html', 'path' => 'index.html'), + array('name' => 'contact.html', 'path' => 'contact.html'), + array('name' => 'error.html', 'path' => 'error.html'), + array('name' => 'styles.css', 'path' => 'styles.css'), + array('name' => 'main.js', 'path' => 'main.js'), + )); + +3. Tell Cloud Files what to use for your default index page like this: + +.. code-block:: php + + $container->setStaticIndexPage('index.html'); + +4. (Optional) Tell Cloud Files which error page to use by default: + +.. code-block:: php + + $container->setStaticErrorPage('error.html'); + + +Bear in mind that steps 3 & 4 do not upload content, but rather specify +a reference to an existing page/CloudFiles object. diff --git a/doc/_build/html/_sources/services/object-store/account.txt b/doc/_build/html/_sources/services/object-store/account.txt new file mode 100644 index 000000000..50c17f1a8 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/account.txt @@ -0,0 +1,52 @@ +Account Details +=============== + +To see how many containers you have in your account +(X-Account-Container-Count), how many objects are in your account +(X-Account-Object-Count), and how many total bytes your account uses +(X-Account-Bytes-Used): + +Setup +----- + +.. code-block:: php + + $account = $service->getAccount(); + + +View all details +---------------- + +.. code-block:: php + + $details = $account->getDetails(); + + +Retrieve total container count +------------------------ + +.. code-block:: php + + $account->getContainerCount(); + +`Get the executable PHP script for this example `_ + + +Retrieve total object count +--------------------- + +.. code-block:: php + + $account->getObjectCount(); + +`Get the executable PHP script for this example `_ + + +Retrieve total bytes used +------------------------- + +.. code-block:: php + + $account->getBytesUsed(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/object-store/cdn.txt b/doc/_build/html/_sources/services/object-store/cdn.txt new file mode 100644 index 000000000..eb29bb27f --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/cdn.txt @@ -0,0 +1,130 @@ +CDN Containers +============== + +.. include:: rs-only.rst + +Setup +----- + +In order to interact with CDN containers, you first need to instantiate a +CDN service object: + +.. code-block:: php + + $cdnService = $service->getCdnService(); + + +List CDN-enabled containers +--------------------------- + +To list CDN-only containers, follow the same operation for Storage which +lists all containers. The only difference is which service object you +execute the method on: + +.. code-block:: php + + $cdnContainers = $cdnService->listContainers(); + + foreach ($cdnContainers as $cdnContainer) { + /** @var $cdnContainer OpenCloud\ObjectStore\Resource\CDNContainer */ + } + +`Get the executable PHP script for this example `_ + + +CDN-enable a container +---------------------- + +Before a container can be CDN-enabled, it must exist in the storage +system. When a container is CDN-enabled, any objects stored in it are +publicly accessible over the Content Delivery Network by combining the +container's CDN URL with the object name. + +Any CDN-accessed objects are cached in the CDN for the specified amount +of time called the TTL. The default TTL value is 259200 seconds, or 72 +hours. Each time the object is accessed after the TTL expires, the CDN +refetches and caches the object for the TTL period. + +.. code-block:: php + + $container->enableCdn(); + +`Get the executable PHP script for this example `_ + + +CDN-disable a container +----------------------- + +.. code-block:: php + + $container->disableCdn(); + +`Get the executable PHP script for this example `_ + + +Operations on CDN-enabled containers +------------------------------------ + +Once a container has been CDN-enabled, you can retrieve it like so: + +.. code-block:: php + + $cdnContainer = $cdnService->cdnContainer('{containerName}'); + + +Retrieve the SSL URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnSslUri(); + + +Retrieve the streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $cdnContainer->getCdnStreamingUri(); + + +Retrieve the iOS streaming URL of a CDN container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Cloud Files CDN allows you to stream video to iOS devices without +needing to convert your video. Once you CDN-enable your container, you +have the tools necessary for streaming media to multiple devices. + +.. code-block:: php + + $cdnContainer->getIosStreamingUri(); + + +CDN logging +~~~~~~~~~~~ + +To enable and disable logging for your CDN-enabled container: + +.. code-block:: php + + $cdnContainer->enableCdnLogging(); + $cdnContainer->disableCdnLogging(); + + +Purge CDN-enabled objects +------------------------- + +To remove a CDN object from public access: + +.. code-block:: php + + $object->purge(); + +You can also provide an optional e-mail address (or comma-delimeted list +of e-mails), which the API will send a confirmation message to once the +object has been completely purged: + +.. code-block:: php + + $object->purge('jamie.hannaford@rackspace.com'); + $object->purge('hello@example.com,hallo@example.com'); diff --git a/doc/_build/html/_sources/services/object-store/containers.txt b/doc/_build/html/_sources/services/object-store/containers.txt new file mode 100644 index 000000000..a2f0485e8 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/containers.txt @@ -0,0 +1,247 @@ +Containers +========== + +Create container +---------------- + +To create a new container, you just need to define its name: + +.. code-block:: php + + $container = $service->createContainer('my_amazing_container'); + +If the response returned is ``FALSE``, there was an API error - most +likely due to the fact you have a naming collision. + +Container names must be valid strings between 0 and 256 characters. +Forward slashes are not currently permitted. + +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in + +`Get the executable PHP script for this example `_ + + +List containers +--------------- + +.. code-block:: php + + $containers = $service->listContainers(); + + foreach ($containers as $container) { + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + printf("Container name: %s\n", $container->name); + printf("Number of objects within container: %d\n", $container->getObjectCount()); + } + +Container names are sorted based on a binary comparison, a single +built-in collating sequence that compares string data using SQLite's +memcmp() function, regardless of text encoding. + +The list is limited to 10,000 containers at a time. To work with larger +collections, please read the next section. + +`Get the executable PHP script for this example `_ + + +Filtering large collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you need more control over collections of containers, you can filter the +results and return back a subset of the total collection by using the ``marker`` +and ``end_marker`` parameters. The former parameter (``marker``) tells +the API where to begin the list, and the latter (``end_marker``) tells +it where to end the list. You may use either of them independently or +together. + +You may also use the ``limit`` parameter to fix the number of +containers returned. + +To list a set of containers between two fixed points: + +.. code-block:: php + + $someContainers = $service->listContainers(array( + 'marker' => 'container_55', + 'end_marker' => 'container_2001' + )); + +Or to return a limited set: + +.. code-block:: php + + $someContainers = $service->listContainers(array('limit' => 560)); + + +Get container +------------- + +To retrieve a certain container: + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $service->getContainer('{containerName}'); + +`Get the executable PHP script for this example `_ + + +Retrieve a container's name +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $name = $container->name; + + +Retrieve a container's object count +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $count = $container->getObjectCount(); + +`Get the executable PHP script for this example `_ + + +Retrieve a container's total bytes used +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $bytes = $container->getBytesUsed(); + +`Get the executable PHP script for this example `_ + + +Delete container +---------------- + +Deleting an empty container is easy: + +.. code-block:: php + + $container->delete(); + + +Please bear mind that you must delete all objects inside a container +before deleting it. This is done for you if you set the +``$deleteObjects`` parameter to ``TRUE`` like so: + +.. code-block:: php + + $container->delete(true); + + +You can also `delete all objects <#deleting-all-objects-inside-a-container>`_ +first, and then call ``delete``. + +`Get the executable PHP script for this example `_ + + +Deleting all objects inside a container +--------------------------------------- + +.. code-block:: php + + $container->deleteAllObjects(); + +`Get the executable PHP script for this example `_ + + +Create or update container metadata +----------------------------------- + +.. code-block:: php + + $container->saveMetadata(array( + 'Author' => 'Virginia Woolf', + 'Published' => '1931' + )); + +Please bear in mind that this action will set metadata to this array - +overriding existing values and wiping those left out. To *append* values +to the current metadata: + +.. code-block:: php + + $metadata = $container->appendToMetadata(array( + 'Publisher' => 'Hogarth' + )); + +`Get the executable PHP script for this example `_ + + +Container quotas +---------------- + +The ``container_quotas`` middleware implements simple quotas that can be +imposed on Cloud Files containers by a user. Setting container quotas +can be useful for limiting the scope of containers that are delegated to +non-admin users, exposed to formpost uploads, or just as a self-imposed +sanity check. + +To set quotas for a container: + +.. code-block:: php + + use OpenCloud\Common\Constants\Size; + + $container->setCountQuota(1000); + $container->setBytesQuota(2.5 * Size::GB); + +And to retrieve them: + +.. code-block:: php + + echo $container->getCountQuota(); + echo $container->getBytesQuota(); + +Get the executable PHP scripts for this example: + +* `Set bytes quota `_ +* `Set count quota `_ + + +Access log delivery +------------------- + +To view your object access, turn on Access Log Delivery. You can use +access logs to analyze the number of people who access your objects, +where they come from, how many requests for each object you receive, and +time-based usage patterns (such as monthly or seasonal usage). + +.. code-block:: php + + $container->enableLogging(); + $container->disableLogging(); + + +Syncing containers +------------------ + +You can synchronize local directories with your CloudFiles/Swift +containers very easily. When you do this, the container will mirror +exactly the nested file structure within your local directory: + +.. code-block:: php + + $container->uploadDirectory('/home/user/my-blog'); + +There are four scenarios you should be aware of: + ++------------------------+-----------------------+----------------------+--------------------------------+ +| Local | Remote | Comparison | Action | ++========================+=======================+======================+================================+ +| File exists | File exists | Identical checksum | No action | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File exists | Different checksum | Local file overwrites remote | ++------------------------+-----------------------+----------------------+--------------------------------+ +| File exists | File does not exist | - | Local file created in Swift | ++------------------------+-----------------------+----------------------+--------------------------------+ +| Files does not exist | File exists | - | Remote file deleted | ++------------------------+-----------------------+----------------------+--------------------------------+ diff --git a/doc/_build/html/_sources/services/object-store/index.txt b/doc/_build/html/_sources/services/object-store/index.txt new file mode 100644 index 000000000..fcdc5aff2 --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/index.txt @@ -0,0 +1,65 @@ +Object Store v1 +=============== + +.. include:: ../common/clients.sample.rst + +Object Store service +~~~~~~~~~~~~~~~~~~~~ + +Now to instantiate the Object Store service: + +.. code-block:: php + + $service = $client->objectStoreService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + account + containers + objects + cdn + migrating-containers + access + +Glossary +-------- + +.. glossary:: + + account + The portion of the system designated for your use. An Object Store system is + typically designed to be used by many different customers, and your user + account is your portion of it. + + container + A storage compartment that provides a way for you to organize data. A + container is similar to a folder in Windows or a directory in UNIX. The + primary difference between a container and these other file system concepts + is that containers cannot be nested. + + cdn + A system of distributed servers (network) that delivers web pages and other + web content to a user based on the geographic locations of the user, the + origin of the web page, and a content delivery server. + + metadata + Optional information that you can assign to Cloud Files accounts, + containers, and objects through the use of a metadata header. + + object + An object (sometimes referred to as a file) is the unit of storage in an + Object Store. An object is a combination of content (data) and metadata. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/object-store/migrating-containers.txt b/doc/_build/html/_sources/services/object-store/migrating-containers.txt new file mode 100644 index 000000000..22adb839b --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/migrating-containers.txt @@ -0,0 +1,111 @@ +Migrating containers across regions +=================================== + +Currently, there exists no single API operation to copy containers +across geographic endpoints. Although the API offers a ``COPY`` +operation for individual files, this does not work for cross-region +copying. The SDK, however, does offer this functionality. + +You **will** be charged for bandwidth between regions, so it's advisable +to use ServiceNet where possible (which is free). + + +Requirements +------------ + +- You must install the full Guzzle package, so that the process can + take advantage of Guzzle's batching functionality (it allows parallel + requests to be batched for greater efficiency). You can do this by + running: + +.. code-block:: bash + + composer require guzzle/guzzle + +- Depending on the size and number of transfer items, you will need to + raise PHP's memory limit: + +.. code-block:: php + + ini_set('memory_limit', '512M'); + +- You will need to enact some kind of backoff/retry strategy for rate + limits. Guzzle comes with a convenient feature that just needs to be + added as a normal subscriber: + +.. code-block:: php + + use Guzzle\Plugin\Backoff\BackoffPlugin; + + // set timeout in secs + $timeout = 10; + + // set HTTP error codes + $httpErrors = array(500, 503, 408); + + $backoffPlugin = BackoffPlugin::getExponentialBackoff($timeout, $httpErrors); + $client->addSubscriber($backoffPlugin); + + +This tells the client to retry up to ``10`` times for failed requests +have resulted in these HTTP status codes: ``500``, ``503`` or ``408``. + + +Setup +----- + +You can access all this functionality by executing: + +.. code-block:: php + + $ordService = $client->objectStoreService('cloudFiles', 'ORD'); + $iadService = $client->objectStoreService('cloudFiles', 'IAD'); + + $oldContainer = $ordService->getContainer('old_container'); + $newContainer = $iadService->getContainer('new_container'); + + $iadService->migrateContainer($oldContainer, $newContainer); + + +It's advisable to do this process in a Cloud Server in one of the two +regions you're migrating to/from. This allows you to use ``privateURL`` +as the third argument in the ``objectStoreService`` methods like this: + +.. code-block:: php + + $client->objectStoreService('cloudFiles', 'IAD', 'privateURL'); + + +This will ensure that traffic between your server and your new IAD +container will be held over the internal Rackspace network which is +free. + + +Options +------- + +You can pass in an array of arguments to the method: + +.. code-block:: php + + $options = array( + 'read.batchLimit' => 100, + 'read.pageLimit' => 100, + 'write.batchLimit' => 50 + ); + + $iadService->migrateContainer($oldContainer, $newContainer, $options); + + +Options explained +~~~~~~~~~~~~~~~~~ + ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| Name | Description | Default | ++========================+===============================================================================================================================================================================================================================================================================================================================================+===========+ +| ``read.pageLimit`` | When the process begins, it has to collect all the files that exist in the old container. It does this through a conventional ``objectList`` method, which calls the ``PaginatedIterator``. This iterator has the option to specify the page size for the collection (i.e. how many items are contained per page in responses from the API) | 10,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``read.batchLimit`` | After the data objects are collected, the process needs to send an individual GET request to ascertain more information. In order to make this process faster, these individual GET requests are batched together and sent in parallel. This limit refers to how many of these GET requests are batched together. | 1,000 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ +| ``write.batchLimit`` | Once each file has been retrieved from the API, a PUT request is executed against the new container. Similar to above, these PUT requests are batched - and this number refers to the amount of PUT requests batched together. | 100 | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+ diff --git a/doc/_build/html/_sources/services/object-store/objects.txt b/doc/_build/html/_sources/services/object-store/objects.txt new file mode 100644 index 000000000..7d9b2120e --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/objects.txt @@ -0,0 +1,449 @@ +Objects +======= + +Setup +----- + +In order to interact with this feature, you must first retrieve a particular +container using its unique name: + +.. code-block:: php + + $container = $service->getContainer('{containerName}'); + + +Create an object +---------------- + +There are three ways to upload a new file, each of which has different +business needs. + +.. note:: + + Unlike previous versions, you do not need to manually specify your object's + content type. The API will do this for you. + +.. note:: + + When working with names that contain non-standard alphanumerical characters + (such as spaces or non-English characters), you must ensure they are encoded + with `urlencode `_ before passing them in. + +Upload a single file (under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The simplest way to upload a local object, without additional metadata, is by +its path: + +.. code-block:: php + + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+')); + + +The resource handle will be automatically closed by Guzzle in its destructor, +so there is no need to execute ``fclose``. + +`Get the executable PHP script for this example `_ + + +Upload a single file (under 5GB) with metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Although the previous section handles most use cases, there are times when you +want greater control over what is being uploaded. For example, you might want +to control the object's metadata, or supply additional HTTP headers to coerce +browsers to handle the download a certain way. To add metadata to a new object: + +.. code-block:: php + + use OpenCloud\ObjectStore\Resource\DataObject; + + // specify optional metadata + $metadata = array( + 'Author' => 'Camera Obscura', + 'Origin' => 'Glasgow', + ); + + // specify optional HTTP headers + $httpHeaders = array( + 'Content-Type' => 'application/json', + ); + + // merge the two + $allHeaders = array_merge(DataObject::stockHeaders($metadata), $httpHeaders); + + // upload as usual + $container->uploadObject('example.txt', fopen('/path/to/file.txt', 'r+'), $allHeaders); + + +As you will notice, the first argument to ``uploadObject`` is the remote object +name, i.e. the name it will be uploaded as. The second argument is either a +file handle resource, or a string representation of object content (a temporary +resource will be created in memory), and the third is an array of additional +headers. + +`Get the executable PHP script for this example `_ + + +Batch upload multiple files (each under 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + $files = array( + array( + 'name' => 'apache.log', + 'path' => '/etc/httpd/logs/error_log' + ), + array( + 'name' => 'mysql.log', + 'body' => fopen('/tmp/mysql.log', 'r+') + ), + array( + 'name' => 'to_do_list.txt', + 'body' => 'PHONE HOME' + ) + ); + + $container->uploadObjects($files); + +As you can see, the ``name`` key is required for every file. You must +also specify *either* a path key (to an existing file), or a ``body``. +The ``body`` can either be a PHP resource or a string representation of +the content you want to upload. + +`Get the executable PHP script for this example `_ + + +Upload large files (over 5GB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For files over 5GB, you will need to use the +``OpenCloud\ObjectStore\Upload\TransferBuilder`` factory to build and execute your +transfer. For your convenience, the Container resource object contains a simple +method to do this heavy lifting for you: + +.. code-block:: php + + $transfer = $container->setupObjectTransfer(array( + 'name' => 'video.mov', + 'path' => '/home/user/video.mov', + 'metadata' => array('Author' => 'Jamie'), + 'concurrency' => 4, + 'partSize' => 1.5 * Size::GB + )); + + $transfer->upload(); + + +You can specify how many concurrent cURL connections are used to upload +parts of your file. The file is fragmented into chunks, each of which is +uploaded individually as a separate file (the filename of each part will +indicate that it's a segment rather than the full file). After all parts +are uploaded, a manifestfile is uploaded. When the end-user accesses the 5GB +by its true filename, it actually references the manifest file which +concatenates each segment into a streaming download. + +In Swift terminology, the name for this process is *Dynamic Large Object (DLO)*. +To find out more details, please consult the `official documentation +`_. + +`Get the executable PHP script for this example `_ + + +List objects in a container +--------------------------- + +To return a list of objects: + +.. code-block:: php + + $files = $container->objectList(); + + foreach ($files as $file) { + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + } + +By default, 10,000 objects are returned as a maximum. To get around +this, you can construct a query which refines your result set. For a +full specification of query parameters relating to collection filtering, +see the `official +docs `__. + +.. code-block:: php + + $container->objectList(array('prefix' => 'logFile_')); + +`Get the executable PHP script for this example `_ + + +Get object +---------- + +To retrieve a specific file from Cloud Files: + +.. code-block:: php + + /** @var $file OpenCloud\ObjectStore\Resource\DataObject */ + $file = $container->getObject('summer_vacation.mp4'); + +Once you have access to this ``OpenCloud\ObjectStore\Resource\DataObject`` +object, you can access these attributes: + +Get object's parent container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $container OpenCloud\ObjectStore\Resource\Container */ + $container = $object->getContainer(); + + +Get file name +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $name string */ + $name = $object->getName(); + + +Get file size +~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $size int */ + $size = $object->getContentLength(); + + +Get content of file +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $content Guzzle\Http\EntityBody */ + $content = $object->getContainer(); + + +Get type of file +~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $type string */ + $type = $object->getContentType(); + + +Get file checksum +~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $etag string */ + $etag = $object->getEtag(); + + +Get last modified date of file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + /** @param $lastModified string */ + $lastModified = $object->getLastModified(); + + +Conditional requests +~~~~~~~~~~~~~~~~~~~~ + +You can also perform conditional requests according to `RFC 2616 +specification `__ (§§ 14.24-26). +Supported headers are ``If-Match``, ``If-None-Match``, +``If-Modified-Since`` and ``If-Unmodified-Since``. + +So, to retrieve a file's contents only if it's been recently changed + +.. code-block:: php + + $file = $container->getObject('error_log.txt', array( + 'If-Modified-Since' => 'Tue, 15 Nov 1994 08:12:31 GMT' + )); + + if ($file->getContentLength()) { + echo 'Has been changed since the above date'; + } else { + echo 'Has not been changed'; + } + +Retrieve a file only if it has NOT been modified (and expect a 412 on +failure): + +.. code-block:: php + + use Guzzle\Http\Exception\ClientErrorResponseException; + + try { + $oldImmutableFile = $container->getObject('payroll_2001.xlsx', array( + 'If-Unmodified-Since' => 'Mon, 31 Dec 2001 23:00:00 GMT' + )); + } catch (ClientErrorResponseException $e) { + echo 'This file has been modified...'; + } + +Finally, you can specify a range - which will return a subset of bytes +from the file specified. To return the last 20B of a file: + +.. code-block:: php + + $snippet = $container->getObject('output.log', array('range' => 'bytes=-20')); + + +Update an existing object +------------------------- + +.. code-block:: php + + $file->setContent(fopen('/path/to/new/content', 'r+')); + $file->update(); + +Bear in mind that updating a file name will result in a new file being +generated (under the new name). You will need to delete the old file. + + +Copy object to new location +--------------------------- + +To copy a file to another location, you need to specify a string-based +destination path: + +.. code-block:: php + + $object->copy('/container_2/new_object_name'); + +Where ``container_2`` is the name of the container, and ``new_object_name`` is +the name of the object inside the container that does not exist yet. + +`Get the executable PHP script for this example `_ + + +Get object metadata +------------------- + +You can fetch just the object metadata without fetching the full +content: + +.. code-block:: php + + $container->getPartialObject('summer_vacation.mp4'); + + +In order to access the metadata on a partial or complete object, use: + +.. code-block:: php + + $object->getMetadata(); + + +You can turn a partial object into a full object to get the content +after looking at the metadata: + +.. code-block:: php + + $object->refresh(); + + +You can also update to get the latest metadata: + +.. code-block:: php + + $object->retrieveMetadata(); + +`Get the executable PHP script for this example `_ + + +Update object metadata +---------------------- + +Similarly, with setting metadata there are two options: you can update +the metadata values of the local object (i.e. no HTTP request) if you +anticipate you'll be executing one soon (an update operation for +example): + +.. code-block:: php + + // There's no need to execute a HTTP request, because we'll soon do one anyway for the update operation + $object->setMetadata(array( + 'Author' => 'Hemingway' + )); + + // ... code here + + $object->update(); + +Alternatively, you can update the API straight away - so that everything +is retained: + +.. code-block:: php + + $object->saveMetadata(array( + 'Author' => 'Hemingway' + )); + +Please be aware that these methods override and wipe existing values. If +you want to append values to your metadata, use the correct method: + +.. code-block:: php + + $metadata = $object->appendToMetadata(array( + 'Author' => 'Hemingway' + )); + + $object->saveMetadata($metadata); + +`Get the executable PHP script for this example `_ + + +Extract archive +--------------- + +CloudFiles provides you the ability to extract uploaded archives to +particular destinations. The archive will be extracted and its contents +will populate the particular area specified. To upload file (which might +represent a directory structure) into a particular container: + +.. code-block:: php + + use OpenCloud\ObjectStore\Constants\UrlType; + + $service->bulkExtract('container_1', fopen('/home/jamie/files.tar.gz','r'), UrlType::TAR_GZ); + +You can also omit the container name (i.e. provide an empty string as +the first argument). If you do this, the API will create the containers +necessary to house the extracted files - this is done based on the +filenames inside the archive. + +`Get the executable PHP script for this example `_ + + +Delete object +------------- + +.. code-block:: php + + $object->delete(); + +`Get the executable PHP script for this example `_ + + +Delete multiple objects +----------------------- + +Bulk delete a set of paths: + +.. code-block:: php + + $pathsToBeDeleted = array('/container_1/old_file', '/container_2/notes.txt', '/container_1/older_file.log'); + + $service->bulkDelete($pathsToBeDeleted); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/object-store/rs-only.txt b/doc/_build/html/_sources/services/object-store/rs-only.txt new file mode 100644 index 000000000..fab4e423f --- /dev/null +++ b/doc/_build/html/_sources/services/object-store/rs-only.txt @@ -0,0 +1,3 @@ +.. note:: + + This feature is only available to Rackspace users. diff --git a/doc/_build/html/_sources/services/orchestration/README.md.txt b/doc/_build/html/_sources/services/orchestration/README.md.txt new file mode 100644 index 000000000..1a983d187 --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/README.md.txt @@ -0,0 +1,98 @@ +Orchestration +============= + +**Orchestration** is a service that can be used to create and manage +cloud resources. Examples of such resources are databases, load +balancers, servers and software installed on them. + +Concepts +-------- + +To use the Orchestration service effectively, you should understand +several key concepts: + +- **Template**: An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + +- **Resource**: A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + +- **Stack**: A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. + +Getting started +--------------- + +1. Instantiate an OpenStack or Rackspace client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +2. Obtain an Orchestration service object from the client. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object as shown in the following example: + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +3. Create a stack from a template. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +Next steps +---------- + +Once you have created a stack, there is more you can do with it. See +`complete user guide for orchestration `__. diff --git a/doc/_build/html/_sources/services/orchestration/USERGUIDE.md.txt b/doc/_build/html/_sources/services/orchestration/USERGUIDE.md.txt new file mode 100644 index 000000000..98bd51db7 --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/USERGUIDE.md.txt @@ -0,0 +1,672 @@ +Complete User Guide for the Orchestration Service +================================================= + +Orchestration is a service that you can use to create and manage cloud +resources such as databases, load balancers, and servers, and the +software installed on servers. + +Table of Contents +----------------- + +- `Concepts <#concepts>`__ +- `Prerequisites <#prerequisites>`__ +- `Client <#client>`__ +- `Orchestration service <#orchestration-service>`__ +- `Templates <#templates>`__ +- `Validate template <#validate-template>`__ + + - `Validate a template from a + file <#validate-a-template-from-a-file>`__ + - `Validate Template from URL <#validate-template-from-url>`__ + +- `Stacks <#stacks>`__ +- `Preview stack <#preview-stack>`__ + + - `Preview a stack from a template + file <#preview-a-stack-from-a-template-file>`__ + - `Preview a stack from a template + URL <#preview-a-stack-from-a-template-url>`__ + +- `Create stack <#create-stack>`__ + + - `Create a stack from a template + file <#create-a-stack-from-a-template-file>`__ + - `Create a stack from a template + URL <#create-a-stack-from-a-template-url>`__ + +- `List stacks <#list-stacks>`__ +- `Get stack <#get-stack>`__ +- `Get stack template <#get-stack-template>`__ +- `Update stack <#update-stack>`__ + + - `Update a stack from a template + file <#update-a-stack-from-a-template-file>`__ + - `Update Stack from Template + URL <#update-stack-from-template-url>`__ + +- `Delete stack <#delete-stack>`__ +- `Abandon Stack <#abandon-stack>`__ +- `Adopt stack <#adopt-stack>`__ +- `Stack resources <#stack-resources>`__ +- `List stack resources <#list-stack-resources>`__ +- `Get stack resource <#get-stack-resource>`__ +- `Get stack resource metadata <#get-stack-resource-metadata>`__ +- `Stack resource events <#stack-resource-events>`__ +- `List stack events <#list-stack-events>`__ +- `List stack resource events <#list-stack-resource-events>`__ +- `Get stack resource event <#get-stack-resource-event>`__ +- `Resource types <#resource-types>`__ +- `List resource types <#list-resource-types>`__ +- `Get resource type <#get-resource-type>`__ +- `Get resource type template <#get-resource-type-template>`__ +- `Build info <#build-info>`__ +- `Get build info <#get-build-info>`__ + +Concepts +-------- + +To use the Orchestration service effectively, you should understand the +following key concepts: + +- **Template**: A JSON or YAML document that describes how a set of + resources should be assembled to produce a working deployment. The + template specifies the resources to use, the attributes of these + resources that are parameterized and the information that is sent to + the user when a template is instantiated. + +- **Resource**: Some component of your architecture (a cloud server, a + group of scaled cloud servers, a load balancer, some configuration + management system, and so on) that is defined in a template. + +- **Stack**: A running instance of a template. When a stack is created, + the resources specified in the template are created. + +Prerequisites +------------- + +Client +~~~~~~ + +To use the Orchestration service, you must first instantiate a +``OpenStack`` or ``Rackspace`` client object. + +- If you are working with an OpenStack cloud, instantiate an + ``OpenCloud\OpenStack`` client as follows: + + .. code:: php + + use OpenCloud\OpenStack; + + $client = new OpenStack('', array( + 'username' => '', + 'password' => '' + )); + +- If you are working with the Rackspace cloud, instantiate a + ``OpenCloud\Rackspace`` client as follows: + + .. code:: php + + use OpenCloud\Rackspace; + + $client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array( + 'username' => '', + 'apiKey' => '' + )); + +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ + +All Orchestration operations are done via an *orchestration service +object*. To instantiate this object, call the ``orchestrationService`` +method on the ``$client`` object. This method takes two arguments: + ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| Position | Description | Data type | Required? | Default value | Example value | ++============+=============================================================+=============+=============+====================================================+==========================+ +| 1 | Name of the service, as it appears in the service catalog | String | No | ``null``; automatically determined when possible | ``cloudOrchestration`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ +| 2 | Cloud region | String | Yes | - | ``DFW`` | ++------------+-------------------------------------------------------------+-------------+-------------+----------------------------------------------------+--------------------------+ + +.. code:: php + + $region = ''; + $orchestrationService = $client->orchestrationService(null, $region); + +Any stacks and resources created with this ``$orchestrationService`` +instance will be stored in the cloud region specified by ``$region``. + +Templates +--------- + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validate template +~~~~~~~~~~~~~~~~~ + +Before you use a template to create a stack, you might want to validate +it. + +Validate a template from a file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Validate Template from URL +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +[ `Get the executable PHP script for this +example `__ +] + +Stacks +------ + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + +Preview stack +~~~~~~~~~~~~~ + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Preview a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create stack +~~~~~~~~~~~~ + +You can create a stack from a template. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code:: php + + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Create a stack from a template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +[ `Get the executable PHP script for this +example `__ ] + +List stacks +~~~~~~~~~~~ + +You can list all the stacks that you have created as shown in the +following example: + +.. code:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack +~~~~~~~~~ + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code:: php + + $stack = $orchestrationService->getStack('simple-lamp-setup'); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack template +~~~~~~~~~~~~~~~~~~ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code:: php + + $stackTemplate = $stack->getTemplate(); + /** @var $stackTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Update stack +~~~~~~~~~~~~ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + +Update a stack from a template file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code:: php + + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ +] + +Update Stack from Template URL +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code:: php + + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Delete stack +~~~~~~~~~~~~ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code:: php + + $stack->delete(); + +[ `Get the executable PHP script for this +example `__ ] + +Abandon Stack +~~~~~~~~~~~~~ + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code:: php + + $abandonStackData = $stack->abandon(); + /** @var $abandonStackData string **/ + + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +[ `Get the executable PHP script for this +example `__ ] + +Note that this operation returns data about the abandoned stack as a +string. You can use this data to recreate the stack by using the `adopt +stack <#adopt-stack>`__ operation. + +Adopt stack +~~~~~~~~~~~ + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code:: php + + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resources +--------------- + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + +List stack resources +~~~~~~~~~~~~~~~~~~~~ + +You can list all the resources for a stack as shown in the following +example: + +.. code:: php + + $resources = $stack->listResources(); + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource +~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code:: php + + $resource = $stack->getResource('load-balancer'); + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource metadata +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceMetadata = $resource->getMetadata(); + /** @var $resourceMetadata \stdClass **/ + +[ `Get the executable PHP script for this +example `__ ] + +Stack resource events +--------------------- + +Operations on resources within a stack (such as the creation of a +resource) produce events. + +List stack events +~~~~~~~~~~~~~~~~~ + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code:: php + + $stackEvents = $stack->listEvents(); + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +List stack resource events +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code:: php + + $resourceEvents = $resource->listEvents(); + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get stack resource event +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code:: php + + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + +[ `Get the executable PHP script for this +example `__ ] + +Resource types +-------------- + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +~~~~~~~~~~~~~~~~~~~ + +You can list all supported resource types as shown in the following +example: + +.. code:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type +~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code:: php + + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + +[ `Get the executable PHP script for this +example `__ ] + +Get resource type template +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code:: php + + $resourceTypeTemplate = $resourceType->getTemplate(); + /** @var $resourceTypeTemplate string **/ + +[ `Get the executable PHP script for this +example `__ ] + +Build info +---------- + +Get build info +~~~~~~~~~~~~~~ + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code:: php + + $buildInfo = $orchestrationService->getBuildInfo(); + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + +[ `Get the executable PHP script for this +example `__ ] diff --git a/doc/_build/html/_sources/services/orchestration/build-info.txt b/doc/_build/html/_sources/services/orchestration/build-info.txt new file mode 100644 index 000000000..b4016bab2 --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/build-info.txt @@ -0,0 +1,15 @@ +Build info +========== + +Get build info +-------------- + +You can retrieve information about the current Orchestration service +build as shown in the following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\BuildInfo **/ + $buildInfo = $orchestrationService->getBuildInfo(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/orchestration/events.txt b/doc/_build/html/_sources/services/orchestration/events.txt new file mode 100644 index 000000000..bceec0f5a --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/events.txt @@ -0,0 +1,53 @@ +Stack resource events +===================== + +Operations on resources within a stack (such as the creation of a +resource) produce events. + + +List stack events +----------------- + +You can list all of the events for all of the resources in a stack as +shown in the following example: + +.. code-block:: php + + $stackEvents = $stack->listEvents(); + + foreach ($stackEvents as $stackEvent) { + /** @var $stackEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +List stack resource events +-------------------------- + +You can list all of the events for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + $resourceEvents = $resource->listEvents(); + + foreach ($resourceEvents as $resourceEvent) { + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource event +------------------------ + +You can retrieve a specific event for a specific resource in a stack, by +using the resource event's ID, as shown in the following example: + +.. code-block:: php + + /** @var $resourceEvent OpenCloud\Orchestration\Resource\Event **/ + $resourceEvent = $resource->getEvent('c1342a0a-59e6-4413-9af5-07c9cae7d729'); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/orchestration/index.txt b/doc/_build/html/_sources/services/orchestration/index.txt new file mode 100644 index 000000000..bae5ec6aa --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/index.txt @@ -0,0 +1,59 @@ +Orchestration v1 +================ + +.. include:: ../common/clients.sample.rst + +Orchestration service +~~~~~~~~~~~~~~~~~~~~~ + +Now to instantiate the Orchestration service: + +.. code-block:: php + + $service = $client->orchestrationService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + templates + stacks + resources + resource-types + build-info + events + + +Glossary +-------- + +.. glossary:: + + template + An Orchestration template is a JSON or YAML document + that describes how a set of resources should be assembled to produce + a working deployment. The template specifies what resources should be + used, what attributes of these resources are parameterized and what + information is output to the user when a template is instantiated. + + resource + A resource is a template artifact that represents some + component of your desired architecture (a Cloud Server, a group of + scaled Cloud Servers, a load balancer, some configuration management + system, and so forth). + + stack + A stack is a running instance of a template. When a stack + is created, the resources specified in the template are created. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/orchestration/resource-types.txt b/doc/_build/html/_sources/services/orchestration/resource-types.txt new file mode 100644 index 000000000..caa58b013 --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/resource-types.txt @@ -0,0 +1,48 @@ +Resource types +============== + +When you define a template, you must use resource types supported by +your cloud. + +List resource types +------------------- + +You can list all supported resource types as shown in the following +example: + +.. code-block:: php + + $resourceTypes = $orchestrationService->listResourceTypes(); + foreach ($resourceTypes as $resourceType) { + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + } + +`Get the executable PHP script for this example `_ + + +Get resource type +----------------- + +You can retrieve a specific resource type's schema as shown in the +following example: + +.. code-block:: php + + /** @var $resourceType OpenCloud\Orchestration\Resource\ResourceType **/ + $resourceType = $orchestrationService->getResourceType('OS::Nova::Server'); + +`Get the executable PHP script for this example `_ + + +Get resource type template +-------------------------- + +You can retrieve a specific resource type's representation as it would +appear in a template, as shown in the following example: + +.. code-block:: php + + /** @var $resourceTypeTemplate string **/ + $resourceTypeTemplate = $resourceType->getTemplate(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/orchestration/resources.txt b/doc/_build/html/_sources/services/orchestration/resources.txt new file mode 100644 index 000000000..b04115122 --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/resources.txt @@ -0,0 +1,50 @@ +Stack resources +=============== + +A stack is made up of zero or more resources such as databases, load +balancers, and servers, and the software installed on servers. + + +List stack resources +-------------------- + +You can list all the resources for a stack as shown in the following +example: + +.. code-block:: php + + $resources = $stack->listResources(); + + foreach ($resources as $resource) { + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack resource +------------------ + +You can retrieve a specific resource in a stack bt using that resource's +name, as shown in the following example: + +.. code-block:: php + + /** @var $resource OpenCloud\Orchestration\Resource\Resource **/ + $resource = $stack->getResource('load-balancer'); + +`Get the executable PHP script for this example `_ + + +Get stack resource metadata +--------------------------- + +You can retrieve the metadata for a specific resource in a stack as +shown in the following example: + +.. code-block:: php + + /** @var $resourceMetadata \stdClass **/ + $resourceMetadata = $resource->getMetadata(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/orchestration/stacks.txt b/doc/_build/html/_sources/services/orchestration/stacks.txt new file mode 100644 index 000000000..29b6c4f7f --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/stacks.txt @@ -0,0 +1,299 @@ +Stacks +====== + +A stack is a running instance of a template. When a stack is created, +the `resources <#stack-resources>`__ specified in the template are +created. + + +Preview stack +------------- + +Before you create a stack from a template, you might want to see what +that stack will look like. This is called *previewing the stack*. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+=====================================================================================================================================================================================================================+=========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters | Yes | - | ``simple-lamp-setup`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of the template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters. For example, see the parameters in `this template section `__ | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Preview a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to preview a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Preview a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to preview a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->previewStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ) + )); + +`Get the executable PHP script for this example `_ + + +Create stack +------------ + +You can create a stack from a template. This operation takes one parameter, an +associative array, with the following keys: + ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+====================================================================+==========================================================================================================================+=======================================+=================+=================================================================================================+ +| ``name`` | Name of the stack | String. Must start with an alphabetic character, and must contain only alphanumeric, ``_``, ``-`` or ``.`` characters. | Yes | - | ``simple-lamp-setup`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('server_hostname' => 'web01')`` | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack creation should time out | Integer | Yes | - | 5 | ++-------------------+--------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+---------------------------------------+-----------------+-------------------------------------------------------------------------------------------------+ + +Create a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to create a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->createStack(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Create a stack from a template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to create a stack as shown +in the following example: + +.. code-block:: php + + $stack = $orchestrationService->stack(); + $stack->create(array( + 'name' => 'simple-lamp-setup', + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + +List stacks +----------- + +You can list all the stacks that you have created as shown in the +following example: + +.. code-block:: php + + $stacks = $orchestrationService->listStacks(); + foreach ($stacks as $stack) { + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + } + +`Get the executable PHP script for this example `_ + + +Get stack +--------- + +You can retrieve a specific stack using its name, as shown in the +following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->getStack('simple-lamp-setup'); + +`Get the executable PHP script for this example `_ + + +Get stack template +------------------ + +You can retrieve the template used to create a stack. Note that a JSON +string is returned, regardless of whether a JSON or YAML template was +used to create the stack. + +.. code-block:: php + + /** @var $stackTemplate string **/ + $stackTemplate = $stack->getTemplate(); + +`Get the executable PHP script for this example `_ + + +Update stack +------------ + +You can update a running stack. + +This operation takes one parameter, an associative array, with the +following keys: + ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| Name | Description | Data type | Required? | Default value | Example value | ++===================+==================================================================+=============================+=======================================+=================+=========================================================================================================+ +| ``template`` | Template contents | String. JSON or YAML | No, if ``templateUrl`` is specified | ``null`` | ``heat_template_version: 2013-05-23\ndescription: LAMP server\n`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``templateUrl`` | URL of template file | String. HTTP or HTTPS URL | No, if ``template`` is specified | ``null`` | ``https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``parameters`` | Arguments to the template, based on the template's parameters | Associative array | No | ``null`` | ``array('flavor_id' => 'general1-1')`` | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ +| ``timeoutMins`` | Duration, in minutes, after which stack update should time out | Integer | Yes | - | 5 | ++-------------------+------------------------------------------------------------------+-----------------------------+---------------------------------------+-----------------+---------------------------------------------------------------------------------------------------------+ + + +Update a stack from a template file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can use it to update a stack as shown in the following +example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'template' => file_get_contents(__DIR__ . '/lamp-updated.yml'), + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Update Stack from Template URL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can use it to update a stack as shown +in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack->update(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp-updated.yaml', + 'parameters' => array( + 'server_hostname' => 'web01', + 'image' => 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)' + ), + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ + + +Delete stack +------------ + +If you no longer need a stack and all its resources, you can delete the +stack *and* the resources as shown in the following example: + +.. code-block:: php + + $stack->delete(); + +`Get the executable PHP script for this example `_ + + +Abandon Stack +------------- + +.. note:: + + This operation returns data about the abandoned stack as a string. You can + use this data to recreate the stack by using the `adopt stack <#adopt-stack>`_ + operation. + +If you want to delete a stack but preserve all its resources, you can +abandon the stack as shown in the following example: + +.. code-block:: php + + /** @var $abandonStackData string **/ + $abandonStackData = $stack->abandon(); + file_put_contents(__DIR__ . '/sample_adopt_stack_data.json', $abandonStackData); + +`Get the executable PHP script for this example `_ + + +Adopt stack +----------- + +If you have data from an abandoned stack, you can re-create the stack as +shown in the following example: + +.. code-block:: php + + /** @var $stack OpenCloud\Orchestration\Resource\Stack **/ + $stack = $orchestrationService->adoptStack(array( + 'name' => 'simple-lamp-setup', + 'template' => file_get_contents(__DIR__ . '/lamp.yml'), + 'adoptStackData' => $abandonStackData, + 'timeoutMins' => 5 + )); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/orchestration/templates.txt b/doc/_build/html/_sources/services/orchestration/templates.txt new file mode 100644 index 000000000..24bc896bc --- /dev/null +++ b/doc/_build/html/_sources/services/orchestration/templates.txt @@ -0,0 +1,55 @@ +Templates +========= + +An Orchestration template is a JSON or YAML document that describes how +a set of resources should be assembled to produce a working deployment +(known as a `stack <#stacks>`__). The template specifies the resources +to use, the attributes of these resources that are parameterized and the +information that is sent to the user when a template is instantiated. + +Validating templates +-------------------- + +Before you use a template to create a stack, you might want to validate it. + + +Validate a template from a file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored on your local computer as a JSON or YAML +file, you can validate it as shown in the following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'template' => file_get_contents(__DIR__ . '/lamp.yaml') + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ + +Validate Template from URL +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your template is stored as a JSON or YAML file in a remote location +accessible via HTTP or HTTPS, you can validate it as shown in the +following example: + +.. code-block:: php + + use OpenCloud\Common\Exceptions\InvalidTemplateError; + + try { + $orchestrationService->validateTemplate(array( + 'templateUrl' => 'https://raw.githubusercontent.com/rackspace-orchestration-templates/lamp/master/lamp.yaml' + )); + } catch (InvalidTemplateError $e) { + // Use $e->getMessage() for explanation of why template is invalid + } + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/queues/Claim.md.txt b/doc/_build/html/_sources/services/queues/Claim.md.txt new file mode 100644 index 000000000..42161536d --- /dev/null +++ b/doc/_build/html/_sources/services/queues/Claim.md.txt @@ -0,0 +1,164 @@ +1. Introduction +--------------- + +A **Claim** is the process of a worker checking out a message to perform +a task. Claiming a message prevents other workers from attempting to +process the same messages. + +2. Setup +-------- + +A Claim is initialized on its parent object, a Queue: + +.. code:: php + + // To initialize an empty object: + $claim = $queue->getClaim(); + + // or retrieve a specific claim: + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +3. Claim messages +----------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +``204 No Content`` message. + +When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it. + +The age given for a claim is relative to the server's clock. The claim's +age is useful for determining how quickly messages are getting processed +and whether a given message's claim is about to expire. + +When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message. + +3.2 Attributes +~~~~~~~~~~~~~~ + +The ``ttl`` attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request. + +The ``grace`` attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted. + +The ``limit`` attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional. + +3.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); + +4. Query claim +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation queries the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +4.2 Attributes +~~~~~~~~~~~~~~ + +Claim ID. + +4.3 Code +~~~~~~~~ + +.. code:: php + + $claim = $queue->getClaim('51db7067821e727dc24df754'); + +5. Update claim +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific **Claim** and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL. + +5.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +5.3 Code +~~~~~~~~ + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); + +6. Release claim +---------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored. + +This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers. + +6.2 Attributes +~~~~~~~~~~~~~~ + +See section 4.2. + +6.3 Code +~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/_build/html/_sources/services/queues/Message.md.txt b/doc/_build/html/_sources/services/queues/Message.md.txt new file mode 100644 index 000000000..ca67e22bb --- /dev/null +++ b/doc/_build/html/_sources/services/queues/Message.md.txt @@ -0,0 +1,257 @@ +1. Introduction +--------------- + +A **Message** is a task, a notification, or any meaningful data that a +producer or publisher sends to the queue. A message exists until it is +deleted by a recipient or automatically by the system based on a TTL +(time-to-live) value. + +2. Setup +-------- + +A message is initialized from its parent object, a Queue: + +.. code:: php + + // Setup an empty object + $message = $queue->getMessage(); + + // or retrieve an existing one + $message = $queue->getMessage(''); + +3. Post message +--------------- + +3.1 Description +~~~~~~~~~~~~~~~ + +This operation posts the specified message or messages. You can submit +up to 10 messages in a single request. + +When posting new messages, you specify only the ``body`` and ``ttl`` for +the message. The API will insert metadata, such as ID and age. + +3.2 Parameters +~~~~~~~~~~~~~~ + +How you pass through the array structure depends on whether you are +executing multiple (3.3.2) or single (3.3.3) posts, but the keys are the +same. + +The ``body`` attribute specifies an arbitrary document that constitutes +the body of the message being sent. The size of this body is limited to +256 KB, excluding whitespace. + +The ``ttl`` attribute specifies how long the server waits before marking +the message as expired and removing it from the queue. The value of ttl +must be between 60 and 1209600 seconds (14 days). Note that the server +might not actually delete the message until its age has reached up to +(ttl + 60) seconds, to allow for flexibility in storage implementations. + +3.3 Code samples +~~~~~~~~~~~~~~~~ + +3.3.1 Posting a single message +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + use OpenCloud\Common\Constants\Datetime; + + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013 + ), + 'ttl' => 2 * Datetime::DAY + )); + +3.3.2 Post a batch of messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Please note that the list of messages will be truncated at 10. For more, +please execute another method call. + +.. code:: php + + use OpenCloud\Common\Constants\Datetime; + + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); + + $queue->createMessages($messages); + +4. Get messages +--------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the message or messages in the specified queue. + +Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations. + +Results are ordered by age, oldest message first. + +4.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++====================+=========+============+============================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ +| marker | Query | String | Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| include\_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +5. Get a set of messages by ID +------------------------------ + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A hash of options. + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|ids\|Query\|String\|Specifies the IDs of the messages to get. Format +multiple message ID values by separating them with commas +(comma-separated). Optional.\| \|claim\_id\|Query\|​String\|Specifies +the claim ID with which the message is associated. Optional.\| +\|----\|-----\|----\|-----------\| + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $messages = $queue->listMessages(array('ids' => $ids)); + + while ($message = $messages->next()) { + echo $message->getId() . PHP_EOL; + } + +6. Delete a set of messages by ID +--------------------------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted. + +6.2 Parameters +~~~~~~~~~~~~~~ + +An array of IDs. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $ids = array('51db6f78c508f17ddc924357', 'f5b8c8a7c62b0150b68a50d6'); + + $response = $queue->deleteMessages($ids); + +7. Get a specific message +------------------------- + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation gets the specified message from the specified queue. + +7.2 Parameters +~~~~~~~~~~~~~~ + +Message ID. + +7.3 Object properties +~~~~~~~~~~~~~~~~~~~~~ + +``href`` is an opaque relative URI that the client can use to uniquely +identify a message resource and interact with it. + +``ttl`` is the TTL that was set on the message when it was posted. The +message expires after (ttl - age) seconds. + +``age`` is the number of seconds relative to the server's clock. + +``body`` is the arbitrary document that was submitted with the original +request to post the message. + +7.4 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message = $queue->getMessage('51db6f78c508f17ddc924357'); + +8. Delete message +----------------- + +8.1 Description +~~~~~~~~~~~~~~~ + +This operation immediately deletes the specified message. + +8.2 Parameters +~~~~~~~~~~~~~~ + +None. + +8.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $message->delete(); + diff --git a/doc/_build/html/_sources/services/queues/Queue.md.txt b/doc/_build/html/_sources/services/queues/Queue.md.txt new file mode 100644 index 000000000..1fe9f7036 --- /dev/null +++ b/doc/_build/html/_sources/services/queues/Queue.md.txt @@ -0,0 +1,197 @@ +1. Introduction +--------------- + +A Queue is an entity that holds messages. Ideally, a queue is created +per work type. For example, if you want to compress files, you would +create a queue dedicated to this job. Any application that reads from +this queue would only compress files. + +2. Setup +-------- + +.. code:: php + + $service = $client->queuesService('cloudQueues', 'ORD'); + +3. Client IDs +------------- + +With most of Marconi's operation, you must specify a **Client ID** which +will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + +4. List queues +-------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation lists queues for the project. The queues are sorted +alphabetically by name. + +4.2 Parameters +~~~~~~~~~~~~~~ + +\|Name\|Style\|Type\|Description\| \|----\|-----\|----\|-----------\| +\|marker\|Query\|​String\|Specifies the name of the last queue received +in a previous request, or none to get the first page of results. +Optional.\| \|limit\|Query\|Integer\|Specifies the number of queues to +return. The default value for the number of queues returned is 10. If +you do not specify this parameter, the default number of queues is +returned. Optional.\| \|detailed\|Query\|​Boolean\|Determines whether +queue metadata is included in the response. The default value for this +parameter is false, which excludes the metadata. Optional.\| +\|----\|-----\|----\|-----------\| + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queues = $service->listQueues(); + + while ($queue = $queues->next()) { + echo $queue->getName() . PHP_EOL; + } + +5. Create queue +--------------- + +5.1 Description +~~~~~~~~~~~~~~~ + +This operation creates a new queue. + +5.2 Parameters +~~~~~~~~~~~~~~ + +A string representation of the name for your new Queue. The name must +not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +5.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->createQueue('new_queue'); + +6. Retrieve queue +----------------- + +6.1 Description +~~~~~~~~~~~~~~~ + +Returns a ``Queue`` object for use. + +6.2 Parameters +~~~~~~~~~~~~~~ + +Queue name. + +6.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue = $service->getQueue('new_queue'); + +7. Check queue existence +------------------------ + +7.1 Description +~~~~~~~~~~~~~~~ + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +7.2 Parameters +~~~~~~~~~~~~~~ + +7.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + +8. Update queue metadata (permanently to the API) +------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +4.2 Parameters +~~~~~~~~~~~~~~ + +Hash of key pairs. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + +9. Retrieve the queue metadata (fresh from the API) +--------------------------------------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns metadata, such as message TTL, for the queue. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $metadata = $queue->retrieveMetadata(); + + print_r($metadata->toArray()); + +10. Get queue stats +------------------- + +4.1 Description +~~~~~~~~~~~~~~~ + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +4.2 Parameters +~~~~~~~~~~~~~~ + +None. + +4.3 Code sample +~~~~~~~~~~~~~~~ + +.. code:: php + + $queue->getStats(); + diff --git a/doc/_build/html/_sources/services/queues/claims.txt b/doc/_build/html/_sources/services/queues/claims.txt new file mode 100644 index 000000000..1111cb9eb --- /dev/null +++ b/doc/_build/html/_sources/services/queues/claims.txt @@ -0,0 +1,121 @@ +Claims +====== + +Setup +----- + +In order to work with messages, you must first retrieve a queue by its name: + +.. code-block:: php + + $queue = $service->getQueue('{queueName}'); + + +Claim messages +-------------- + +This operation claims a set of messages (up to the value of the limit +parameter) from oldest to newest and skips any messages that are already +claimed. If no unclaimed messages are available, the API returns a +``204 No Content`` message. + +When a client (worker) finishes processing a message, it should delete +the message before the claim expires to ensure that the message is +processed only once. As part of the delete operation, workers should +specify the claim ID (which is best done by simply using the provided +href). If workers perform these actions, then if a claim simply expires, +the server can return an error and notify the worker of the race +condition. This action gives the worker a chance to roll back its own +processing of the given message because another worker can claim the +message and process it. + +The age given for a claim is relative to the server's clock. The claim's +age is useful for determining how quickly messages are getting processed +and whether a given message's claim is about to expire. + +When a claim expires, it is released. If the original worker failed to +process the message, another client worker can then claim the message. + +Parameters +~~~~~~~~~~ + +The ``ttl`` attribute specifies how long the server waits before +releasing the claim. The ttl value must be between 60 and 43200 seconds +(12 hours). You must include a value for this attribute in your request. + +The ``grace`` attribute specifies the message grace period in seconds. +The value of grace value must be between 60 and 43200 seconds (12 +hours). You must include a value for this attribute in your request. To +deal with workers that have stopped responding (for up to 1209600 +seconds or 14 days, including claim lifetime), the server extends the +lifetime of claimed messages to be at least as long as the lifetime of +the claim itself, plus the specified grace period. If a claimed message +would normally live longer than the grace period, its expiration is not +adjusted. + +The ``limit`` attribute specifies the number of messages to return, up +to 20 messages. If limit is not specified, limit defaults to 10. The +limit parameter is optional. + +.. code-block:: php + + use OpenCloud\Common\Constants\Datetime; + + $queue->claimMessages(array( + 'limit' => 15, + 'grace' => 5 * Datetime::MINUTE, + 'ttl' => 5 * Datetime::MINUTE + )); + +`Get the executable PHP script for this example `_ + + +Query claim +----------- + +This operation queries the specified claim for the specified queue. Claims with +malformed IDs or claims that are not found by ID are ignored. + +.. code-block:: php + + $claim = $queue->getClaim('{claimId}'); + + +Update claim +------------ + +This operation updates the specified claim for the specified queue. +Claims with malformed IDs or claims that are not found by ID are +ignored. + +Clients should periodically renew claims during long-running batches of +work to avoid losing a claim while processing a message. The client can +renew a claim by executing this method on a specific **Claim** and +including a new TTL. The API will then reset the age of the claim and +apply the new TTL. + +.. code-block:: php + + use OpenCloud\Common\Constants\Datetime; + + $claim->update(array( + 'ttl' => 10 * Datetime::MINUTE + )); + + +Release claim +------------- + +This operation immediately releases a claim, making any remaining +undeleted messages that are associated with the claim available to other +workers. Claims with malformed IDs or claims that are not found by ID +are ignored. + +This operation is useful when a worker is performing a graceful +shutdown, fails to process one or more messages, or is taking longer +than expected to process messages, and wants to make the remainder of +the messages available to other workers. + +.. code-block:: php + + $message->delete(); diff --git a/doc/_build/html/_sources/services/queues/index.txt b/doc/_build/html/_sources/services/queues/index.txt new file mode 100644 index 000000000..4175df760 --- /dev/null +++ b/doc/_build/html/_sources/services/queues/index.txt @@ -0,0 +1,56 @@ +Queues v1 +========= + +.. include:: ../common/clients.sample.rst + +Queues service +~~~~~~~~~~~~~~ + +Now to instantiate the Queues service: + +.. code-block:: php + + $service = $client->queuesService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + queues + messages + claims + + +Glossary +-------- + +.. glossary:: + + claim + A Claim is the process of a worker checking out a message to perform + a task. Claiming a message prevents other workers from attempting to + process the same messages. + + queue + A Queue is an entity that holds messages. Ideally, a queue is created + per work type. For example, if you want to compress files, you would + create a queue dedicated to this job. Any application that reads from + this queue would only compress files. + + message + A Message is a task, a notification, or any meaningful data that a + producer or publisher sends to the queue. A message exists until it is + deleted by a recipient or automatically by the system based on a TTL + (time-to-live) value. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/queues/messages.txt b/doc/_build/html/_sources/services/queues/messages.txt new file mode 100644 index 000000000..3a5dee89d --- /dev/null +++ b/doc/_build/html/_sources/services/queues/messages.txt @@ -0,0 +1,203 @@ +Messages +======== + +Setup +----- + +In order to work with messages, you must first retrieve a queue by its name: + +.. code-block:: php + + $queue = $service->getQueue('{queueName}'); + + +Post new message +---------------- + +This operation posts the specified message or messages. You can submit +up to 10 messages in a single request. + +When posting new messages, you specify only the ``body`` and ``ttl`` for +the message. The API will insert metadata, such as ID and age. + +How you pass through the array structure depends on whether you are +executing multiple or single posts, but the keys are the +same: + +* The ``body`` attribute specifies an arbitrary document that constitutes + the body of the message being sent. The size of this body is limited to + 256 KB, excluding whitespace. + +* The ``ttl`` attribute specifies how long the server waits before marking + the message as expired and removing it from the queue. The value of ttl + must be between 60 and 1209600 seconds (14 days). Note that the server + might not actually delete the message until its age has reached up to + (ttl + 60) seconds, to allow for flexibility in storage implementations. + + +Posting a single message +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: php + + use OpenCloud\Common\Constants\Datetime; + + $queue->createMessage(array( + 'body' => (object) array( + 'event' => 'BackupStarted', + 'deadline' => '26.12.2013', + ), + 'ttl' => 2 * Datetime::DAY + )); + +`Get the executable PHP script for this example `_ + + +Post a batch of messages +~~~~~~~~~~~~~~~~~~~~~~~~ + +Please note that the list of messages will be truncated at 10. For more, +please execute another method call. + +.. code-block:: php + + use OpenCloud\Common\Constants\Datetime; + + $messages = array( + array( + 'body' => (object) array( + 'play' => 'football' + ), + 'ttl' => 2 * Datetime::DAY + ), + array( + 'body' => (object) array( + 'play' => 'tennis' + ), + 'ttl' => 50 * Datetime::HOUR + ) + ); + + $queue->createMessages($messages); + + +Get messages +------------ + +This operation gets the message or messages in the specified queue. + +Message IDs and markers are opaque strings. Clients should make no +assumptions about their format or length. Furthermore, clients should +assume that there is no relationship between markers and message IDs +(that is, one cannot be derived from the other). This allows for a wide +variety of storage driver implementations. + +Results are ordered by age, oldest message first. + + +Parameters +~~~~~~~~~~ + +When retrieving messages, you can filter using these options: + ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++====================+=========+============+============================================================================================================================================================================================================================================================================================================================================================================================================================================================================+ +| marker | Query | String | Specifies an opaque string that the client can use to request the next batch of messages. The marker parameter communicates to the server which messages the client has already received. If you do not specify a value, the API returns all messages at the head of the queue (up to the limit). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | When more messages are available than can be returned in a single request, the client can pick up the next batch of messages by simply using the URI template parameters returned from the previous call in the "next" field. Specifies up to 10 messages (the default value) to return. If you do not specify a value for the limit parameter, the default value of 10 is used. Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| echo | Query | Boolean | Determines whether the API returns a client's own messages. The echo parameter is a Boolean value (true or false) that determines whether the API returns a client's own messages, as determined by the uuid portion of the User-Agent header. If you do not specify a value, echo uses the default value of false. If you are experimenting with the API, you might want to set echo=true in order to see the messages that you posted. The echo parameter is optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| include_claimed | Query | ​Boolean | Determines whether the API returns claimed messages and unclaimed messages. The include\_claimed parameter is a Boolean value (true or false) that determines whether the API returns claimed messages and unclaimed messages. If you do not specify a value, include\_claimed uses the default value of false (only unclaimed messages are returned). Optional. | ++--------------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. code-block:: php + + $messages = $queue->listMessages(array( + 'marker' => '51db6f78c508f17ddc924357', + 'limit' => 20, + 'echo' => true + )); + + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } + + +Get a set of messages by ID +--------------------------- + +This operation provides a more efficient way to query multiple messages +compared to using a series of individual GET. Note that the list of IDs +cannot exceed 20. If a malformed ID or a nonexistent message ID is +provided, it is ignored, and the remaining messages are returned. + +Parameters +~~~~~~~~~~ + ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++============+=========+============+========================================================================================================================================+ +| ids | Query | String | Specifies the IDs of the messages to get. Format multiple message ID values by separating them with commas (comma-separated). Optional | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ +| claim_id | Query | ​Boolean | Specifies the claim ID with which the message is associated. Optional. | ++------------+---------+------------+----------------------------------------------------------------------------------------------------------------------------------------+ + + +.. code-block:: php + + $ids = array('id_1', 'id_2'); + + $messages = $queue->listMessages(array('ids' => $ids)); + + foreach ($messages as $message) { + echo $message->getId() . PHP_EOL; + } + + +Delete a set of messages by ID +------------------------------ + +This operation immediately deletes the specified messages. If any of the +message IDs are malformed or non-existent, they are ignored. The +remaining valid messages IDs are deleted. + +.. code-block:: php + + $ids = array('id_1', 'id_2'); + $response = $queue->deleteMessages($ids); + + +Get a specific message +---------------------- + +This operation gets the specified message from the specified queue. + +.. code-block:: php + + /** @var $message OpenCloud\Queues\Message */ + $message = $queue->getMessage('{messageId}'); + + +Once you have access to the ``Message`` object, you access its attributes: + ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| attribute | method | description | ++===========+=============+==============================================================================================================+ +| href | ``getHref`` | An opaque relative URI that the client can use to uniquely identify a message resource and interact with it. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| ttl | ``getTtl`` | The TTL that was set on the message when it was posted. The message expires after (ttl - age) seconds. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| age | ``getAge`` | The number of seconds relative to the server's clock. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ +| body | ``getBody`` | The arbitrary document that was submitted with the original request to post the message. | ++-----------+-------------+--------------------------------------------------------------------------------------------------------------+ + + +Delete message +-------------- + +.. code-block:: php + + $message->delete(); diff --git a/doc/_build/html/_sources/services/queues/queues.txt b/doc/_build/html/_sources/services/queues/queues.txt new file mode 100644 index 000000000..f324e3f3b --- /dev/null +++ b/doc/_build/html/_sources/services/queues/queues.txt @@ -0,0 +1,135 @@ +Queues +====== + +A note on Client IDs +-------------------- + +For most of the operations in Cloud Queues, you must specify a **Client ID** +which will be used as a unique identifier for the process accessing this +Queue. This is basically a UUID that must be unique to each client +accessing the API - it can be an arbitrary string. + +.. code-block:: php + + $service->setClientId(); + + echo $service->getClientId(); + +If you call ``setClientId`` without any parameters, a UUID is +automatically generated for you. + + +List queues +----------- + +This operation lists queues for the project. The queues are sorted alphabetically by name. + +.. code-block:: php + + $queues = $service->listQueues(); + + foreach ($queues as $queue) { + echo $queue->getName() , PHP_EOL; + } + + +Filtering lists +~~~~~~~~~~~~~~~ + +You can also filter collections using the following query parameters: + ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Style | Type | Description | ++==========+=======+=========+================================================================================================================================================================================================+ +| marker | Query | ​String | Specifies the name of the last queue received in a previous request, or none to get the first page of results. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| limit | Query | Integer | Specifies the number of queues to return. The default value for the number of queues returned is 10. If you do not specify this parameter, the default number of queues is returned. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| detailed | Query | ​Boolean | Determines whether queue metadata is included in the response. The default value for this parameter is false, which excludes the metadata. Optional. | ++----------+-------+---------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +.. code-block:: php + + $queues = $service->listQueues(array('detailed' => false)); + + +Create queue +------------ + +The only parameter required is the name of the queue you're creating. The name +must not exceed 64 bytes in length, and it is limited to US-ASCII letters, +digits, underscores, and hyphens. + +.. code-block:: php + + $queue = $service->createQueue('new_queue'); + +`Get the executable PHP script for this example `_ + + +Find queue details +------------------ + +.. code-block:: php + + /** @var $queue OpenCloud\Queues\Resource\Queues */ + $queue = $service->getQueue('{name}'); + + +Check queue existence +--------------------- + +This operation verifies whether the specified queue exists by returning +``TRUE`` or ``FALSE``. + +.. code-block:: php + + if ($service->hasQueue('new_queue')) { + // do something + } + + +Update queue metadata +--------------------- + +This operation replaces any existing metadata document in its entirety. +Ensure that you do not accidentally overwrite existing metadata that you +want to retain. If you want to *append* metadata, ensure you merge a new +array to the existing values. + +.. code-block:: php + + $queue->saveMetadata(array( + 'foo' => 'bar' + )); + + +Retrieve the queue metadata +--------------------------- + +This operation returns metadata, such as message TTL, for the queue. + +.. code-block:: php + + $metadata = $queue->retrieveMetadata(); + print_r($metadata->toArray()); + + +Get queue stats +--------------- + +This operation returns queue statistics, including how many messages are +in the queue, categorized by status. + +.. code-block:: php + + $queue->getStats(); + +Delete queue +------------ + +.. code-block:: php + + $queue->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/volume/index.txt b/doc/_build/html/_sources/services/volume/index.txt new file mode 100644 index 000000000..bee589086 --- /dev/null +++ b/doc/_build/html/_sources/services/volume/index.txt @@ -0,0 +1,51 @@ +Volumes v1 +========== + +.. include:: ../common/clients.sample.rst + +Volume service +~~~~~~~~~~~~~~ + +Now to instantiate the Volume service: + +.. code-block:: php + + $service = $client->volumeService('{catalogName}', '{region}', '{urlType}'); + +.. include:: ../common/service-args.rst + + +Operations +---------- + +.. toctree:: + + volumes + volume-types + snapshots + + +Glossary +-------- + +.. glossary:: + + volume + A volume is a detachable block storage device. You can think of it as a USB + hard drive. It can only be attached to one instance at a time. + + volume type + Providers may support multiple types of volumes; at Rackspace, a volume + can either be ``SSD`` (solid state disk: expensive, high-performance) or + ``SATA`` (serial attached storage: regular disks, less expensive). + + snapshot + A snapshot is a point-in-time copy of the data contained in a volume. + + +Further links +------------- + +- `Getting Started Guide for the API `_ +- `API Developer Guide `_ +- `API release history `_ diff --git a/doc/_build/html/_sources/services/volume/snapshots.txt b/doc/_build/html/_sources/services/volume/snapshots.txt new file mode 100644 index 000000000..555824a9d --- /dev/null +++ b/doc/_build/html/_sources/services/volume/snapshots.txt @@ -0,0 +1,56 @@ +Snapshots +========= + +Create a snapshot +----------------- + +A ``Snapshot`` object is created from the Cloud Block Storage service. +However, it is associated with a volume, and you must specify a volume +to create one: + +.. code-block:: php + + // New instance of OpenCloud\Volume\Resource\Snapshot + $snapshot = $service->snapshot(); + + // Send to API + $snapshot->create(array( + 'display_name' => 'Name that snapshot', + 'volume_id' => $volume->id + )); + +`Get the executable PHP script for this example `_ + + +List snapshots +-------------- + +.. code-block:: php + + $snapshots = $service->snapshotList(); + + foreach ($snapshots as $snapshot) { + /** @param $snapshot OpenCloud\Volume\Resource\Snapshot */ + } + +`Get the executable PHP script for this example `_ + + +To get details on a single snapshot +----------------------------------- + +.. code-block:: php + + $snapshot = $dallas->snapshot('{snapshotId}'); + +`Get the executable PHP script for this example `_ + + +To delete a snapshot +-------------------- + +.. code-block:: php + + $snapshot->delete(); + +`Get the executable PHP script for this example `_ diff --git a/doc/_build/html/_sources/services/volume/volume-types.txt b/doc/_build/html/_sources/services/volume/volume-types.txt new file mode 100644 index 000000000..ba3176df9 --- /dev/null +++ b/doc/_build/html/_sources/services/volume/volume-types.txt @@ -0,0 +1,30 @@ +Volume Types +============ + +List volume types +----------------- + +.. code-block:: php + + $volumeTypes = $service->volumeTypeList(); + + foreach ($volumeTypes as $volumeType) { + /** @param $volumeType OpenCloud\Volume\Resource\VolumeType */ + } + + +Describe a volume type +---------------------- + +If you know the ID of a volume type, use the ``volumeType`` method to +retrieve information on it: + +.. code-block:: php + + $volumeType = $service->volumeType(1); + +A volume type has three attributes: + +- ``id`` the volume type identifier +- ``name`` its name +- ``extra_specs`` additional information for the provider diff --git a/doc/_build/html/_sources/services/volume/volumes.txt b/doc/_build/html/_sources/services/volume/volumes.txt new file mode 100644 index 000000000..a84ed0421 --- /dev/null +++ b/doc/_build/html/_sources/services/volume/volumes.txt @@ -0,0 +1,88 @@ +Volumes +======= + +Create a volume +--------------- + +To create a volume, you must specify its size (in gigabytes). All other +parameters are optional: + +.. code-block:: php + + // Create instance of OpenCloud\Volume\Resource\Volume + $volume = $service->volume(); + + $volume->create(array( + 'size' => 200, + 'volume_type' => $service->volumeType(''), + 'display_name' => 'My Volume', + 'display_description' => 'Used for large object storage' + )); + +`Get the executable PHP script for this example `_ + + +List volumes +------------ + +.. code-block:: php + + $volumes = $service->volumeList(); + + foreach ($volumes as $volume) { + /** @param $volumeType OpenCloud\Volume\Resource\Volume */ + } + +`Get the executable PHP script for this example `_ + + +Get details on a single volume +------------------------------ + +If you specify an ID on the ``volume()`` method, it retrieves +information on the specified volume: + +.. code-block:: php + + $volume = $dallas->volume(''); + echo $volume->size; + +`Get the executable PHP script for this example `_ + + +To delete a volume +------------------ + +.. code-block:: php + + $volume->delete(); + +`Get the executable PHP script for this example `_ + + +Attach a volume to a server +--------------------------- + +.. code-block:: php + + // retrieve server + $computeService = $client->computeService('{catalogName}', '{region}'); + $server = $computeService->server('{serverId}'); + + // attach volume + $server->attachVolume($volume, '{mountPoint}') + +The ``{mountPoint}`` is the location on the server on which to mount +the volume (usually ``/dev/xvhdd`` or similar). You can also supply +``'auto'`` as the mount point, in which case the mount point will be +automatically selected for you. ``auto`` is the default value for +``{mountPoint}``, so you do not actually need to supply anything for +that parameter. + + +Detach a volume from a server +----------------------------- + +.. code-block:: php + + $server->detachVolume($volume); diff --git a/doc/_build/html/_sources/url-types.txt b/doc/_build/html/_sources/url-types.txt new file mode 100644 index 000000000..26a0410bd --- /dev/null +++ b/doc/_build/html/_sources/url-types.txt @@ -0,0 +1,15 @@ +URL types +========= + +internalURL +----------- + +An internal URL is a URL that is accessible only from within the Rackspace +Cloud network. Access to an internal URL is always free of charge. + + +publicURL +--------- + +A public URL is a URL that is accessible from anywhere. Access to a public URL +usually incurs traffic charges. diff --git a/doc/_build/html/_sources/using-php-5.3.txt b/doc/_build/html/_sources/using-php-5.3.txt new file mode 100644 index 000000000..8790710f2 --- /dev/null +++ b/doc/_build/html/_sources/using-php-5.3.txt @@ -0,0 +1,20 @@ +Using the SDK with PHP v5.3 +=========================== + +Since PHP 5.3 has entered EOL and no longer receives security updates, we have bumped the minimum requirement to 5.4. Using 5.3 is still possible, however, but you will need to use an older stable version of the SDK. There are two ways to do this. + +The first way is by requiring it through the command line: + +.. code-block:: bash + + composer require rackspace/php-opencloud:1.12 + +The second way is by updating your composer.json file, and specifying the appropriate version of the SDK: + +.. code-block:: json + + "require": { + "rackspace/php-opencloud": "~1.12" + } + +Note that **1.12** is the last minor release supporting PHP 5.3. Version 1.13 and above has shifted to PHP 5.4. diff --git a/doc/_build/html/_static/ajax-loader.gif b/doc/_build/html/_static/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..61faf8cab23993bd3e1560bff0668bd628642330 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nno%(3)e{?)x>&1u}A`t?OF7Z|1gRivOgXi&7IyQd1Pl zGfOfQ60;I3a`F>X^fL3(@);C=vM_KlFfb_o=k{|A33hf2a5d61U}gjg=>Rd%XaNQW zW@Cw{|b%Y*pl8F?4B9 zlo4Fz*0kZGJabY|>}Okf0}CCg{u4`zEPY^pV?j2@h+|igy0+Kz6p;@SpM4s6)XEMg z#3Y4GX>Hjlml5ftdH$4x0JGdn8~MX(U~_^d!Hi)=HU{V%g+mi8#UGbE-*ao8f#h+S z2a0-5+vc7MU$e-NhmBjLIC1v|)9+Im8x1yacJ7{^tLX(ZhYi^rpmXm0`@ku9b53aN zEXH@Y3JaztblgpxbJt{AtE1ad1Ca>{v$rwwvK(>{m~Gf_=-Ro7Fk{#;i~+{{>QtvI yb2P8Zac~?~=sRA>$6{!(^3;ZP0TPFR(G_-UDU(8Jl0?(IXu$~#4A!880|o%~Al1tN literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/basic.css b/doc/_build/html/_static/basic.css new file mode 100644 index 000000000..967e36ce0 --- /dev/null +++ b/doc/_build/html/_static/basic.css @@ -0,0 +1,537 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + width: 30px; +} + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- general body styles --------------------------------------------------- */ + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.optional { + font-size: 1.3em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +tt.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +tt.descclassname { + background-color: transparent; +} + +tt.xref, a tt { + background-color: transparent; + font-weight: bold; +} + +h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/doc/_build/html/_static/comment-bright.png b/doc/_build/html/_static/comment-bright.png new file mode 100644 index 0000000000000000000000000000000000000000..551517b8c83b76f734ff791f847829a760ad1903 GIT binary patch literal 3500 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 A2LJ#7 literal 0 HcmV?d00001 diff --git a/doc/_build/html/_static/comment.png b/doc/_build/html/_static/comment.png new file mode 100644 index 0000000000000000000000000000000000000000..92feb52b8824c6b0f59b658b1196c61de9162a95 GIT binary patch literal 3445 zcmV-*4T|!KP)Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bXU&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWwr)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>={htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!hR|78Dq|Iq-afF%KE1Brn_fm;Im z_u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV2nzr)JMUJvzW@LNr%6OX zR5;6Zk;`k`RTRfR-*ac2G}PGmXsUu>6ce?Lsn$m^3Q`48f|TwQ+_-Qh=t8Ra7nE)y zf@08(pjZ@22^EVjG*%30TJRMkBUC$WqZ73uoiv&J=APqX;!v%AH}`Vx`999MVjXwy z{f1-vh8P<=plv&cZ>p5jjX~Vt&W0e)wpw1RFRuRdDkwlKb01tp5 zP=trFN0gH^|L4jJkB{6sCV;Q!ewpg-D&4cza%GQ*b>R*=34#dW;ek`FEiB(vnw+U# zpOX5UMJBhIN&;D1!yQoIAySC!9zqJmmfoJqmQp}p&h*HTfMh~u9rKic2oz3sNM^#F zBIq*MRLbsMt%y{EHj8}LeqUUvoxf0=kqji62>ne+U`d#%J)abyK&Y`=eD%oA!36<)baZyK zXJh5im6umkS|_CSGXips$nI)oBHXojzBzyY_M5K*uvb0_9viuBVyV%5VtJ*Am1ag# zczbv4B?u8j68iOz<+)nDu^oWnL+$_G{PZOCcOGQ?!1VCefves~rfpaEZs-PdVYMiV z98ElaJ2}7f;htSXFY#Zv?__sQeckE^HV{ItO=)2hMQs=(_ Xn!ZpXD%P(H00000NkvXXu0mjfa,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.0.3");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.0.3") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.0.3") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$rotation);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before,.icon-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before,.icon-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before,.icon-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before,.icon-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"\f0a8"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before,.icon-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a{color:#2980b9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:60px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:60px}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27ae60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980b9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{display:block;float:left;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{display:block;float:left;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:0.5em 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e74c3c}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 1px #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:"Incosolata","Consolata","Monaco",monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980b9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:fixed;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040} diff --git a/doc/_build/html/_static/default.css b/doc/_build/html/_static/default.css new file mode 100644 index 000000000..5f1399abd --- /dev/null +++ b/doc/_build/html/_static/default.css @@ -0,0 +1,256 @@ +/* + * default.css_t + * ~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- default theme. + * + * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: sans-serif; + font-size: 100%; + background-color: #11303d; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + background-color: #1c4e63; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + background-color: #ffffff; + color: #000000; + padding: 0 20px 30px 20px; +} + +div.footer { + color: #ffffff; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ffffff; + text-decoration: underline; +} + +div.related { + background-color: #133f52; + line-height: 30px; + color: #ffffff; +} + +div.related a { + color: #ffffff; +} + +div.sphinxsidebar { +} + +div.sphinxsidebar h3 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: #ffffff; +} + +div.sphinxsidebar h4 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: #ffffff; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: #ffffff; +} + +div.sphinxsidebar a { + color: #98dbcc; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + + + +/* -- hyperlink styles ------------------------------------------------------ */ + +a { + color: #355f7c; + text-decoration: none; +} + +a:visited { + color: #355f7c; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + + + +/* -- body styles ----------------------------------------------------------- */ + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Trebuchet MS', sans-serif; + background-color: #f2f2f2; + font-weight: normal; + color: #20435c; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 160%; } +div.body h3 { font-size: 140%; } +div.body h4 { font-size: 120%; } +div.body h5 { font-size: 110%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + text-align: justify; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.admonition p { + margin-bottom: 5px; +} + +div.admonition pre { + margin-bottom: 5px; +} + +div.admonition ul, div.admonition ol { + margin-bottom: 5px; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 5px; + background-color: #eeffcc; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +tt { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} + +th { + background-color: #ede; +} + +.warning tt { + background: #efc2c2; +} + +.note tt { + background: #d6d6d6; +} + +.viewcode-back { + font-family: sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; +} \ No newline at end of file diff --git a/doc/_build/html/_static/doctools.js b/doc/_build/html/_static/doctools.js new file mode 100644 index 000000000..c5455c905 --- /dev/null +++ b/doc/_build/html/_static/doctools.js @@ -0,0 +1,238 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('

        • S)-&+% zX{KPt^8sOIJ6^1rCBU;au#PozOVePFI~MtY$8kx}arlN>1|B{=MUeGW5wnrn zU7rQx#V^a3$+OUlByVD&yO0l|S=Z2IXfe_t z_=$M68@0oY3mYJ{9WGtZF+t zX{j(+rWv3v*0wFmmSn%xx$8b14iq)bz0Pn{_T;X$jMAm2w1H9-#VW?T~W;BdCE*chV|I~|z&u5&cvCkLmevK^?u1#QLRM{f%TLX9< z=d>rV8W%|V#A;lK{2haO6 z83$3y!S-spScpF@Abt}B=2Nf-w(^1f)^XXgIR1WYHrEneo4R5K{ub$Wev#3;)>wp- z@#`&Gv?R!pWnIasFO9BVt(PHUix;nHu6n##n=TQdZ+DuuX49pTTHBXR1NaWNW^W5C zh1 z!+7{Kg5PjVzy%|<$4z?z-?m*Z&<#T@3>qevB|DqBS95LPUM+?V*{xKzJX!TK-L%#BlM1?=OP`-dwBR% z6)N$3z=TE7tt8jdT>`ROgQRw#mg++do3lo*v1o-gY@c?@(DLr(tFoEym8WG+?zWa^ zGWZ$JoSHp#X=drtWsBCtGzt(RhbMxw5}UJ9%Nn$kTDC`+Mr+xqq}FTM<@gS3*(IT2 zuRx;IvZv$4Ctq;lhf;jhk}HLat|iaFdss`Z!h3TqVWV;_xtgE7hfpkfCODv=uffBo zXW=*WayCrF%f-d77074TA)8(NIg-)fakLjH&e7uH*YRtcxcER=64wg?yZCeQhNWEj z)`yGEYQB4gp3nG+Jd4oi(%`19iJk-2AQzTYw_^ub@&ZEgT;w>h72sw z*D$AhWR+aZo?(kLy&i!5=8W1!GuYMX(yc=8HvF3HNRX>d=naziMoygVjuwZ?g>;DJ z((Ondpf_>moTiIwgx;Ga_br@zcmy>q0B;q5w=qCRCWpox`=nfYyQJQMUpBYd8Qz)0 z$DrsPNX9KWHC<{K?9e*}?Opg~=__Tz{#JUofVbze`YIiI5594m4#qk4TXtM}ub{q< zQD=m5XbInsG@m}eIkS0_7vY+aG%n@AJvlDjDQF+$%yyj1@{Fm>Z)HBfm@^!LLsrWmt1=XwNhsRfd*3UAhZ7kn=F`D^nKN3Sn1R76<-wwo7*- zuixyX;S3OP#ZZ+B^f9EF^MkbluZ*Jy$(-#CC(AU_!>iam^l^shfZZ~-+-CC$eDB3A zMRtWX)wc<_^hu#T)5Xm=l{7Z5@hHfpPf6+wugn}n5kHMY*;IiuaoD*~#pEzBvNnAN z8RitbM4#oC4jjtHxoBLNM4uBfow{(+1$&oWpnH&p+j2UgB5qN_`V?<;|GWUk9IN03 zU|(Rc7IQ`*n7*2azQ}OS>V9(pin>f`7^ut_R;2YOmh!U&ynYE_=4_VS1)>A~1z3T; z%*icT+`}?L_cA4Owp|>;`k3pD;szMUwmtd^(yucY;(82RDK!!OuQH)l7A@n^*Z5-w zUo%vy7SnsUzR}l_hSHpstrjckn(MlBAJS0^aT1G9?gqf$(7-cyTU26QK-ad4^i6?1 zg3D_d7u0a6r?G+vRp96MBNN9C?MioI>B=lUz@KyMJvm%7C0*peir-?=EoLjmu*10# z`nHgi!iHDx%DFk5m80)S>JhkK$=aE=ix?88aUTkAiiGcd7nys_g;2Fv3j=lkNy`aH z`W}F;GpE|6UG#k+rWLQab~Otviv{|DB+s_@RIu?Qjr*r;D&gWAdQj5mvh{GSmRxoe zSmlR8bGp7HD$SLIek8y%xgIol@Q)>RR;0w}CrCD1s@`z-sq|BRj|HT-_B^C12*x2h1a-reOE3 zO&nKHfB9rsmL5g!UUNDwBdaC-3rXBCh$(Bq{NGHwI%FOZZN5;g$fHR(#@jbZZUm#r zc*l_>{B}7DbZ>ti`d>%uF40jqNq-Cu3(m)#R||2N_*j`RtD1A0ZjaL~?o^>P4Zpat zWF?PTTZAgQ4|45EcvTv11jFrLxc$WJ2wM=BS^+vhZTOW-Sa4Svwd2F54*XV!lK%m> C5hOAI literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/compute/Keypair.md.doctree b/doc/_build/doctrees/services/compute/Keypair.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..87052e9b7e2f8ecbd89092fbe61c18abc3084c43 GIT binary patch literal 12007 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/compute/Server.md.doctree b/doc/_build/doctrees/services/compute/Server.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..41b786999b2bf9c377ebbf88a0774b7c7c032cc8 GIT binary patch literal 55723 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 Gb^bpeR;%3r literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/compute/Service.md.doctree b/doc/_build/doctrees/services/compute/Service.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..ada2a1d5acb45d233f9c44a0d2add71737397951 GIT binary patch literal 6867 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/compute/keypair.doctree b/doc/_build/doctrees/services/compute/keypair.doctree new file mode 100644 index 0000000000000000000000000000000000000000..6b57752112cab99cd059af683fd1dd0cbec9a8ae GIT binary patch literal 13233 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/compute/keypairs.doctree b/doc/_build/doctrees/services/compute/keypairs.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1555d7ab835f329416d508dc7314d29450b2b0a9 GIT binary patch literal 8422 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/database/configurations.doctree b/doc/_build/doctrees/services/database/configurations.doctree new file mode 100644 index 0000000000000000000000000000000000000000..1b2ca99d4add0e68d3ce87632210feb42adc5b9e GIT binary patch literal 16196 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 Y(tbJ?zjBj^#}%FU@FYcSV8 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 literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/database/index.doctree b/doc/_build/doctrees/services/database/index.doctree new file mode 100644 index 0000000000000000000000000000000000000000..00858d04f583e8df89fb49b96aa3b3410c4c4efb GIT binary patch literal 21373 zcmeHP2bkQ{wKm3U?~c8$7)Y=|rg;tC8Jmv54#tL!vv^IgMTUfBRy(6zNuC*vjx@Vo zhyo;m*dYy4NJ0__>78`aNl0&`klsk|y^+fM&$(9`&FmV&_X6R)FaFje-FweD_jKJR zT`{{{@ypG~t%mu!U$Mg+7i)Gs$^{3kxiXj?2M6|jv}`{ZiJB~Fyl?PVjE*V8IfZfARF}TEp{_t)el1pWOJ<^EB1t4Wo8UP3+C@F zc5#n()NBL`hKh?&AgjDPY*=O6P}RLd#rddakqm##Xi_y;xK|)Ss17#DyEWvZq2dBS zR;5VN1nbA*#KyHn#B&V1EVY8r(Sta28ZUCIJS zI=$wi)GYm~@+fDaIiHQv?~nDfQI2*NnTt}BXsBbH#pZ(4ApJ1NvCa~6NvkF?$7*M( z*{hAwuZ+>xG`q`=TdhFB!J6UVxK8&E?K0aa7aR}oUmFMg@P4u;wJGxEEORbw2I~X` zXSO+4*RLy{1-lna^c)sEtLEB;2KgRng@uOGC`_#H4=cM1h^>NVE-WPc7TkKp-jk;= z3f7zRB5RzD7@T0v)2sw1nmH?q$Q^A)aFLVZ;N(8DyV7hhTo{}(Y|blJtq{H$oH}fF zkHU9}9qhDW^Fa26(}#l%&asx+rQpvP4$gEITTOEgG-3xE2WNwrj`?7dGuwey9?{pS z7Wi(}&IM;dDVyWq>^>(iVX-wE7F(Qxv({PXtanZ@y96QF>VVyI`kYgSol}RM(}tbX zhn+Kqn+|$)bYJIocD2jigXA+Xo&F=Qx-!@n2aiJM4)?XWIchaW9X8Zp9Gr)0bCzPE zlO)*hb8bj!BM;x3#>YU)5$bvk& zO_1OM=SU0E1UsDtR+~t>`W#l283z}#qBtAvlx*}Eur~+|?EpnlO|gw)r)n;81~SNx z1v`(6gNs>wCShwbta^nLxzTJGmZ5B%H_r7b*AlPE zLA#bwjY+p!HR?8=QbLGZIB0~*=JU51yX~nBL0v0idvPV$>GUHC`*k-Q&_1MX#6bWT zGJ8^?0%C6pXVTvRiA5sl?;#;>6CzB+!6X}uMX%0iu&NtjG*B&#R{iqsT(Ad5n~H-= z*=W~8OVFgT15eg1RlixeIMw|bx!lo8TgFD{ebPu<`qH}%8_b=hZClS7+O=)hMWu6w zww}LZ+t9AHhDBse_2mp>bz-K~#>P#?>dTU1ym~FS;_)~;6cW4Hv!_cRC`q%T%EME<(j9o`E#(> zIYx5LK4+2Sn)!+NPgS|*nmBkGy9Xx;c8#5xLD$n^n5(k6<{53Yb*@P%K2ztKPBPDe zAlJshb?C-nlGmt=$rSsuKIV=$)ff%=h>$lB65)p6xfx+3@Siu6FujR-u7@xW0$0z6Ze9=vFJ#>? zhtF2J;lRBi4qn8%c`kI*9-gfbPF3Bv`Z8oijP>ibNmazvzZ{sHvnPG3=&)@CCW1Dy z>sCG5mb4|sR@f%qA#wWRJ}2Jiq(SLi?#vW_ixUmM1R8$G$`;%R!`u`HFNFmp`fg~8 zzMJFVWl0N1AI4g0Z1VCprwU%-td{6|rL#n$Z(bt&t5o#8Iu2gLHs*OuYY?X&X2EOW z5I1Y;GERA28+9FjDb3eIp)(VD1B7{F9J~o#IZX7`tq8m&4&KZhtN{l@{vJ&kev8IK8Et9%}ygDGN!~RQ-*$69YB7^7*b~VhJ|5fgcXT~ zjfp#tI5UkoZSH)>LnJhgt2M*uI2njWm~|TyR<&u%IJ7n5G5C3daj1p4{+QLQMrt%V zYRkh0>me1xl?PQi*c}{0Q z&K*qoD+owl2x@bz;H&WPuf@UFli;`$@dS%uh$+1W-@piKpm+iB{{0>pd=s62D-OQR z5?)`!0+PV_uPk+Ro@uj^J)rb$3Miu zAG!4=;UIZvwGyLxie*!u%2mFs2-`@D9l0&1a~sdG%NS&bSR^h-2F8rM`pPME%&%7c zNnQgUBfln9Bk7W4+nRCi%z|E_+%!_kranjV-JhmwX=9r2{w(?K?l|}hi+AHgUp$!R z57}h)*BPW6?BbO3w?1*6zsJEpSTM$*#bjgb*{pnpzBE;x5H@;z`McDDqcX2CJ8 zx{?mSC8pW<1x*cQ~|I#4k-M;Pl;j1?bzK46!-r{_8>yV_V9F55Yp z3)a?LN%MryZq4V>Y?Ob&t6?naVH|S~k3sHb1I3(+KAYrH4&Y+KBsge3YNm~ml)Y&I zw+-A2UFrn{9AF_XF)hNcb^wnhQhB#06_*xEWoPQgW)5}^ab8iVZUrGk2hsqG8qfCK`9a<|PuR(Pq>*D|l^+&gR>>vOKj+&q!&DAYk{5w&E_PbEKS? z0|!`h)CUgeTt+Q;+&ki!7r}U$iUtHa)16+aO36Qx5e7P`Z9^?I{U}^w8Wb9NPj|s= zzz3&4?6Ho~@SF8Yj?M!J7~U@ELkgW$kzo&;&KjI{0DyTtUjT(ydNl6MMUh{Q$hP|@ zvGD~_=>pV>Tc(?)D-t#BL{m9zIBtDU9ywl}+|c+r+J#myWa1Ljh4@VZidj_p!bY>| zjUK~w;D;ZC*jKJMYoi#%ROlj<2WOeRZoOP>R&2+uU{j4Ai`soop?C-aB3;r+7QWO@ zHa!k7W==MML%&{~qKg^HoP+ZLqcki8+IL6HdDyNa*AD3tv<_1VzkNeRPEa%T!Ha}t z0FMCdYO?UVKiGt^kVXZzEU+{4E>je$2%If&D`xDUY4Xt)Ffb;-u)&Vh3T>&6;eI+h2Jy9sil*nUd zbWknoz@jH{8#s8fz^h=t3ilqf))w0-GgmX{eQ={zGU+J*1xtHziD@6>$t(^1Jyi;w z5*Iw?o`)gVaC>Jb7$eql^fWYq=+kkD=@|@%#}1$FYTw}+C%@T%TX5%E`Z-L1p2-zp z;aP&3VS%nijd>^o^|#UL7g}^3n#EI}jT=u&90aSF-{&x~X$emb&gAI1XavX4!zHHc z8B$GFJ(fZ9)+Mm?e69g=FA%8qEaa)=^FnUI!5$3Qt2W($(*C5U7okxYHRseUw@xoc zIii=~SHg@rmx;%Iv`04r647uIE-}3nzxSqUe$25p3L=)|giZstye~&LgB+CdGNJu) zMH`U<*(nP1ZOqWv?oQz&S@@5&3e0CK|PQ4uwY} zPU=*Q$`q!?ZrQ3%$YVULZWv%Im( zu{$tP7Q`WtYG>5XEtyr(V7fhJEr-VWgHAN}N^H%Wty+JjoPvehO}0J=rm zJ^dVbGfFYN1;6QY;H^^V^o`bY;BDOQ>6HI=!GDLsmnHhKsx{#ge2z&gpYxLZ{!W3* zkfvKvgX5c=-xVUg3pJ9`-;EnjN}STyuxGx9@jacp--|*_@8c?+yO|l?W0JJrFU>P1 z?GJF1C&`+(7F74rp{J7aZ2*v@%zaBzZo$z9(Fogo2$z^{XHt0Nwc3RZ=DOf9SCV=? zonJpJI3H0sD!+bI3K_-d{3<$x;2%S~Arc9DfX&$ftg7)UE5BHD=W88V+eomMy!{ zlpJ(x5YiGCKs~qReA9^B8V*E$lG!eJEO0jad`j5wly>9b@oeKyGw{GeUbN4EP1ygl zxWsga2$c7nsFUdX8g4u(arEWcGGAv3N%VaKg_yp{RXX~Zp^oVL zmNd^8ec$FL6@9!yl``-h)JwlQ2=78!f{*ND3J(=|B{PLI*!ay9DA-1ox*3w|K1t;E88T=z)Nv)O@=mzq*ZE}%ewd>fVGPWMGZpg4#c+I_Vyz)mamFxjY~}-I zvI@>X{J07;5C5MrvjvaE%!c33h3QTiHx902BmaVd2OgpV{SwT<+P}gjreBK~c~48p z2D#6m`;B1!R$(fO|4s^>+Gqvc@3}n*x<3g19~Hj%Sz>eOnTdA<-Jb+9L!ADM8Xa^A zD&37b3A(@F#*-2U-HB|EzcPg+=>CR6On>Jp9dyi4N6`I4nr95Ue{xeR=u!smLA~^= zgKpMr9dyh>8gwc60ceI9X5$jmf%r{=E`iWNcOsO+47LT`9Kq^RSjq%*rI68*4mwdN zbTAL?5_H{y+oN!cXLbY~KeBL?7(t@qEaApT#>pH|t+9BXa!j}2F-O_>m@o7@+1WUF zGTUPT0}uS~`|brD=x`w}*esJC^PXmi#k@~fUM!eP6sFSSQYmyY-g4z-+@84dL4v?@gj!_s&-N#BHqZRG8qCm*K8tvk?eS*72;TCs@+cM*77Myiisf`?- zCBjp!GL|gat7rD)Or}4MyDE6hWY&|9mo7Uw+&Fj|n`JEn4?H03^n*0CoW~`mf-sTy zG>@!|`*iMgg1KH{Ds7)2g-%&o&V3@cC(eD6;GeAUW#^1lC`+(V6I1W-@KXddL!C}V zjk!=fJjJEcP$_PHI&M5Ev72vTeQsb9iJPB+LQH3Jm3DJxsl&}TO7o0vzKNTZoAbV3 zi-Sj?K|0pHeiq8&>&%1rdOQ4Pw8Io<;}X*r{IbKhfoQMa0L3tmZC<}s(9Th4$`0pB zA)_hn^`ca$VF2yo^^X+XZ3?$|Ii$dnNz0Y(`Z$I#+1xDe&m;rEys<^S(TKGgUemym z4AzYFL5LhrVpNhpIe*I9^mhGP<9wU2-eei5mzl|Si(G5#*~a>Hd3qGHR`6J;Y?KTN zi=7f}96Xb)dL9E0JS5oP4pv~wAzWhGAx!5zEfXvIKBMY0++Z@x;t@#M7`e&lpdaaFdEBo}{bpr9&M@B>+eqao-Y0EjSuMBQ$T}64NM? z(vy8HzglPA1U)cUZ4p!!oQlFxI=7{eQH+it(IEsML%T%KxZpYpx5$gVn+???#{xeq z`oo4@cE@;elam|XGeWHJ)5EVvSY5_@ZZm`&CzG67GsQ$cZ(JxwRBNaFl5h=EeOV+` zhf6}d+6wv@Jn6Z}HaRMS!;(8|SW3-p!7e4z0if;M!KUNWR zoC9MKR(ny4eKx-d8lzY-)}u@Y)Y2FiY~HrDpL2}M#w&Pi_H2&v#E6|X-#B;{d-`q$ z9{8_;|4$gW3S%J`)NqNZE@sVp+Ugvz`_01XQ3%K%SsLSm-Pl{LThlE^X>{ zjs9uJf;J8gCh}e(fH-+fR|?T5Dx$?pk=;iI^tT~)>Tg2~w0#?b-^xAHiK51a0>534 zS4ooBmh)KZER-vw1-w{>_wV!TxX|D6DFeKXf&^HP3R}-E?Z6JvglkXcov2pD0X}*X z*nn6ps8+6=jC-=~(lY@J zUwjrWF6Twm&ZpqIxdsh#=4BgJ)&@3`D{&Kz!$O(ZN7@}9887}-v zTw;0^eiIjF8E10gR}1886lC$d`|HB@F3hHu;|l*R$9*k#m^!YOnO=ulXH~o7zMkvm zPj}ompdK5WOb3oTkvMM0=YalQ@wnA5m$o=~9p5fJQhjJB&Dw8b2B$moEyCF}_e-7m z&5Xv*{1)8A^i~1Sdzu z0_yDmAHtW1k`FXtQzxVkqcWzC;MeR%1;5x4(nqE8V_dm7oT`ob)lz~L(#KIfOrPLJ zyu_XP;FHk%q_lpDTMz0$EeXJ<1>iFb(1XogHG`thO6?u^<QuzAe*YHl!~J>X#X{H{nBZ_!ZQ} z^i^(IjPqG?@~5O;_Xz20g7$T8?8e)Ce3mApZ=jetw}yAcc=%SyUC1xghB^8s+I{*K zeq;JJ!DoX5?u_#-CR#m8c&=*N8Dn|yo(h!vZD!gaH8Aor(y(}OuK zA8o2N=w~Q1|6-$3mB$VAbJXE8PYWO^pGgu=pkD}J&atX^d0W7K$zWY(ujWsEQ;L4Y zaL(2ta|JxxrxNspQ%v>ok}4tzS8z^_ehn~lF$*68(Lw)JD1&~()m;^Pv^h?{WlH8^ z8wZTpW2SI4*0Jq~euw&f=1RXAVeMgm^ncHU<}zzIB>e|2^z!$ya9p{xhr^Ekh&mW` zQKeaHlu{24=})LfFk*7x*2jDe{AUHc5N9g*)D^hFIz^4{7TD!%Ha@w_#xG$R1fpO~ zHsb9+4!&m9#n6}o+foR!h&R5?w!!e`!Zki*& z3ppGzj@%`+i#jYub5U(}HKVcq(`X*w=QJq}qHaaWt!Ir)Jq*?(DE*;3PB|_wDplN& z&%MCc`Ku~4UvPVnXz+!dF?SCwU@(q2YqT`!RJB0yOdORR;Ry#L0&82N0 zX;csBW2Ljz_{BRWn|M{DPddiXPu8ZBF970jEAzMij>Vx_v)i{Sb~InLLvdIvmSpp57cE*;lC~Au zhGqEnoZ? zrNH;?Qfn{av%0|)hXrWT{jOCAHS|jP&vn}fpy}B2Ck*(;&%CVYxcvvfjksE>e zfnO-wlc87@i^I*{swG3aP_e_%ngYjHkBcMB-cn6UNTm=4H3>Ga88b6wyA(()B=E)B zF>|$5i-O4@i2NY33p}eha{D3XGLoHJ4#iQixL4L$YOYm+%UL-HE~l9zj&_!rYqZ#M zmfz?(#_2MT(Lzi2Y?O3PVTz5PDiO7y~GUkbiz;X~+4g#xBkc)zx64*Ltx!J8L z3dQlx3UiGXR(fJ_g41L6H$;{0BWzZVM3t+H%5ZwkBc-hcl?E3lIxDG9i>aw^ea-AD zIZ!3AUYs;8HnbW`NU9R}8F4af<&;?T!B(U-mk23qE;5%wnvk35cNUvVllFV(w?c7- z%+QwM(6m)??V+j+rhzLQsyfx7+0%1jd1eTfI0TScw`7My&{(Tzh4xULJ%cig#Hr@8 z$eJP%ive?4LXOCp87snSi?s*}G#HDatl3qrRS7N>r;VG-N}d&lcs_mH>?&HN8Avd(Ga>XM8RO2zapykc zH3w5V$wc>WooBbb7rttw1^MRdt`%Eiu@%!gG|{=WqE#z8B&Kb#xB$)OqQ?AAjwZga zX<~7abF_uo#i+B?A`+Hp5+0CsXjR)|aWSomeBqXkzVH&vuuZMd;iZi=&5$guj`vb$ z3EvSYL{DmSdWO4gFnn$Kno{Ocq2 z>BGbM!$#je`V#jIsGdpBnhj(h^)x0^joH3^>S44T_w^_6%8f9*nxGFM1h_H&%zm0H z^$%p8dEsUwm^9QVqwIz=ng0HB8`3a)n)oF4koYgln#)MR{U8+i17;W8{8SAdSX`bz z6s&_`n%#RV-lni>!2w@kc2(guV4k+PlJpeXs?=5~>@uw_S3#c;mq-H*#vl8ia+A5y2Y|mVyd;N;51=^0~R7YWpQGsFjB*RtOs! zQi#H#t=jP>VN;vLAVv*O6bho0(yQ+J3PhQ7%I;+FM`+cUY_(o^VH?q+1iv|j^*ga} z$=pkXYC3BkMmChsfg1))}$+D@;1iaDV%Auh4tfEi0`2i*5oqm! zG%ZAp?mE9jwc+1IETjh1-k1G**dz=QIqTTJ*PIOd_oK8}W|e=Ri^X2jKFT#O`?tB~ zJhVWhef`x98BUE;%#34&MkFmiM4BD= zb{Qa)1^dRt#K@N2qkFbZOc=x_ zFm)0(t7?U-hcN9jp4%~0-HbHwGhc(%;}PL zXq+ECIqT#PIQ3-6xx(p`_pH;l_Y~;BU2oCisnDON#p3B&xo=>1OVXsh!;P_chVE&K zEzfK+9`P*4;MnqPr-x(9DVi(KQL*KwSUi`sf+`MM7F=<79#rebbc}g^)2vC1sn7KS zNZ{Zmcp(=5qFB5bq&YO%O^sM{b1Ys$6d!kpiZwb7GE`bb(SaQ|lHyqx&K^o;SB(FaSYK>@1TvAAp?XoqpgY)OT;-C z71e?CW~V+lAs%6r=Wj(em=X~OsF!vUQC6$N%P2riK?x{)-w7-gFNcI*5sO!nDDT91 z!Ihg~LKJ{*iN&jE_Fk;IS;7>AUfs?v45c9S8gPn&RzoMmYq6Nu#p3lq$ARb;4n&TM zKQ{yRE}}Ey4Oqw)vUwSELCq^0 zo;zc6DV1G8vsG%ZV2ommCgBJMGGy#*m%a_RK}M;|EKQYIz(nx3zA?JPu*#^_g=Fxs zP&drTAlWOnq384yRWhWO&4jeL2XY@)hH7LvDt=IroN5c4>o$=wDBjX){BLd7=Cilw zwDG?U8h!U=*4v@;?})`ap)w9J?p76RC}(7hsgmbn`<@#n z#&1pv@kw`9B~X3)l-Eb?NR_O%D4l68cBRJ?-ufiXsCC(EBn*R+O96_-e(`3dauikS zPMg%kSWoJ)KC>Z>KQ|1e97B5}rOORFK^W0)e!$qc@wBr*YWt8qu8eAyH(Jg1?JzfA zJi4X6qQIYYr)m;<2nwV@H3z(&K{1(iI&7`cH4~$3s8Oe@SLsxpZtG^_VtUP?xyu#1 zzG@&Ns^l>YQA~BYKI}E4pcHsqhwHoK@?8(S7~rV1ajlT{%t>8_Lb2LaHmtaH#S6%Y z(+kmAvCegwXkGAE?|<6z-1PY7TsMo?Ai!t{qk*8E1@cbq~ae(K~0j! zALx*MWM~c@7&uw-Tx`L7_Q03S!!_$I4ZI3rM=ef99DRV-^(_y(G%J(1yL2j*-7~tQ zpO?@;pX{%5WuvZ_n9XZr|kscxej^lmL0@lh!7$71nu($ss{fpGc3WCQw%SbUP0qAH+Tfob#k zsdmM}Fxq^68ahQ4Pz5bM1Kxf%7N673;S=eN=T(5aBh5j49^1-rJJPsOBN9oP49Mg?aMQtu4t%|KsRx9*%O3%T?@ud#ol_ZP@- zIr?DhhfQ3)`jaZmSpIy5w_GVwLY`RlRx266o0heL8-N;LkPvG^7t zX|MTzyPa?hBaQz~qVY-1|GS{&d$IUF&~P~XW?JLlhqe7Lh*kUmtNdXseniarUs;sW z16Z*7kIUFUj>S*3B$FdJ>IlqFoAg2a%sG+E*gtoAxr{xoRr(jIjQz`4{EB!*=iqkM zk5^GCMsJJ7uQC6R(>3egG)3$^?STKM>|b?QodFlaiCC)H&HUfkjop8`YatAvRuI(sdIF)2QAJCWX`ge z9#^nMPieudCRgDpmWT8AEFb1IJDiu5s|iW}G|`bVZ+QfdOHG_uczvQZG+^ZR z@vcQbIB_ICVtEu}%T6bg5ovWJk@sSdqZLTL0LhOu91p*G!doXkKB|H`4gg_i7FO+& zJBi}Ugx)Uk5nAKCrmrC#?V*ZKh%&a1{J;z52IVoBt9i|=^~W*^?Q3>5bLpiSB>{Bu z<)CPk>o5}BI36FdJb}?<4;~F@CT2l($n*?Gc4C5TJ!ty(tRUlFPa_iJ?7^rehQape zjZRZr*ls6G2bs!AKn9Mq;gK8Affb*Ok64~UQzn)`WY;YLLswzCHVO5q(ODkduSR!- zth0m+3h_hUn}?j547m`<>ro^(thA_6G)3{VX{D+Hy058?Vv9OkJ;ZQH{&r1b*T5 zEHd(RIXWp5;Ha@GpYvG-u3ameleVb2$n!0#57Rk$s%)TUQGX$;U;IoY{k-AO!P_|R zq@)@7efr$CvfW8k#2H=*4@rAU4p3t(MxF`sNg(&(WwcrFm}dW31c$&Z&&ES6&tdTF zU1ZfHsl-LIySgt^-?rH8A=%|7?i|TqdB+M=o{M>)edpmLmgn=N**khtXaLDCaf8D= z_5G5m&%k;QUdu>#?oSvJw@6oCI;kqW<5B3k9ewTKCW%fG#|V9@XxRy`+>Cy(aSJ|T zxfOr4x1`mjz2$05Z&#h&Cn6W|&`~v%_lD^mg?s>a zq?Ao=M~ia|Cn6Wq<1$V}_G(kRga=R}GKQyEUdrE9BC@r^ts=RDkn~Rz9qB~mfjllX zagvDCC)!B^6l*);-GzRzb~ip^X)?C#bTS!{#$4dgv4=q}Qy`q#q(TQ@sj=}Uv6)7W z19={@fH-iq=Fa69kr3T!4tWLoLG+dQh~-s`UO9E5u}N;@4Dz4`NOJ;N;Eojgu|+gr`_ib)2>^YB$H;oyst&y`i)TP5(4el9t>gk4#OSNUlEB z6b;b(#EzI9K!6P{K4Lk|__EWYD&a!Pn9GFf;P9?%HDDHy+!9#PpOJ;@+p1Eg7c5qG3mC~IhOj%Df1(qk7(Z@(sO zjt7wR_Tnj)^ZY%__w{ymzOT1)ozClahr_h1?@^6#+6fC4)n zf{$1}lyPRKlPQTzB}EF$*D%Pn3M79P7`z*&MI7AVP&G=c65y;eo!bD_2Htf9CA`hA zUuDr5#U}YMplDtYhs=jF=Jo|WTJ!G_G;ZYoGV^u7fWJdrz6B9u3v#W{{m;z{(_%_i|Ay_6^O z05XZE;3<|*I-9UH;^nG54|pNik_ZIU+ubMsQ!UY^SnwlDQ5nz_%TaU*vCLC*(bu;vB$h~*2J zjO=tmB{8F6MlWKJ7b}qbMz97atVin$4UBVei;VA%wHeJGzKEPXhm9sBW;U9e8RG(s z=INRlFTn^f<3A)pz7)f;5iU!Gn#SdxxSYAb!qt)9`;uJ zCFSDBfJHF#?G!ADs?o5aB9X#}%G_*(4p(uP8+G6=`>RAPw!Jf4g z6|iANVK9{M;bHHk&aTjo=(2Yx--oZMJF$4HO*hZVnLU1yEdl zi-G{R`dvGk91P^tkP}tBA^A~)=my_PHsAaG7{2evopZZND^<78LiusV-5cT#akYRq zc=Uq+q5K55_TbD{io{P6!X@aJ^YT;l+@mi}Vqn>ppQg6Owl64GwZQBYrz4U|1RehFaaDq4Jqfez;Pzzq3iYF<*di?u2F6~bh$vMWWKG-lqj zL&wJZjjy8pfVmE@BdfLO_V9m=5G^IrN|F3Jed(cl*?z53*h^+7zkxOgb!E9$sTS%Q z9LjH^9ma^O)viAoB*5QNz`b*p^l2@?Yg-lhZH8S-Vw-e57JebZ8AlK7mES=puBY17 z+{ve%S(e|WuZP=vUHp0g-|tAc_&q|s#9XSXR`UCdl4TC%o^^5WE0FRB+`1Nr-L08| zU9nuRkR0)V3jPqC`^|M&YNhTC8fsMF7$JWIpabTiwm&O>%*fQjtD#*hW0e(8{)C%X z*?X%2^cZ(naQ+wJN&b}ER}=T}TM2wcJmJdE80T_zrm#S^B7e@ny<`q4Mg9f1u56K* z{3V*rCADZWcaHoOJ!cYBWJJGKm|Q*gW3(4CUMrSB^X2oQ8-0J=_xGUe}h zY&RSYE<#MYd*$y5j4aM77UtY?Qna}x1r5EA@Ln^b8~~$`N#=;_8nz=b_l@Xko*ThAp-c3wYT87Q;vuKBe`fX znh)~=e@Y%gkHLoaM0E%qg7JYXKH%pSD> zc1vJu}UH0wYS~!Nw8FWoR5Xk7P?h0&L?4Iq;Jkwoj5P%P#cpH<#n4aJ{J; zO%xxPm35z6g1xwbWm@g7tzr$LMOClurgJjb#Jf3wapsy17G%JFz) zs7K=$0@mT?sDYb3$EuCgdfpM&%+*al5Te3xFXLT_zs>-{BFayzcn+jmd0xdwl0#le39wi^~s literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/database/service.sample.doctree b/doc/_build/doctrees/services/database/service.sample.doctree new file mode 100644 index 0000000000000000000000000000000000000000..da9ffe7653928bfb43158838832fdb52959db8b9 GIT binary patch literal 8146 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 Lrbh6aHpl)ASSll? literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/database/users.doctree b/doc/_build/doctrees/services/database/users.doctree new file mode 100644 index 0000000000000000000000000000000000000000..2f3ff75db695e3727b7309b2666f979edc056e6b GIT binary patch literal 8793 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 E1GUj)(EtDd literal 0 HcmV?d00001 diff --git a/doc/_build/doctrees/services/dns/Domains.md.doctree b/doc/_build/doctrees/services/dns/Domains.md.doctree new file mode 100644 index 0000000000000000000000000000000000000000..4dcf49cc0644b5a35d28df2c273a22bbb8d31c86 GIT binary patch literal 42004 zcmeHw2b>$l^}jK$ayLD+#Tf1kcam+Y4K^4XF3#bDfJFq@-04nxw_59@+gZuy12%yW z5=id_QXmN-q>_X*(jlbxN=PAvkVZ&PNdJGoZ)T*`NTOD zPPuIAP@ytZs}@UPx?Cx^VT!M#Zn>HY_E>g#Ff|JH?99$C=SSV@#F!ff)1qK{XKvn< zDN~B0V-;0(3YF1(u^i0klgJ_E=BuuaoT(#6D~00lgpHilmzxgs(5=?Sg4un!xp*lR zLrW(_>rCg`8pf5KQz*>|rSlE|K;dR8dJOzt%Mlm*>AunGZfkGDd)v}Ai+F)2M zR-wmAxxe5JhrwP^uy=Mo@(tboQ8x_pgxzOruy1z$P)#W)xIe7a)Q}tO*OyHd+@Xrf z6Fb3k)5Z@pxczeLEg;l zJY51d>R@kHcAnP1jXK1eot>_=f2fzr&ZST$EesrQPIjKwyoEo^o12}d^=_k8%`ez-S3yJ)ybop;A{zSLWgohur*6pru~W@lP`AL%X1rl4(`*x@bC?iFLJ z+v>WrmYp@^6-x!^FIcuUI4Wtpp*ut+rh=nkyIoPx4ck@uaii&Uc1m^z*n;wdj5jqq z!^AJo^`N*j*%0=T@$;iaH#4RxK4^uRF>j3jyDMXE84}bA8B~xAAh#EX+%QuphnWph z1H$xZA+5q{up&FNnjc|N1uL^N4M)K-*;Kw-RmH(t6^3wZ6dc!?omHrf5iSgl-xk?E}PvQa2`EPq@Q@@<-x`%coMSon9aJ{V7@k( z#H7WHsQj3h3VOlgnNhHbJYIytSy!*xp_4B=D8XWNA~RGeSK)k6!7IZ~zEpBH^qt$S z+!9<+!6}x9D{53KRmuR;NwC^ilrvgUuA^A(3Sm0vi-|tF&PH$+#Xq~9_|vTTPxg8q z3kJ`D0?&{xFbcL%-E_|9igVryidk(Co>;;~O%#nK)amp?6%FJ8aGZ~VL2^t&7ix}KutQN$ zU`}sCPG=TJi&Y1{*{c*nXS|3)9CV!_6efzWQg*7I%Tjerjq76R7t8R8gaB5)S zEY!^PteIN?x8+=2GY1CJ&en;zUvKRI7?{t%})oeuJlpGLA&vyJo1tWgvNZ z6g-{s?K+xPn_C;iKXENrM8PvGSEgmquI0+6QV*W#b%<+umbXY;%K}>muF|gM>L_?N zSz)iLr_rl=4l=wV?q8nUMArD1I>Cofoo=HdO9CcK&aMV#<4tip#Vl}j_ z5N(0(z;Z9{;3e(2Uu0GBQo4iHkBFJzWf1q}QSb`N^~gwp_ZPLgfUv46<&jkIN>q!aWe%zo!!S_GJCvKwJsNdI7|GPErfyb;3O5Cv}{wI#@OsV-b+jkDA=D&1>! zROgD7#~gdyvXdGGo1NE0EVz+6cr!YcJ4Q>Z!?8TN?>A*WE^uzhnByz3#b4c^{u$|rbFc6tSttb$we zv3^^|yMbl(UX<0YsUmnE%HsV|@Bv$vH=DB5Yz7}}RJdEC;6r5gYRI~ASSl5|+K!js z&Tbbht2S6-muu7=9dwm+#gvmDLN7WLfO@raHg1qQnHoY{U#K*Obu#=4-Mkv|q=`no zRklXv%Tja8hN8pDx^Y7~_;8~zJ*lN1X|>=rz5CnZ_$V~<-zC7uPyio~f=@uT9)-D8 zG;TfgZBcN$Et)0|{mCXVgF8G&JoKl$lz8ZatiYev9{Mv;@L3if2jgeP2jibZmbb-y z^yixh8*{Fc`vUaWLgR}N;Y(5QWr{$0pJ^D)SEAsn%=f50;H5oOBkB5u4#r>Giv@aX zDfCA7-jsLfjONQ2Q|D<@gA;%oDQbc zVyz%UkQ7@J=HBI?@j=%W;}iqyfFF!YhTO4gP4Ym?WV4X*47fuDHqfb}?hBJvI`~?w zRK%*k-mX**v4#8%l*(=m(@J((!{3C>ek%&TO`D~*zoKoHjme!+@EzLhjz(h=wrNey zL`iinJ1Lbq+&Kqh2?bx~VzkO}O>Af?j?B%&qiN$xndWJy6sxM{N)u&krL;+Rb$T~> z&CSnuJH1B3^F7<}9A$0d`yN}EA4I|3kGm{rw|;2sH~5ilw|?B*Zr#at>nE_|$5ZRU zJ+O+OM!~&Mne=Ra0ITp;da+jUXHoETDpbKZ%m5UXoPJ0TLS>Lut*Ast{$x%og_#=q z5hFD?zRX4r#sJ4@C5}+VPGr&|W<06tL4L1b(z-rTT__^fA^M3%l;2tye$DFKPjcPgtFCrBWOVz~VUp<*lj%kX zxKt|g2L1VGXFWiz&y<6|wq7K)OnP&+(z7?G4wp<49XhpCV(Wc6bp+mRx9()?4vs`5 zdv>Y=50UB=oOJHdWfv?i6T)IqibD(2ZBs|%xvA&oQ*W%wT?~63T~1Uth{7P#2qKli zuW2%Uimwf_(K%Dg1#E=|%N>d$H0{@rVppSxh5=0l`)eEmSB6)sm4HpUq*h|~!}IPL39eR3-eW^#&Xu!gTA)qV~IQ|d%Op}1EGh1HsZ z^i7RLW#`1e)ky%Lg`G^m>cVa%CWXBS50UB@@U(9TGTXSY1CrcAt#=)T%@bHpPBk$% zh{41AHx+PDnA4(4^!=>9TB7!{_4_tNEc~~ zl80~dSd5C#+0yTL z;}&(fovS66@@H2(|)k-AcHezfJI?&79=H1zpQ z$?sV@KPjj9m|I;X2{9F@s}bWJ&XKozHXrB7;CrLB$L9zFmQ|?d;we(klXpFnaz=8w z+Qtp)`J|EY$%auq7hI?p2wE&(GZe4q^Fku%W~&|97aDDBg3A@oq6?U@B!1h2|=XZf?qo%p^{-&_4#5nqh_*Cy;XqU z)&RB(@npG$db=dVs)>3BV!TdqKkwutmW*Oa*ZH_3==9GKM8b;QA}AXbGnC8Y{j44A1Fv0g*I z32Kn~+EjjotBi4eF<;W(1>NC1ChJ4yFjjX#}Gv7<04qvH*&MU8cy;P0&<%M$$c8(Qd?Fr4`!mw zN3r#K0ktVKEPkyz+?UUv*j6RN|VOZ2qPYUhY>OL?6v0MyT$F=^~p`Mjjd;@Slyu?P$VU{tXP ztl|dVW-Ov7OZ4xJHb8S=x!NF~31RVU5#-tOMyxu28j6fK;{eq(3eK>qj4}@Pt69_} zy4Rv*E)~F#eXo=X=W&8O7FBk~B?=ret4P|Lm{XAU=5{INWP9^TiEXquci`Q&H)U)4 zpF*UxH?)vQeMWH7yY0p8Ui-gdFKD_yE5)jt?$6=bHr=1cb92-E3B<7J{sP0E-FEQ5 zV+ZPsC{nZ?UqTS6FXPwtf>`wvy&$eNRbLUvuQni?yFg!)gjl<(zK$60FzEt)gO78h z3p8eZ%{K*sU7&B_DN^5-_q5D*ogUv>r0yhk#wP>u&d+xQA(o@*{M2)}iwJu9teN0< zkpS7hhaghl7cyzzs19a@i+8dm7-_f(CZSjNFA~Y4t-Wq`73IkM$$bS8K^ATlPOD9_IRX>$6#( zKY>DEnD-!v)KBrtd0nGqnqs3vcdvl_OoQZ#9QEeIiSp1nZa9X|BW`?Ps<(@=SGRPq zQkY;;6Kwa|@qDp*Mn!dYtUqhh*=L@<`ScAPfOdzxiX$i&c5s6Z&9!4IR^P^}qI5Za zE_@`r9KVp*MwjE4c(+}SORXvV3X#&~xDOAJ`nBMsA7hEoDBdp_>qhZ6c(#q=Z}HsR zC{p!o6o1FC=dtBf{T`XZLjHgtQh&s+b=y>5JGcEOf&6m=ve|9_MG|6FRXupA?J2-yUj91d*DGU)Bzj3z=!S?L7r#ng+?;g4z+^pN6#|W0eq|7Ot0jMa(WUr_)W3 zq_L!A-M~N>g7tmtHl02&K=(N?uqih%kTQVFWFU%_F!g~czl@O@e5TnW8KxaB_K+{^ zde;Ixs_bi+;7}D6bkK$C#d2Eej8p7vMXbZEhHhzib+husvrZS0T9tR1EuI+yfYqcA zNoF+@Xi)7e1d*C8G_dCPA<+Q0RJ&PKk`hXD3?=S56TNcIbgo1uWzg8HSM!jL@<)MA zSqGlx3#A2`5@&vW($=P~my+NWI~CMIgn(a!AX1C*3-gd24_!_(Z+i*E-Uec}@Ma2| z{OuzNJLhj-q=P@oBv@)cL?Gk+5nv^UVCzK}%qY$%SIN+v)Dtwufr4?6#*j5MOl@ZN zV1YTrz;LsNHb!!KsKoA^QwQnb6u(n8<pGZ4oXs)7Ki0OcmTK;f z5bloD+~pn*PU`Bn>qTtM3?wI43FPB;F`qVX8)9iFwzG6Jrz1;db^XO)9W9}|9FZIOo!4DLg z$MNy;^rS+xN!&Fl41?{Y*p#b_F}mX+%CRYDg=i=mD+WvLq-cowU{aW}!^BYq%UmWz zH^=R?fGpGPWNS2Lsioym3%6pIOR4YNR_U;RenlF)d9jTHLkg_Bsm9H1*JM2I*h%cI z&L-ulGrjVWGQILKVR~h|LZ}a%LQIv`2AlL>Piv~>E^TOC0jDN}p{^PD7bnKp;BI$#UPr#v(PCQX+M!;jFl>YhU2fT`IP1>dXuTU| zK&6ZKAl#3;gQJ)(c2zqWbOqLo)!k8(Z;PdMv{6EMcfjsG zKYC`9k3Z`z+3r4neQ>?I&)@M__4(B?aM+mRIu=2sj>E5=<6^g6&vEJPCU#-h@d9~5 z1G0H~{6tBJ&9JFei1Ch)>G9QkTp-iqpSI(KlLUd&<0s=OQfuU0pWA>#uxqG0XbcRcq^6HV#4L9en&Fd`6x6g66 zR7$HeAW^eMWy-uq1Z~ymXKj%^k$64-LyFi4fnW+xLJ+B55kKu4?Xd70rf{Z!Y|vHQWcxHYz_$2)3&Hi&o`l6R+oCWcXwtLbP)b zU~7$JllBc46h@22Wx<;TB&$JWB6*0_Z-ZSUHXT!r=AbxGL zKxM|8h2)kInfOepA%MZA3WD#(@Eh(MCvtL;0hcJz$ zR1s;&&PNcbZ9<42lt8s4FU@Dy@er$0*gFkp*!kp;${rWD9xlFhE05DMVp!ouM1zEmBn>W%#bi%TDReZ=&lr zliM!j#^dC6Oipg@WaK7<+{JNnmrPFXmUiTl%0)w$FQn9^AO+P%lF3u!nLJHik_J&% zr@9R3NusJUo>G@10VJP}AW~Nd6{FQ>$V)3p=1wcPlIcmBm=;zODfLXGf$FmmMCvL6 zS{t}pURtS=0aAT7L+){U4jv-)TmetZ=S%uf^*p9$d{Q<&8)=81FK|h+YkI#ynlB)* zK1M)jyb$lGurERosTT`o+Bbxmt@wGKM>$=@Q7@4=-w5?mL3^1-(-r6C@{-Iw<6EI# zfpih-m4f>!jho}g;SKnv=I}$Dx`sd*pXB3`T`Ryz+D5Wh6SyZq=rxFhc&|kesn-d5 z+Bek6fECYgmg_o!zh1*@xn3_X$-EnKy#eVW*Bb@*292xF{?%PC^(KO3d=igKb)$eL zDI2NYOki__upx63q9D^-5Jc*&f}HjZZDzg`ncgPwZ`bfzrgz9oGUtX&??k%DbhF^T zOXKQeJKYdl#pQE=dN;u`J~N2R^&SCFQa5tlLSSL|&3SH+;uOneH1$ z@iD>wcpU!|JHcmGtbVsK-PWGl@e-*|3W{+wcgRa?{>+N_pJGUt@M%0?-ME0KWgF|j zKyw#sU_gDA&>5fk#0%zgf|blGgkN#aI9-z6{=^gTRaRja_JeIW(L ze$W;-(CHAt6rL9NFiWPD~yNq&jY?(+W?o+EW1k!|btYk6s9gqZ>z zg}h!bCBxH6^`~Y(e7}(WO`Pm+cS6<{{O?GfHRSh#`v;AiJKPMpICQR$ndR6_8LimG z!0L}g&-lzZZV`VHGD%^KMf{n-J-eL6UjUBsegHwF{wjpizR8zNCn^DaoUh%R^*etP z%)e_)?aBWkFG>C~zV$o*WV-b`{}TLv$ML7^$&zcuXI3ohJ&-P1or;G@?J3~t+yZD7 zey1^pL!FvN(2UPa;u@STD9Iew^zt}6HG?>;Q#0{^qn-pd?F%W`^z62{8K2A$J;hKr zCNW;r97IFWa}h*p9)4}bVlILpHOvWemxNi0l*d%GAn|%pP-Ry^lNbN7MX9-Q-x3=d5Np=SfQL`#HjE%Nz`3SldU=)anQpF2qJZ&0O*@B3;^;V&t~cA zDgj$erYS#UDs|B@q21`Av^wYYlFJ62i+0YZ%S)>!taCnt>DD>-2>uh}_#1bE z&#b7)CoyD`*NX=n$|K-{?_!~MdD`>m^JNeJVffr z0-N@Q6iWG=wzwIetPuTNhI&+1=R|ErB#JtVAX4Yy*H$;?Q7`JYUBRaa4v!JnIAGy? zc}cRL@vV9;K)R^sLc!glaho0UR>EX_QjcrqB7sekHk#=tFf}uPhe+iGHth>3ur=5g zH{+8FYG#OG%rO`64yCvVA~lR(s}z!-Tqz@hBVClTO>j#Zx7jg| z5+>u5dR!@GflZP&N~sW-N*Tigrnv+*?F%WerP|_Vd~!jhgbZVjxr%ovrG_9<+wp6a zLh_REB65!_2PZtfZF9W&0oF5%pJr!u6s!{hw+#vU_s z!A8?(8u@xdC!YL%gL67LZFk(t?B(9 zDZi4y^dPUoL!_<|SR6^iXw>0r0#hj;#6zTR71*>d zq`=mP+Tvz>azUkhm|?87`3T;jl#e2a)W`5^l|u59E9K*Y^NBhRa=%SplALFJtCZW3 zE=u{N;NGEeb2mQ*tqm7g;dp5rgMq!s*iL7@r!JeU^}*(9v*kHIJdBMvPGb)QTP7+X zUT&Wfagx%Ta{II-Hn&WlK^&C-Sp<>#oB$+SCMwFbOrIC9FBq_7%k)KwPiCI+Nq`0a zC5Ew<>C1RW-d{lwsjuSK7Cf`57d$Yo|4+0`k9KLOuW50RS>6M=rJM+amp)MtWYwf{oYqX!F(TJ4iro$0w$#?h^Q< z<*ezw+b$3IF7foTpeB0XLp)6A`v@=zEL1QlOkm9YB7hZvF)o<6TR?uOL2@U9vF2Uk z@f|s_r4%@N-5u}d+@)U8j*Eo!{TFc=Wbwm~gmiN8!;dAlvH0O9c(;om?y=I{gGgEY z@KZcQ>R!P~KXNAF4u2-JM6>GWc(w}}eu3xag$z^>Yw0f;_B`Uu)vrJYMROkl?0d(r zt=$w-*Y4)EYWEA|ZyJ!zYt?=$2{9W{ze9|7kgQevJs)StTD5zvR{tOfr=slth^I*X zN#6AcePjIcVD)EWWBQqa?itAGhkp@-SdM0~T0Msch@iJ}Hxv9T5+M8E5Jc+lLMH8- zT*!L@P~q$iU9NL_=S=BMpzb2qoAe<$aD>oy95Q*vH{-3Ju6sFCZ~+B zWOktd8t0^N*&i;9z?q9EzI*{UFUhWXebQXxq*_%gxmcMt1AMe8kD0<=YkAyf%VQSt zdLDQ7XM+u>J%u1rbA;)%Z)BpPMeX)`*<68|r=etSvQbgx8y0)w^O-!^7qm6Kzp+YK zAh4(4>lZ5ELZQA$Q_u07)YEw|rtX_z867Ua#smYe^k5C--FR|50%BOi!$n-Vj5<}S zjh536jx5dF-M9K^gh~}4I6DD{DFBh!U9!5+n#Y#@RhlL%VzFG43ub5(xU7o@xH;o6 z1ieCW3`d^gIx=^-%6lG=jAIrs4bAg$46;6Y5E+@>?Y3W8#*N4g&NUq+2MfjtAtzh+ zxlA>M?U6X;A=cV<>0{%}CY63Kf?#b={80+-^QjJmjLq~9Q2Ox;l z6GWT4t&q-7``0#323E*At6P8x?V}GA^&OeVK zj2?~ydsxm3qLA}=g*r_5NV2`A_fM=)OC+%;cPu&=4~I#0I0(SnmLiDM5kf8fu&IH2 z>JGyvT(G8&6q+4|=HB42ol4jecM`^4Or1Lrklg-Xt*mZ~(7a}rD^Zy#wXuw0Pi|*f zsiTk%VUI=-sV@B5@da6h`Cw&}zBuhLu<)P_jq29q(n2ny$>nwlwXP^LdcG7!D@QBa zWjf1+<_bfz40bl72H>1LRm3^w((S|9c>IcCdk@ZN4c!V{L?605DwxOSu~%ph$Qvx> z%iB`?5S<@PJAD;L-_$wo;UW}_1NeCa$FZ8rAksT&LstqvEv8s+Lytka)SzR9{o@S# zqmMQF>Uc7e@u|glRXRZ!YT>{40UFPV1n$W__ROwA#<0rO2=FmpSWNpy0xDAc8s;Rd zZJaECYcyc)S|}HtNH|Dz`ZyxsZ553fzb@%>HYTokG_}3oV$e;s?|;eph~==&i6L?# z^@P19OYVp)mQzdWn~#i>UgEe)@g~Tf7}8o{zNH}QhI9(j#gI-FS=Sj^ulcW*Rh>qz zGd{~FZguO0`xaq)|4yshK;WL-f4O9*gG-p`83=G^k;sF)gvffvXAxNwg*%gCd!hht z)PQokPHa%HT0Kc)HpXQ7vr4bPw<_x2b`*9d@!Cx3Z9+WQ>q8K!vxG|8H<^$XVNa&? z&K8g-YmnSAaJx$faks@PS<$}6Ir-$1Pd{sOY72%xGW6K>0ZY4<>cLu&L^dZLk5hp- zFk0uVSg}04Vnuod4y!so0VSgy0O8Q4<9Ih?X=VMi8;-8Fl;cN8$#{z!k4aVz8L{)K zBH5O53gYsVp{CO>Ij>niv8yQ4UY!j!$vHByqLZ1-b3~)bnapz~wlR~r8SiE$bJMIn zY^i1uD^r>0;UQ8_5v26)!g-j5gZ>nEo|r|V>U=!g8Qcr-+&qKJ`am~yA;X^CI)8XN zS6iSyIH0WvB6SgdZO4)(tvi;@6W9F$InaP?p196SLd+AYLBx26$i(#!A7^pmdQ+-i zi3)bjYt zj|51+4FUG!37xcWvLQFx{c>t$R6xoaB*)7a8dKM!_BPu-FQ?Q>6KUrx z{7R>0^X4;gMLk_$3a9n)q%bqHB?o;qI?JY;Y-YiE#}9hRCbvS`IY)0%O(6l-*E0>& zTx_?tAlP_~11jCo%69ars~$Eaj(7+$k%n`?uqm52S##d4o4bNjP3N0(S1^aJ10ZS7 zk91lt&W6Al!v(I+J{+*!daTP^w7Z-EVFbHj%wF0D!`I+366AkC3$V*X3OKa8>NXLv zf`%Z%U6a+~b{7TGY)dl7Uy%ZCwWYara~%_ip4`1MG=M6HxZB-m{D0B}Q~;4tOB8}g zg(83Y|0DTfCanEtZlEe!s2MGALG>f3BAi0lPEy%v{OT&(-cX6mSR};d>&uWq^78ea z*1DDpMK$MaCdZKrT;Lp#i`8hg1JNkj2?UY4n4HMonv72|wfooe3iyW2b9pb(@RtgF zq~UWqtyxV^6^N%9h;>xcBXtH;)@3AUl*J8Tnr2L2T)F)q`fBnrlc?-+Q^$fCPv_`4pTEnY8B=|}^0ppnnBK0i%0)u@WNp05LRRVLhff3C;QV&SY zJ(~n$n$t8Nn&z5Z>m-_c4)QXZYi3|NlbaN!u44}LxJ2T`g8LGK%XzB$AwRV-%sRMD=VG=TdSmaB>t2f_+y=Q~`SNAv zw7=F9K=CoHABTIM8o!nf48=M!G8B2KFq0gLyi8&nLy?!`-3~<#tT#)qK&%W!UWtcD zy-JYMxyL~+*xEIcyR@R}T0Glc{HyWY+>2)^&^^6|Vb9~hu6iwUhDE*(L8Pw3ueC@j zQd?wm2l#q{e0>A5xdZ$LNr?3z)Eg1wEs+lJ4Sbv{9pHoOMfWDbURvfcq=h<2e_Hy+mHb1-;N+s?+`j^-(*8>pa4@} z-YFnAYY^EDT(2+ME#OXyX5Pf6-z5l1n|`;%Hf;Jmc(*paq^_7-5GywQUOYtVeS)NS zBDcvKcK3dP(02C$JX^c_AfB7;j?&QXZe^(N=xrsbK7>?g=femh^%4A9+aN;;+mQKi z>?X?2k+YSDaXAcT5e?y82y5veG$Lpp2rYF&Myi0tYV>G77?f~>HsmrJ^#ir5=P~9!G=W~1ZSijwvp?)Z_^Qsl*Xj%LbB4x9B zb{@WjjMPvFz`%m2A0r_<)h(-^@L@LFEGU0;Ox+`7=IX-fFS!`(sGlOP7ljNFdH9!@ zfVx)zQ(nG=3+@H%X9Sy`oo5)s1%M&A{W;;h4Sm^zWOBS8GGNGC9_iPwxLdG*({j`= z0G3@y&O;!2W^0A|C8MVo+`-z2`V}c<7rMBfou)E@^*x@8)5z~b{MFfmE43yHfZ?=

      • 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�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 z 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

      >YDitBtq1c}bYP3L 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